听说你会架构设计?( 二 )

  • 抢红包消息推送给所有群成员 。
  •  
    3.4 抢红包从 2015 年起,微信红包的抢红包和拆红包就分离了 , 用户点击抢红包后需要进行两次操作 。
    这也是为什么明明有时候抢到了红包,点开后却发现该红包已经被领取完了 。
    听说你会架构设计?

    文章插图
    图片
    抢红包的交互步骤如下:
    1. 抢红包:抢操作在 Redis 缓存层完成,通过原子递减的操作来更新红包个数,个数递减为 0 后就说明抢光了 。
    2. 拆红包:拆红包时,首先会实时计算金额,一般是通过二倍均值法实现(即 0.01 到剩余平均值的 2 倍之间) 。
    3. 红包记录:用户获取红包金额后,通过数据库的事务操作累加已经领取的个数和金额,并更新红包表和记录表 。
    4. 转账:为了提升效率 , 最终的转账为异步操作,这也是为什么在春节期间,红包领取后不能立即在余额中看到的原因 。
    上述流程,在一般的秒杀活动中随处可见,但是,红包系统真的有这么简单吗?
    当用户量过大时,高并发下的事务一致性怎么保证,数据分流如何处理,红包的数额分配又是怎么做的,接下来我们一一探讨 。
     
    4. 详细设计由于是秒杀类设计 , 以及 money 分发,所以我们重点关注抢红包时的高并发解决方案和红包分配算法 。
     
    4.1 高并发解决方案首先,抢红包系统的用户量很大,如果几千万甚至亿万用户同时在线发抢红包,请求直接打到数据库 , 必然会导致后端服务过载甚至崩溃 。
    而在这种业务量下,简单地对数据库进行扩容不仅会让成本消耗剧增,另一方面由于存在磁盘的性能瓶颈,所以大概率解决不了问题 。
    所以,我们将解决方案集中在 减轻系统压力、提升响应速度 上,接下来会从缓存、加锁、异步分治等方案来探讨可行性 。
     
    1、缓存和大多数秒杀系统设计相似,由于抢红包时并发很高,如果直接操作 DB 里的数据表,可能触发 DB 锁的逻辑,导致响应不及时 。
    听说你会架构设计?

    文章插图
    图片
    所以,我们可以在 DB 落盘之前加一层缓存 , 先限制住流量 , 再处理红包订单的数据更新 。
    这样做的优点是用缓存操作替代了磁盘操作,提升了并发性能,这在一般的小型秒杀活动中非常有效!
    但是,随着微信使用发&抢红包的用户量增多,系统压力增大 , 各种连锁反应产生后,数据一致性的问题逐渐暴露出来:
    • 假设库存减少的内存操作成功,但是 DB 持久化失败了,会出现红包少发的问题;
    • 如果库存操作失败,DB 持久化成功,又可能会出现红包超发的问题 。
    而且在几十万的并发下,直接对业务加锁也是不现实的 , 即便是乐观锁 。
     
    2、加锁在关系型 DB 里,有两种并发控制方法:分为乐观锁(又叫乐观并发控制,Optimistic Concurrency Control , 缩写 “OCC”)和悲观锁(又叫悲观并发,Pessimistic Concurrency Control,缩写“PCC”) 。
    听说你会架构设计?

    文章插图
    图片
    悲观锁在操作数据时比较悲观,认为别的事务可能会同时修改数据 , 所以每次操作数据时会先把数据锁住,直到操作完成 。
    乐观锁正好相反,这种策略主打一个“信任”的思想,认为事务之间的数据竞争很小,所以在操作数据时不会加锁,直到所有操作都完成到提交时才去检查是否有事务更新(通常是通过版本号来判断) , 如果没有则提交,否则进行回滚 。
    在高并发场景下,由于数据操作的请求很多 , 所以乐观锁的吞吐量更大一些 。但是从业务来看,可能会带来一些额外的问题:
    1. 抢红包时大量用户涌入,但只有一个可以成功,其它的都会失败并给用户报错,导致用户体验极差;
    2. 抢红包时,如果第一时间有很多用户涌入 , 都失败回滚了 。过一段时间并发减小后,反而让手慢的用户抢到了红包;


      推荐阅读