红包的架构设计简介
虽然春节已经过去⼀段时间,但不少⾥⾯依旧乐此不疲的在玩发红包活动,⽤户⾃发的将最初的⼀个春节拜年的场景功能慢慢演化成⼀个长尾功能。
  ⽤户在中抢红包时分成抢包和拆包两个操作。抢包决定红包是否还有剩余⾦额,但如果⾏动不够迅速,在拆包阶段可能红包已经被其他⽤户抢⾛的情况。
  红包的⾦额是在什么时候算? 据某架构腾讯财付通专家反馈,红包的⾦额是拆的时候实时计算,⽽不是预先分配,实时计算基于内存,不需要额外存储空间,并且实时计算效率也很⾼。每次拆红包时,系统取0.01到剩余平均值*2之间作为红包的⾦额。
  为了保证每次操作的原⼦性,拆包过程中使⽤了CAS,确保每次只有⼀个并发⽤户拆包成功。拆包CAS失败的⽤户可以由系统⾃动进⾏重试。但也有可能在重试过程中被别的⽤户抢得先机⽽空⼿⽽归,因此严格意义拆包的调⽤也未能保证⽤户先到先得。
  基于上⾯的原因,当时在中提到这种算法有些复杂,红包为了减少存储,每次进⾏了⼀个理解稍复杂的实时计算。对⽐⼤部分架构师想到的预分配⾦额的做法,预先分配⾦额需要将⾦额保存在⼀个内存队列中,如果红包的份额较多,则需要较⼤的存储空间。⽽红包仅保存 count:balance 这样2个数字。count指还剩⼏个⼈可以抢,balance只还剩下的⾦额。
  但是预分配⾦额也并不是⾮得需要额外存储。⽐如利⽤随机算法,在种⼦相同的情况下,随机数实际上返回的随机序列也是固定的。如以下Python代码,对于给定的seed 1024,每次执⾏返回的结果都是相同的。
>>> import random
>>> random.seed(1024)
>>> random.randint(1,100)
80
>>> random.randint(1,100)
49
>>> random.randint(1,100)
何琳老公39
>>> random.randint(1,100)
83
>>> random.randint(1,100)
88
再执⾏⼀遍,结果相同。
>>> random.seed(1024)
宋运萍
>>> random.randint(1,100)
80
>>> random.randint(1,100)
49
韩松落
>>> random.randint(1,100)
39
>>> random.randint(1,100)
83
>>> random.randint(1,100)
88
  因此预分配⾦额也只需要额外存储⼀个种⼦,或利⽤⼀些红包id做加密变换做seed达到零存储。⽽在发放红包时候,⽆需进⾏CAS操作,⽽只需要对剩余红包count做⼀个DECR操作。当count<0时,表⽰红包被拆包抢完。由于DECR是原⼦操作,⽆需加锁,⽤简单的⽅法达到了先拆包先得,原理上不存在早拆包但由于并发冲突失败⽽抢不到红包的情况。
  每个⼈分配的⾦额是:total * random(n) / random_total,不需要重复计算。random_total是指所有n个random值之和。
random(1)..random(n)不需要保存,因为对于给定的seed,random(1)到random(n)返回是固定的。
  以上算法评论与对⽐,与Tim所在雇主的红包算法⽆关,特此声明。
  部分细节下⾯列表已做说明,未做详细阐述。
  Reference(请打开阅读原⽂浏览)
  1、红包的架构设计简介
  2、红包实现原理
  3、⽹友周航⽼师基于聊天记录整理的红包架构图
  1、背景:有某个朋友在朋友圈咨询红包的架构,于是乎有了下⾯的⽂字(有误请提出,谢谢)
概况:2014年红包使⽤数据库硬抗整个流量,2015年使⽤cache抗流量。
的⾦额什么时候算?
  答:⾦额是拆的时候实时算出来,不是预先分配的,采⽤的是纯内存计算,不需要预算空间存储。
  采取实时计算⾦额的考虑:预算需要占存储,实时效率很⾼,预算才效率低。
  2、实时性:为什么明明抢到红包,点开后发现没有?
  答:2014年的红包⼀点开就知道⾦额,分两次操作,先抢到⾦额,然后再转账。
2015年的红包的拆和抢是分离的,需要点两次,因此会出现抢到红包了,但点开后告知红包已经被领完的状况。进⼊到第⼀个页⾯不代表抢到,只表⽰当时红包还有。
  3、分配:红包⾥的⾦额怎么算?为什么出现各个红包⾦额相差很⼤?
答:随机,额度在0.01和剩余平均值*2之间。
例如:发100块钱,总共10个红包,那么平均值是10块钱⼀个,那么发出来的红包的额度在0.01元~20元之间波动。
当前⾯3个红包总共被领了40块钱时,剩下60块钱,总共7个红包,那么这7个红包的额度在:0.01~(60/7*2)=17.14之间。
注意:这⾥的算法是每被抢⼀个后,剩下的会再次执⾏上⾯的这样的算法(Tim⽼师也觉得上述算法太复杂,不知基于什么样的考虑)。
厦门娱乐这样算下去,会超过最开始的全部⾦额,因此到了最后⾯如果不够这么算,那么会采取如下算法:保证剩余⽤户能拿到最低1分钱即可。
如果前⾯的⼈⼿⽓不好,那么后⾯的余额越多,红包额度也就越多,因此实际概率⼀样的。
  4、红包的设计
答:从财付通拉取⾦额数据库来,⽣成个数/红包类型/⾦额放到redis集⾥,app端将红包ID的请求放⼊请求队列中,如果发现超过红包的个数,直接返回。根据红包的申请处理成功得到令牌请求,则由财付通进⾏⼀致性调⽤,通过像⽐特币⼀样,两边保存交易记录,交易后交给第三⽅服务审计,如果交易过程中出现不⼀致就强制回归。
5、发性处理:红包如何计算被抢完?
答:cache会抵抗⽆效请求,将⽆效的请求过滤掉,实际进⼊到后台的量不⼤。cache记录红包个数,原⼦操作进⾏个数递减,到0表⽰被抢光。财付通按照20万笔每秒⼊账准备,但实际还不到8万每秒。
  6、通如何保持8w每秒的写⼊?
答:多主sharding,⽔平扩展机器。
7、据容量多少?
答:⼀个红包只占⼀条记录,有效期只有⼏天,因此不需要太多空间。
明星最想删掉的照片
  8、询红包分配,压⼒⼤不?
答:抢到红包的⼈数和红包都在⼀条cache记录上,没有太⼤的查询压⼒。
9、⼀个红包⼀个队列?
答:没有队列,⼀个红包⼀条数据,数据上有⼀个计数器字段。
  10、有没有从数据上证明每个红包的概率是不是均等?
答:不是绝对均等,就是⼀个简单的拍脑袋算法。
  11、拍脑袋算法,会不会出现两个最佳?
答:会出现⾦额⼀样的,但是⼿⽓最佳只有⼀个,先抢到的那个最佳。
英语完形填空及答案
12、每领⼀个红包就更新数据么?
答:每抢到⼀个红包,就cas更新剩余⾦额和红包个数。
  13、红包如何⼊库⼊账?
答:数据库会累加已经领取的个数与⾦额,插⼊⼀条领取记录。⼊账则是后台异步操作。
  14、⼊帐出错怎么办?⽐如红包个数没了,但余额还有?
答:最后会有⼀个take all操作。另外还有⼀个对账来保障。