Go-SecKill
Go-SecKill copied to clipboard
Gin+Gorm+Redis+ETCD的秒杀系统,列举出一系列并发情况下数据竞争问题的解决方案。
Go-SecKill
基于Go语言的秒杀商品系列
1. 单机模式
1.1 实现方法
1.1.1 不加锁 出现超卖情况
api/v1/without-lock?gid=1197
1.1.2 加锁(sync包中的Mutex类型的互斥锁),没有问题
api/v1/with-lock?gid=1197
1.1.3 加锁(数据库悲观锁,读限定), 出现超卖
api/v1/with-pcc-read?gid=1197
1.1.4 加锁(数据库悲观锁,更新限定), 正常
api/v1/with-pcc-update?gid=1197
1.1.5 加锁(数据库乐观锁,正常)
api/v1/with-occ?gid=1197
1.1.6 使用 channel 限制,正常
api/v1/with-channel?gid=1197
2. 分布式模式
2.1 环境搭建
- 搭建三主三从Cluster模式的Redis集群,配置Redisson
- 搭建ETCD集群
2.2 实现方法
2.2.1:基于Redisson的Redis分布式锁,正常
api/v2/with-redission?gid=1197
注意要用Redis Lock把整个事务提交都包住。这里仅仅使用了Redis分布式提供的锁功能,秒杀数据处理还是直接访问数据库来完成
2.2.2:基于缓存的ETCD分布式锁,正常
api/v2/with-etcd?gid=1197
类似于之前使用BlockingQueue时编写了一个单例模式的工具类来全局使用的形式相同, 注意这里也要用ETCD分布式锁把整个事务提交都包住。这里只用了ETCD的分布式锁功能, 秒杀数据处理也是直接访问数据库来完成
2.2.3:Redis的List队列,正常
api/v2/with-redis-list?gid=1197
这里利用Redis分布式队列的方式是,在秒杀活动初始化阶段时有多少库存就在Redis的List中初始化多少个商品元素。 然后每有一个用户进行秒杀,就从List队列中取出一个商品元素分配给该用户。 同时将该用户信息存入到Redis的Set类型中,防止用户多次秒杀的情况。 在秒杀结束之后,在Redis中数据写入到数据库中进行保存。可参考下图:
2.2.4:Redis原子递减,正常
这里先将秒杀商品的库存数量,写入到redis中,利用redis的incr来实现原子递减。 假如有100件商品,这里相当于准备好了100个钥匙,有人没有抢到钥匙,就返回库存不够,有人抢到了钥匙, 就进行下一步处理,先将秒杀订单的信息写入到redis中,等空闲下来后在写入到数据库中。这里其实与case3差不多
其他
-
基于Redis的任务队列,订阅监听 (是将在前端进行秒杀的用户的信息传入到通道中,等待被消费。后端订阅监听这个通道,有秒杀用户信息传过来就进行消费处理,再将处理数据写入到数据库。)
-
基于MQ消息队列的分布式锁
改进:
- 索引与SQL语句检查
- 尽可能利用缓存
- 利用MQ进行流量削峰
- Nginx负载均衡
- 读写分离与分表分库
- CDN内容分发网络
- 流量防刷和反爬虫