关于Redis TopK实现亿级文章概率型阅读量Top10的跟踪疑问
嘿,我正好在生产环境里用Redis TopK处理过类似的海量内容排行场景,来给你捋捋这些问题的核心和解决方案:
首先得明确:Redis TopK是概率型数据结构,它的设计目标就是用极少的内存(亿级数据下通常几MB就够)来近似计算Top K元素,代价就是它不会保存所有元素的计数,也不会记录被淘汰出候选集的元素——这正是你疑问的根源。
关于被挤出TopK的文章如何跟踪的问题
当新文章冲进第5位把第10位挤出去时,Redis TopK本身不会留存这个被淘汰的articleId的任何信息,因为它的内部只维护一个固定大小的候选堆和用于近似计数的sketch结构。要解决这个问题,你需要自己补充一套辅助存储机制:
- 可以用一个Redis Hash结构,专门保存所有曾经进入过TopK的文章的近似阅读量(每次文章在TopK里更新计数时,同步更新这个Hash)。这样即使它被挤出TopK,你依然有它的计数记录,后续如果它的阅读量再次上涨,你可以用
TOPK.INCRBY把它的计数重新喂给TopK结构,看是否能重新进入候选集。 - 如果担心Hash内存占用太高,也可以设置一个阈值,比如只保存阅读量超过100的文章,过滤掉低阅读量的长尾内容,这样能大幅节省内存。
关于初始没进榜的文章后续阅读量上涨的跟踪问题
同样,Redis TopK不会主动监测不在候选集里的元素,所以你需要一个前置的计数或过滤机制:
- 最简单的方式是用一个Redis Hash来记录每篇文章的基础阅读量(每次有阅读请求时,先执行
HINCRBY article_read_counts articleId1 1)。然后定期(比如每分钟)做一次扫描,把Hash里阅读量超过当前TopK最低阈值的文章,批量用TOPK.INCRBY命令更新到TopK队列里——比如你用TOPK.LIST mostReadArticle拿到当前的Top10列表,再用TOPK.QUERY获取它们的近似计数,取最小值作为阈值,把超过这个值的文章喂给TopK。 - 要是亿级数据下扫描Hash太耗时,你可以换成用Redis Stream来记录所有阅读事件,然后用消费者组来批量聚合阅读量,再同步更新到Hash和TopK结构里,这样能避免全量扫描的性能问题。
再聊聊你用的TOPK.INCRBY命令
你当前用的TOPK.INCRBY mostReadArticle 150 articleId1 221 articleId2这个命令,作用是给指定的文章ID增加对应的阅读量。这里要注意:如果articleId1不在TopK的候选集里,Redis TopK会基于它的概率算法(比如判断这个增量是否足够让它进入候选集)来决定是否把它加入——这就是概率型结构的特点,存在极小的漏判概率,但对于亿级数据的Top10场景,这个误差完全在可接受范围内。
最后要提醒的是,Redis TopK的核心是近似、高效,如果需要100%精确的Top10,那它就不适用了,得用Sorted Set,但亿级数据下Sorted Set的内存占用会非常恐怖,显然不现实。所以结合辅助存储来弥补TopK的“遗忘”问题,是海量场景下的最优解。
内容来源于stack exchange

