使用预存队列(适合抢单业务):
实现原理:
预生成订单,通过队列削峰
伪代码:
// 步骤一 // 1. (预存)将需要抢单的订单id存入预抢单队列 preId = "preId" // 预存id push(preId,1,2,3...) // 将需要抢单的id存入队列 // 步骤二 // 1. 判断队列中有没有预抢单的订单id,有的话就抢单成功,没有的话就驳回请求 // 2. 将订单id和用户信息存入抢单成功的消息队列 successId = "successId" // 抢单成功的id if id,ok = queue.pop(preId);ok{ // 判断id是否获取成功 // 一些逻辑处理操作 // ... if err { // 如果操作失败,将id重新加入预抢单队列,回滚事务,返回错误 queue.push(preId,id) } else { // 如果操作成功,将生成的订单号等数据加入成功抢单队列,提交事务,返回成功 queue.push(successId,something) } } else { // 抢单失败,返回错误提示 } // 步骤三 // -. 多线程/分布式消耗队列(加分布式互斥锁或乐观锁) // ...略
使用乐观锁(适合抢单业务):
实现原理:
通过版本号防止多个用户重复下单
伪代码:
//// [goods] - 商品表 //// id int 商品ID //// num int 商品数量 //// version int 版本号 ///////////////////////////////// // 1. 开启事务 // 2. 选择当前商品的版本号 // 3. 扣减版本号相匹配的商品的数量并更新版本号 db.startTransaction() version := db.Table("goods").Where("id = 商品ID").Select("version") db.Table("goods").Where("id = 商品ID AND version = ?",version).Update("num = num - 1,version = version + 1")
使用互斥锁(适合所有业务):
实现原理:
通过互斥锁控制数据混乱问题
伪代码:
// 步骤一 // 1. 加锁 // 2. 设置过期时间 // 3. 生成唯一值 // 4. 加入队列 // 5. 将数据库中读取频繁的操作放入缓存,并在并发操作中从缓存中读取 // 6. 释放锁 lockKey = "lock" queueKey = "queueKey" uniqVal := math.Rand() // 生成唯一值 if redis.setNX(lockKey,uniqVal){ // 加锁 expire(lockKey,time.Second) // 设置过期时间 // 一些逻辑处理操作 // ... someData := redis.get(someBigData) // 将数据库中读取频繁的操作放入缓存,并在并发操作中从缓存中读取 queue.push(queueKey,something) // 加入队列 if redis.get(lockKey) == uniqVal{ // 释放锁(只释放本次程序的锁) redis.del(lockKey) } } else { // 加锁失败,返回错误提示 } // 步骤二 // -. 多线程/分布式消耗队列(加分布式互斥锁或乐观锁) // ...略
文档更新时间: 2024-04-17 18:11 作者:lee