如何用 Redis 有序集合解决微博的大 key 问题

[复制链接]
查看56 | 回复0 | 2024-11-7 05:04:05 | 显示全部楼层 |阅读模式
微博热度排行榜的排名机制持续受到广泛关注,尤其是基于双击数量进行排名的方法,其实现过程及其中所涉及的诸多问题引发了广泛的争议和挑战。

假设按双击排名的初步实现

微博采用Redis有序集合技术,实现了基于双击量的排名功能。在该应用中,外层键通过固定前缀与排行榜名称的组合构成,便于识别和管理。内层值则存储微博ID,score字段则记录双击次数。每当发生双击操作,微博的双击数量便可通过zincrby命令实现增长。从理论上看,这种做法能够较为精确地统计双击数量,为排行榜提供数据支持。但需注意,这仅是理想状态下的初步构想。

技术方案的实施过程中,将遭遇众多挑战。各微博平台的技术架构不尽相同,服务器承载能力和网络状况亦对排名机制构成影响。特别是在高流量时段,如热门话题热议之际,若众多用户同时进行双击操作,能否迅速且精确地更新排行榜数据,成为亟需解决的关键问题。

大key问题的应对

微博数据量巨大,若所有信息都集中在一个主键中,将不可避免地引发大key问题。一种可行的解决方案是将单一主键拆分为100个子键,每个子键负责存储微博数据的一部分。在所有子键均位于同一Redis环境的情况下,可利用Lua脚本执行相关操作。然而,当这100个子键分散至不同的Redis实例时,操作流程将变得复杂,Lua脚本的使用则不再适用。

在某大型微博平台的应用中,经过key的拆分,数据管理效率有所提高。然而,不同key间的数据交互与同步问题随之显现,成为新的挑战。每个key对应微博数据的某一部分,确保数据汇总准确无误至关重要,尤其是在双击操作频繁的情况下,这一问题亟待深入研究。

汇总key保存排行N数据

<p><pre>    <code class="prism language-cpp"><span class="token comment">// 写操作</span>
zincrby key_<span class="token punctuation">{</span><span class="token number">1</span><span class="token operator">-</span><span class="token number">100</span><span class="token punctuation">}</span> increment score<span class="token punctuation">;</span>
zincrby key_sort_<span class="token punctuation">{</span>N<span class="token punctuation">}</span> increment score<span class="token punctuation">;</span>
<span class="token comment">// 如果已经包含N个元素,删除最小的一个</span>
<span class="token keyword">if</span><span class="token punctuation">(</span>zcard key_sort_<span class="token punctuation">{</span>N<span class="token punctuation">}</span><span class="token operator">></span>N<span class="token punctuation">)</span><span class="token punctuation">{</span>
        zremrangebyrank key_sort_<span class="token punctuation">{</span>N<span class="token punctuation">}</span> <span class="token number">0</span> <span class="token number">0</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre></p>
无论是单一Redis中的100个键,抑或是多个Redis环境,均能通过汇总键存储排名前N的数据。在执行写入操作后,将微博的双击数异步累加至汇总键。即便异步线程更新出现故障,也并非每次都会失败。查询排名前N的数据时,亦需与该汇总键相联系。

<p><pre>    <code class="prism language-cpp"><span class="token comment">// 读操作</span>
zrevrange key_sort_<span class="token punctuation">{</span>N<span class="token punctuation">}</span> <span class="token number">0</span> <span class="token operator">-</span><span class="token number">1</span> WITHSCORES
</code></pre></p>
此方法有助于降低数据查询与处理的复杂性,然而,在实际执行过程中,异步更新的滞后性可能会引发数据暂时性的不准确。以瞬间高流量双击为例,排名前N的数据在短时间内可能产生误差,进而影响用户的使用体验。

加入浏览量后的设计思考

在排行榜的评价标准从单纯的双击量转向同时考虑浏览量时,设计层面需更为周全。简单地将双击次数与浏览量直接相加并非恰当做法。首先,必须明确两者之间的权重分配。鉴于不同微博平台业务侧重点的差异,它们可能对双击次数和浏览量赋予不同的权重设定。

此外,还需关注如何有效收集这两种数据并确保记录的准确性。例如,某些平台对浏览量的界定可能更为严格,需剔除如机器刷量等异常情况,这无疑对数据收集的技术标准提出了更高要求。

Redis数据持久化引发的排名变化

Redis的数据存储于内存之中,当未进行数据持久化或在特定时间范围内key的排名出现变动时,一旦Redis系统重启,用户端将面临排行榜信息前后不一致的问题。为解决此问题,可以实施定期的数据持久化策略或实施实时数据备份措施。

<p><pre>    <code class="prism language-cpp"><span class="token comment">// lua脚本执行下面2条命令</span>
zincrby key_sort_<span class="token punctuation">{</span>N<span class="token punctuation">}</span> increment score<span class="token punctuation">;</span>
<span class="token comment">// 如果已经包含N个元素,删除最小的一个</span>
<span class="token keyword">if</span><span class="token punctuation">(</span>zcard key_sort_<span class="token punctuation">{</span>N<span class="token punctuation">}</span><span class="token operator">></span>N<span class="token punctuation">)</span><span class="token punctuation">{</span>
        zremrangebyrank key_sort_<span class="token punctuation">{</span>N<span class="token punctuation">}</span> <span class="token number">0</span> <span class="token number">0</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre></p>
然而,在设置定期持久化的时间间隔时,难以准确把握。若时间间隔过长,一旦遭遇意外重启,可能会导致大量数据丢失;反之,若时间间隔过短,则会显著增加对系统资源的消耗。此外,实时备份对数据处理和存储的负载也有较高要求。

给读者的问题

在深入探究了微博热度排行榜的排名依据——基于双击数量,并认识到其中存在的各种问题时,对于在排行榜中引入更多考量因素,例如浏览量,如何确保数据的精确性同时维持排行榜的稳定性,您有何见解?我们期待广大读者通过评论积极表达个人观点,并对本文进行点赞及转发。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则