高并发下怎么保证不超卖?
下面是我在某云买的2核2G内存,装了一堆软件ngix,mysql redis docker etcd等一堆东西测试机的测试结果,开启5万个协程去模拟并发抢购,大概消耗2-3秒处理完毕,这样的机器都能做到就2万QPS,给我几台机弄个集群的话我能整它十万QPS呢
正常的中小公司有500张下单每秒都一年已经赚几十亿了,以前在的公司,100-200张单每秒,一个月十多万张单,ab测试并发也就一百多,一个月月PV 4-5000万的都已经一年赚几个亿了
CREATE TABLE `stock` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`pid` int(10) unsigned DEFAULT NULL,
`name` varchar(255) DEFAULT '',
`stock` int(10) unsigned DEFAULT NULL COMMENT '库存',
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb4;
废话说完,上代码:
package main
import (
"errors"
"fmt"
"github.com/go-redis/redis"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"strconv"
"sync"
"time"
)
type Stock struct {
ID int64 `gorm:"column:id"`// 主键
Pid int64 `gorm:"column:pid"`
//例如 `gorm:"column:name"` 标签说明含义是: Mysql表的列名(字段名)为name
Name string `gorm:"column:name"`
Stock int64 `gorm:"column:stock"`
}
//设置表名,可以通过给struct类型定义 TableName函数,返回当前struct绑定的mysql表名是什么
func (s Stock) TableName() string {
//绑定MYSQL表名为user
return "stock"
}
var (
WaitGroup = sync.WaitGroup{}
)
func InitRedis()(client *redis.Client){
client = redis.NewClient(&redis.Options{
Addr: "localhost:6400", // redis地址
Password: "", // redis密码,没有则留空
DB: 0, // 默认数据库,默认是0
})
return
}
func main() {
//配置MySQL连接参数
username := "root" //账号
password := "root" //密码
host := "127.0.0.1" //数据库地址,可以是Ip或者域名
port := 3306 //数据库端口
Dbname := "gf" //数据库名
//通过前面的数据库参数,拼接MYSQL DSN, 其实就是数据库连接串(数据源名称)
//MYSQL dsn格式: {username}:{password}@tcp({host}:{port})/{Dbname}?charset=utf8&parseTime=True&loc=Local
//类似{username}使用花括号包着的名字都是需要替换的参数
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=True&loc=Local", username, password, host, port, Dbname)
//连接MYSQL
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Println("连接数据库失败, error=" + err.Error())
return
}
s := Stock{
Name: "iphone13",
Stock: 1000,
Pid: 50,
}
//插入一条用户数据
//下面代码会自动生成SQL语句:INSERT INTO `stock` (`name`,`stock`,`pid`) VALUES ('iphone13','1000','50')
if err := db.Create(&s).Error; err != nil {
fmt.Println("插入失败", err)
return
}
//查询并返回第一条数据
//定义需要保存数据的struct变量
res := Stock{}
//自动生成sql: SELECT * FROM `stock` WHERE (name = 'iphone13') LIMIT 1
result := db.Where("name = ?", "iphone13").First(&res)
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
fmt.Println("找不到记录")
return
}
//打印查询到的数据
fmt.Println(res.Name, res.Stock)
redisCli := InitRedis()
redisCli.Set("stock",2,3600*time.Second)
//更新
for i := 0; i < 50000; i++ {
WaitGroup.Add(1)
go LockKey(db, i,redisCli) //加锁,保证库存正常
//go UnlockUpdate(db, i) //不加锁,库存异常
}
WaitGroup.Wait()//等待协程执行完毕
}
var LockIt = sync.Mutex{}
//加锁,库存正常
func LockKey(db *gorm.DB, i int,redisCli *redis.Client) {
defer WaitGroup.Done()
res := Stock{}
//模拟处理数据需要1秒时间
//time.Sleep(1 * time.Second)
LockIt.Lock()
defer LockIt.Unlock()
stockNum,err:=redisCli.Get("stock").Result()
if err!=nil{
fmt.Println("get key error ",err)
return
}
num,_:= strconv.Atoi(stockNum)
if num<=0{
//库存不足
//fmt.Println("库存不足",i)
return
}
res01 := db.Where("id = ?", 2).First(&res)
if errors.Is(res01.Error, gorm.ErrRecordNotFound) {
fmt.Println("找不到记录")
return
}
//打印查询到的数据
fmt.Println(res.Name, res.Stock)
stock := res.Stock - 1//减少数据库库存
cacheStock := num-1//减少redis库存
redisErr := redisCli.Set("stock",cacheStock,3600*time.Second).Err()
if redisErr!=nil{
//to do
return
}
//开启事务
tx := db.Begin()
row, err := db.Model(&Stock{}).Where("id = ?", 2).Update("stock", stock).Rows()
if err != nil {
tx.Rollback()
fmt.Println("update error:", i, err)
} else {
tx.Commit()
fmt.Println("update success", i, row.Err())
}
}
运行结果
[root@VM-0-15-centos demo]# go run redis.go
iphone13 1000
[{1 1649255108}]
update success 2
update success 0
消耗时间秒: 3
[root@VM-0-15-centos demo]# go run redis.go
iphone13 1000
update success 3
[{1 1649255108}]
update success 0
消耗时间秒: 2
[root@VM-0-15-centos demo]# go run redis.go
hone13 1000
update success 4
[{1 1649255108}]
update success 0
消耗时间秒: 3
[root@VM-0-15-centos demo]# go run redis.go
iphone13 1000
update success 0
[{1 1649255108}]
update success 1
消耗时间秒: 2
[root@VM-0-15-centos demo]# go run redis.go
iphone13 1000
update success 0
[{1 1649255108}]
update success 1
消耗时间秒: 3
[root@VM-0-15-centos demo]# go run redis.go
iphone13 1000
update success 1
[{1 1649255108}]
update success 0
消耗时间秒: 3
[root@VM-0-15-centos demo]# go run redis.go
iphone13 1000
update success 0
[{1 1649255108}]
update success 35
消耗时间秒: 3
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)