起因
在学习redis的过程中,因为教程都是java编写的,但自身学习的是go语言,所以想用go实现一下,本次使用的库是go-redis,在github上可以看到详细教程。
经过
由于教程是编写一个秒杀功能,即并发对商品进行抢购,会出现一个超卖的问题,所以需要使用redis的watch对数据进行监控,一旦被修改进行回滚,也就是redis的乐观锁,但找了网上的教程都实现不了,最后看了官方示例以及各种网上操作尝试成功了,所以将代码分享出来,以免走弯路。
使用的是gin框架
func seckill(c *gin.Context) {
ctx := context.Background()
//获取pid和uid
productId := c.Query("productId")
userId := c.Query("userId")
//判断是否为空
if productId == "" || userId == "" {
c.JSON(400, gin.H{"msg": "bad request"})
return
}
//组装商品key和用户key
scProdKey := "sc:" + productId
scUserKey := "sc:user:" + productId
err := client.Watch(ctx, func(tx *redis.Tx) error {
//查看活动是否开始
val, err := tx.Exists(ctx, scProdKey).Result()
if err != nil {
return errors.New(err.Error())
}
if val == 0 {
return errors.New("活动还没开始")
}
//是否还有库存
prodNum, err := tx.Get(ctx, scProdKey).Int()
if err != nil {
return errors.New(err.Error())
}
if prodNum <= 0 {
return errors.New("库存已卖完")
}
_, err = tx.TxPipelined(ctx, func(p redis.Pipeliner) error {
err := p.Decr(ctx, scProdKey).Err()
if err != nil && err != redis.Nil {
return err
}
err = p.SAdd(ctx, scUserKey, userId).Err()
if err != nil && err != redis.Nil {
return err
}
return nil
})
return err
}, scProdKey)
if err != nil {
c.JSON(400, gin.H{"msg": err.Error()})
return
}
c.JSON(200, gin.H{"msg": "抢购成功"})
}
如果需要解决库存问题,还需要加上悲观锁或者使用lua脚本进行实现,这里就不进行讲解了。
希望能帮到你 😃