1.整体设计
- 用户id取模,放到到redis槽位的排行榜(lotter:user:01 ~ lotter:user:127),存放top1000
- 定时任务定时将分片槽位排行榜汇总到lottery:top,取Top1000
- 本地缓存lottery:top,定期刷新
2.流程图
3.流程
- 分128(支持可配置)个key的zset(lottery:user:01),根据0~127。每个只存1000(可配置)条数据
- 每次积分更新,用uid对128取模找对应的槽位,落到对应的zset
- 起一个定时任务,定时将这个128key的放入另外一个最终排行榜zset,也是只取前1000(可配置)条数据
- 服务再使用本地缓存,缓存这个zset,根据需求,10s更新一次或者1min更新一次
4.落库
这里的流程图是没考虑落库的,但是实际中,是需要考虑的。根据业务形态来,可以考虑设计积分表,分库分表,然后异步落库。
当然如果压力不大,可以考虑去先写库再写redis,但是针对一些超高并发、并且是短期的排行榜,可以考虑就存redis,然后异步写mysql,同时记得打日志,到时候方便回溯。
5.考虑点
- 拆分成128个对应槽的Zset:
是由于考虑存在读写热点、以及大key问题。所以改成128个,将写压力分摊到多个redis节点。
同时我们这里用的槽位,这样redis的扩、缩容不影响数据 - 定时任务更新到排行榜Zset:
这里不考虑实时,如果更新频繁,可以将定时任务的间隔调小(秒级别、分钟级别、小时级别、天级别),不然前面的拆分就失去了意义,仍然存在热点key - 本地缓存排行榜:使用本地缓存来解决排行榜读的压力,否则排行榜Zset又会重新新的热点Key
- 考虑批量写、批量读
- 活动中期对于排名靠后的用户,根据业务规则,可以考虑不参与排序,减少压力