秒杀系统设计思路
在秒杀系统设计中,最主要难点在于解决高并发下的并发请求抢锁问题。我这里主要有几种方案。
方案1 乐观锁
在DB“库存”记录中维护一个版本号,在更新“库存”操作进行前先去DB中获取当前版本号,在更新库存的事务提交时,检查该版本号是否被其他操作修改。如果版本没被修改,则提交事务,且版本号加1;如果版本号已经被其他事务修改,则回滚事务,并报告给上层。
这个方案解决了“并发请求抢锁”的问题
这个方案的缺点是DB性能较差,会带来大数据量的无效更新请求、事务回滚,给DB造成不必要的压力
方案2 内存操作
将“实时扣减库存”的动作上移到内存中操作,内存操作成功后直接给逻辑服务器返回成功,然后异步DB持久化
这个方案的优点分析:
该方案用内存代替磁盘操作,提高了并发性能
这个方案的缺点分析:
在内存操作成功但DB持久化失败、或者内存故障的情况下,DB持久化会丢失数据,要想解决内存故障,解决方案是搭建一套高可用的缓存系统
注意:这个系统的设计关注点在于数据层对外的高性能和高可用,至于数据何时会持久化到DB中不是秒杀系统的主要矛盾点,所以我们应该尽量将精力花在数据层的高性能和高可用上面。
总结
方案 | 先到先得 | 性能 | 缺点 |
---|---|---|---|
乐观锁 | 不是 | 不佳 | 给DB压力大 |
内存操作 | 是 | 好 | 内存故障会丢失数据 |
既然该系统的主要复杂度在于数据层对外的高可用和高性能,那么选择内存操作是最为合适的,为了解决内存故障丢失数据的问题,必须要搭建一套高可用的缓存系统。
搭建高可用缓存系统
背景
DB+NoSQL这种架构模式在我们的生产环境中十分常见,但其实在系统数据规模较小时还用不到像REDIS这种NoSQL工具,只有随着业务数据量不断增加、访问量持续上涨系统支撑不住、系统需要提供高性能的时候我们才会考虑引入NoSQL。
选型
在众多的NoSQL产品中,REDIS是最常用的,我们一般不选择memchached而选择redis,这其中有以下几点原因:
1、redis支持更丰富的数据结构(string,hashmap,list,set,sorted set)
2、redis支持主从复制(我觉得挺low,今后会有博客专门讲解主从复制的坑)
3、redis支持数据持久化
4、redis支持数据分片
5、redis在内存内配置采用申请分配方式,内存使用更加高效
本系统采用redis一主多从一备,主节点负责写、并且将数据异步复制到其他从节点,从节点负责读,主要用于做读写分离的横向扩容架构。
在这个架构中还有一个问题,单纯的一主多从架构如果没有自动化的故障发现和转移机制的话,是称不上高可用的,所以还需要引入redis哨兵,一款redis官方的故障发现和转移工具。
redis哨兵
Redis哨兵是Redis的高可用实现方案,对提高整个系统的高可用性是非常有帮助的。当主节点出现故障时,Redis哨兵能自动完成故障发现和转移,并通知应用方更新主节点。Redis使用了Raft算法实现领导者选举。
缓存系统分析
这个架构有一个缺点,每个数据节点的数据是相同的,无法存取海量数据,所以只适用于数据量不大的场景,如果秒杀的数据量很大,这个方案就不可行了。