从微博几年前放出来的 slides [1][2] 上看,有将关注/被关注视为 『长列表』 来针对性的解决关注/被关注的扩展性问题,猜点赞/我的点赞列表该也差不多。
这类 『长列表』 场景有一定共性:数据规模显著高于内容类数据:发微博来自用户深思熟虑的行为,而点赞、关注是无意识行为,容易产生大规模数据;
数据分布极度不均匀:大V 与小透明的数据量差异极大,前者轻松几百万赞/关注,后者零星可怜几个赞/关注,这时按后者没有性能问题的查询在前者身上容易跪惨,而这类性能问题在早期用户活动较少时不会暴露;
有反向查询的需求:比如我点赞了你的微博,你的微博下面需要列出点赞的人的列表,我也需要看到自己点过赞的微博列表;
有基数查询的需求:需要查看点赞者数量,也需要看我点赞过的数量;
有这几个问题在,早期的单表点赞/被点赞 + 反向索引的做法会很容易遭遇存储瓶颈(数据规模大)和性能瓶颈(计数类操作开销大)乃至稳定性问题(赶上无分页捞全部数据时压力山大)。
不过题主讲的 "一条微博的点赞数就可能有几十万,这个数据量对关系型数据库来说也是大到超乎想象的" 数据规模并不大,一个 shard 上突发的热点数据访问,可以通过缓存 + 读写分离 hold 住大部分问题。
更具体的设计上,结合一点自己的经验:
schema 设计上:
避免反向索引,允许分库分表:单表多维度查询的存在使数据表难以 shard;
替代以业务层双写,或者统一的关系服务中实现双写逻辑;
业务层的数据访问上:首先一定要缓存,乃至直接用持久性的 redis 做存储,严格避免一个 query 捞回几十万 id 的行为直接落数据库上:点赞操作发生时,务必要『更新』缓存,而非『失效』缓存;
计数类操作一定要缓存:同样,点赞操作发生时,务必先『增加』缓存中的计数,而非『失效』计数缓存;
服务架构上:
类似的『长列表』场景在社交网站中无处不在,往往也是最显容易暴露瓶颈的场景,根据问题共性,开发类似的『关系服务』统一解决扩展性问题。
References