李优秀 发表于 2025-3-31 20:26:53

B站批评体系的多级存储架构

1.  背景


批评是 B站生态的紧张构成部分,涵盖了 UP 主与用户的互动、平台内容的推荐与优化、社区文化建立以及用户情感满意。B站的批评区不仅是用户互动的核心场所,也是平台运营和用户粘性的关键因素之一,尤其是在与弹幕联合的情况下,成为平台的标志性特色。
在社会热点事件发生时,批评区的读写流量会急剧增加,直接影响业务运行,对用户体验、内容创作和社区文化等多个方面产生负面影响,以是批评服务的稳固性至关紧张。
批评体系对缓存命中率要求非常高,一旦发生缓存失效,大量请求会直接访问 TiDB,如果 TiDB 出现问题,将导致批评服务不可用。以是批评必要构建一套可靠的容灾体系,并具备自动降级能力,以提拔批评服务的整体稳固性。


https://i-blog.csdnimg.cn/img_convert/26e30e09c94ec3a9a6fb3ae60482697c.png

https://i-blog.csdnimg.cn/img_convert/149c37e669970f24f6a5187b3fe6ea1f.png

2. 架构设计


批评体系架构主要依赖 Redis 缓存和 TiDB 存储,列表接口中依赖多种排序索引,如点赞序、时间序、热度序等,这些索引通过 Redis 的 Sorted Set 数据结构举行存储。
在大批评区中查询这些排序索引,当 Redis 缓存 miss 时必要回源 TiDB 查询,因数据量过大、查询耗时较长,慢查询会占用大量 CPU 和内存,进而导致其他查询的延迟或壅闭,严峻影响整个 TiDB 的吞吐量和性能。例如查询点赞序排序索引:

SELECT id FROM reply WHERE ... ORDER BY like_count DESC LIMIT m,n
为了克制 TiDB 故障导致批评服务不可用,我们希望创建一套新的存储体系,办理 TiDB 单点故障问题。该体系不仅为业务提供容灾能力和自动降级通道,还能在大流量查询场景下提供更优的查询性能,从而提拔整体批评服务的稳固性。
基于 B站自研的泰山 KV 存储(Taishan),我们搭建了「多级存储架构」,整体设计方案的核心思绪包罗:


[*] 将排序索引的存储从「结构化」转为「非结构化」
[*] 将排序索引查询从「SQL」转换为性能更高的「NoSQL」
[*] 通过「写场景的复杂度」来换取「更优的读场景性能」


https://i-blog.csdnimg.cn/img_convert/17b11c598bcb82497fe2ea30c7f972c9.png

3. 存储设计


 存储模子


在批评的业务场景中,我们抽象了两种数据模子:排序索引(Index)、批评物料(KV)

抽象数据模子 TiDB 模子
Taishan 模子
说明
Index
Secondary Index
Sorted Set
排序索引,例如点赞序、时间序的排序索引
KV
Primary Key & Row
Key-Value
包罗元数据、内容等必要的批评物料

下图以按点赞序排序的前 10 条批评为例,展示了利用 Index + KV 模子实现的详细思绪。在更复杂的推荐排序场景中,依然可以通过此模子来实现。详细流程是:首先通过排序索引召回一批批评 ID,再通过推荐算法对这些 ID 举行重排,最终根据重排后的 ID 获取批评详情并返回给用户。


https://i-blog.csdnimg.cn/img_convert/aecb86b12f76afa90c07cf7b399fdab1.png

将领域对象的存储建模划分为 Index 和 KV 两种模子,可以利用差异的底层存储结构来分别优化查询、扫描、排序、分页等场景。利用 Redis 或 Memcache 作为缓存构建 KV 模子,提拔查询性能,利用 Redis 的 Sorted Set 构建 Index 模子,支持增量数据及时更新排序索引,并提供极高效的分页查询性能。在关键词搜刮场景,可接纳 ElasticSearch 作为检索索引,克制在原始数据库上举行低效的遍历操作;
基于 KV 作为唯一事实表,接纳同步全量数据和及时捕获增量数据的方法,将原始数据转换为下游索引表。这样可以机动构建定制化的排序索引,以应对多变的批评业务需求。同时该方案在不影响原有业务逻辑和存储资源的情况下,实现了业务、代码和数据的解耦。
如果索引的界说和实现不再范围于源数据库的原生索引,而是扩展到应用逻辑,并在其他存储上自行维护物化视图,这必然会带来额外的明确和维护成本,同时引入一致性的困难。然而考虑到批评业务的数据量级和复杂度,该方案的整体上风仍然大于劣势。以是我们必要新的存储方案,既支持根本的 Index 和 KV 模子,又能满意高性能、可用性和扩展性等方面的需求。

数据范例


我们盼望将数据范例从 SQL 转向 NoSQL,因为 NoSQL 提供了更机动的数据模子,意味着更可表明的执行筹划和更高的优化潜力。例如 Taishan 查询 Redis Sorted Set 的 P999 耗时约 10ms,查询 KV 的 P999 耗时约 5ms,这种高效的查询性能对批评业务尤为紧张。
相比 TiDB,Taishan 不支持 ACID 事务、二级索引等功能,提供的能力更为精简。基于之前的履历和问题,偶然候“less is more”反而能带来更高的可用性。以下是批评业务在利用 TiDB 时碰到的一些问题:

[*] MVCC 机制:TiDB 的事务实现基于 MVCC 机制,当新写入的数据覆盖旧数据时,旧数据不会被删除,而是以时间戳区分多个版本,并通过定期 GC 清理不再必要的数据。在热门批评区中,频繁更新点赞数时,排序索引的 MVCC 历史版本过多,导致 TiDB 的读写性能下降。相比之下,不支持 MVCC 的 Taishan 在查询排序索引时能提供更高效、更稳固的性能;
[*] 分片策略:TiDB 不直接支持完全自界说的分片策略,而 Taishan 支持哈希标签(hash tags),可以在 Key 中利用大括号 { } 指定参与哈希计算的部分。这样多个 Key 可以利用相同的 ID 举行分区路由,确保同一批评区的批评位于同一分片。在批量查询批评时,这能大幅降低扇出度,减少长尾耗时的影响。而对于大概出现热点的场景,Taishan 可以选择不利用哈希标签,从而打散请求,为性能要求高的批评业务提供更大的优化空间。
基于现有批评在 TiDB 中的存储结构和索引设计,以时间序和点赞序为例,列举 Taishan 的数据模子如下:


https://i-blog.csdnimg.cn/img_convert/7f262d0aa6a4c37ba026feddfcf1361a.png

4.  数据一致性


从 TiDB 的结构化数据变革为 Taishan 的非结构化数据,目前缺乏现成的同步工具,必要业务自行实现数据同步。然而数据同步过程中大概出现数据丢失、写入失败、写冲突、顺序错乱和同步延迟等问题,导致数据不一致。由于批评业务对数据一致性要求较高,我们必要一套可靠的数据同步方案,确保两者之间的数据一致性。

重试队列


针对写失败的问题,我们通过引入重试队列来办理,将写失败的请求放入队列中举行异步重试,确保数据不会因暂时性问题而永世丢失。引入重试队列大概会导致写并发产生数据竞争,进而引发数据最终不一致。虽然可以通过 CAS(Compare-and-Swap)来办理数据竞争问题,确保“读取-修改-写回”操作的原子性,但这也大概带来乱序问题。


https://i-blog.csdnimg.cn/img_convert/8c7bebc4054cd33123b0f2a084a5b237.png

乱序问题


由于写数据有多个场景来源,包罗 binlog 同步、重试队列,这些并发写操作导致数据错误,此外 MQ 消息因 rebalance 大概会被重新消费,导致消息回放,以是数据同步过程中不仅必要保证幂等性,还必须确保消息的顺序性:
例如并发写的场景,批评 A 被点赞两次,点赞数(like_count)为 2,TiDB 会生成两条 binlog 数据:

[*] 第一条数据 binlog_0 中,like_count = 1,由于网络原因写入失败,数据被转入重试队列举行异步处理。
[*] 第二条数据 binlog_1 中,like_count = 2,写入成功,批评 A 的 like_count 更新为 2,符合预期。
[*] 然而,重试队列继续处理 binlog_0,由于无法保证两个写操作的顺序,写入后 like_count 被更新为 1,导致数据不一致。

回退问题


在消息回放场景中,假设批评 A 被点赞三次,点赞数(like_count)为 3,TiDB 会生成三条 binlog 数据 。正常情况下,这三条数据会被顺序消费并处理。如果在消费过程中发生 rebalance,导致消息回放,这三条数据会被重新消费,从而导致点赞数出现短暂的数据回退。

版本号


为克制乱序和回退问题导致的数据不一致,我们引入了版本号机制,每次批评数据变更时,版本号会递增。

UPDATE reply SET like_count=like_count+1, version=version+1 WHERE id = xxx
在 CAS 写操作时,将 binlog 数据中的 version 值与 Taishan 中数据的 version 值举行比对。如果 binlog 中的 version 值大于或等于当前数据的 version 值,则执行更新;否则认为该数据为逾期数据,予以丢弃。


https://i-blog.csdnimg.cn/img_convert/f9efc79e392f3725337c3f3ff6786dd8.png

 对账体系


根据 CAP 理论,在保证可用性(Availability)和分区容忍性(Partition)之后,分布式体系无法完全保证一致性(Consistency)。尽管引入了重试机制、CAS 和版本号机制,但由于网络调用的不可克制失败,批评数据之间不免会出现长期或短期的不一致状态。一旦发生不一致,必要有一套对账机制来及时发现并修复这些不一致的数据。

及时对账


通过 TiDB 的 Binlog 事件驱动,利用延迟队列延迟 n 秒后消费, binlog 数据关联查询 Taishan 数据,并对比两者的数据。对于发现的非常数据,举行通知并触发数据修复。

离线对账


利用 TiDB 和 Taishan 的数仓离线数据,举行 T+1 数据对比,验证数据的最终一致性。


https://i-blog.csdnimg.cn/img_convert/bfb96d481f33fa8e4f449da1c77a4dfa.png

5.  降级策略


批评业务对可用性的要求非常高,尤其是在高并发、及时性强、用户互动频繁的场景下。通过搭建多级存储架构,我们能够在 TiDB 故障时自动降级到 Taishan,确保批评服务持续正常运行,我们的目标是实现每个请求的自动降级。
每次请求时,首先尝试从主存储获取数据。当主存储服务返回错误或长时间无响应时,降级到次要存储服务获取数据。在设计降级策略时,通常接纳串行或并行方式,分别影响体系的响应时间和复杂性,而且整体耗时不能超过上游的超时限定,否则降级无效。

降级策略
上风
劣势
串行
简朴
耗时长,容易整体超时
并行
耗时短
多1倍的请求,浪费资源

串行策略无法满意批评业务对响应时间的要求,而并行策略则大概浪费资源。以是我们选择了「对冲策略」(Hedging Policy)。在主节点请求超时后,我们会发起一个「备份请求」(backup request)到次节点。如果主节点返回成功,则直接返回效果,否则等待次节点的响应。通过根据主次节点的耗时特性设置合理的延迟阈值,我们在整体响应时间和资源消耗之间达到了平衡。


https://i-blog.csdnimg.cn/img_convert/462e33835fd0e7ae9b09008a20e8a365.png

在现实生产环境中,我们根据详细业务场景设定 TiDB 和 Taishan 的主次关系。对于对数据及时性敏感、查询轻量的场景,设定 TiDB 为主存储,Taishan 为次存储;而对于数据及时性要求较低、大 SQL 查询的场景,则设定 Taishan 为主存储,TiDB 为次存储。
某天凌晨,TiDB 底层的 TiKV 节点宕机,在 TiKV 自愈期间,体系自动降级到 Taishan,批评业务未受影响,线上服务持续稳固运行。


https://i-blog.csdnimg.cn/img_convert/c3711dfcfaf53509f8f7314c4f12ae4f.png

6.  总结与展望


B站批评服务对社区业务和用户体验至关紧张。我们不仅致力于持续提拔批评服务的稳固性,还不停优化批评业务,以便用户能够看到更优质的批评内容,从而增强归属感和认同感,提供更好的消费体验。
最后,发一条和睦的批评吧,遇见和你一样有趣的人。

-End-
作者丨rumble、脆鲨鲨


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: B站批评体系的多级存储架构