B站千亿级点赞系统服务架构计划
原文链接:https://www.bilibili.com/read/cv21576373/
原文作者:哔哩哔哩技能团队-芦文超
点赞的功能太过于简单不再赘述,各人可以点击原文链接简单看下便可知晓。
本讲联合B站着名UP主陆总监的一期视频(https://www.bilibili.com/video/BV1mw4m1k71M)来做对比讲解。
表结构计划
陆总监的视频中提出的表结构方案
点赞关系表:主键ID,用户ID,内容ID,点赞时间。如果须要分表,则可以按照用户ID或内容ID来做。
内容的点赞总数表:内容ID,点赞数量
B站点赞系统的表结构方案
点赞记录表 - likes : 每一次的点赞记录(用户Mid、被点赞的实体ID(messageID)、点赞来源、时间)等信息,而且在Mid、messageID两个维度上创建了满意业务求的联合索引。
点赞数表 - counts : 以业务ID(BusinessID)+实体ID(messageID)为主键,聚合了该实体的点赞数、点踩数等信息。而且按照messageID维度创建满意业务查询的索引。
雷同点与不同点
其中的用户Mid中的M为member,B站自己对会员有普通会员与大会员的区别。实体ID与陆总监提出的内容ID意思一样。点赞系统在互联网行业发作之初就已经存在了,所以表结构的计划方式根本一样。
分库分表的方式也是一样的。不同点是B站的数据库采用TiDB,属于弹性数据库,不须要分库分表。陆总监视频中提到的数据库为MySql,再须要分库分表的场景下可以通过用户ID或内容ID来作为分片Key.
分片key是什么
表分片的意思是把一大张表的数据分割到不同的表中,除了表主键ID外,还须要一个同一的ID作为分片key,这样在操纵数据库的时候可以快速的定位到要操纵的数据存放在哪一张分片表中,不用一张表一张表的找了。比如用内容id作为分片key,就会尽可能的把内容id雷同的数据存放到一个分片中,加速查询速度。
焦点争议点-到底用不用缓存?
陆总监视频中说的很明确,俩字:看量!
如果点赞的哀求量本来就很小,那么直接利用mysql以及对应的两表计划足够了。
量大了怎么办?会带来怎样的题目呢?
前面的表结构计划中我们了解到,无论是B站还是陆总监给出的方案,都有一张点赞数表:
内容的点赞总数表:内容ID,点赞数量
当有大量的点赞操纵高并发的方式修改点赞总数表中的某一条视频的点赞数量时,会针对表中的这条记录产生一个“行锁”,这个行锁是灰心锁,简单说就是只有抢到锁的人才气修改点赞数,其他人要排队比及修改完成锁释放后再取竞争锁。如果点赞哀求并发量很高,排队时间就会很长。
排队时间长会导致如下的题目:
1.上卑鄙服务会由于排队题目导致调用接口超时。
2.过多的用户排队也占用了太多的数据库链接,数据库链接被耗尽后会导致系统瓦解。
办理方案:
陆总监给出的办理方案,利用MQ:
把用户的点赞哀求交给MQ,让高并发哀求变成队列,排队逐个访问数据库,而且参加队列的操纵是异步的,可以或许实时返回上卑鄙服务须要的结果,不会造成上卑鄙服务调用超时,别的,数据库避免了高并发访问,毗连池中的数据库链接也没有耗尽风险。
B站技能团队给出的办理方案:
先看原文:
针对写流量,为了保证数据写入性能,我们在写入【点赞数】数据的时候,在内存中做了部门聚合写入,比如聚合10s内的点赞数,一次性写入。如此可大量减少数据库的IO次数。
同时数据库的写入我们也做了全面的异步化处理,保证了数据库能以公道的速率处理写入哀求。
原文解析:
把点赞数先在Redis缓存中进行汇总,汇总10秒后一次交给数据库更新。比如批评每秒500次点赞,则先在缓存中汇总10秒,10秒后汇总了5000次点赞后,向点赞总数表的指定字段只发出一条更新下令。
别的数据库写入也做了异步化处理,类似陆总监的MQ方案,写入点赞记录表之前先在缓存中进行汇总,然后交给MQ排队,针对数据库公道速率进行批处理写入。
无论是哪一种方案,只要量大,我们发现都采用了异步操纵,异步操纵都采用了MQ处理,这就带来一个题目:
我给一个视频点了赞,但是这个操纵须要缓存10秒才气更新到数据库,我点赞后对应的视频的点赞按钮应当被点亮对吧,简单的办理方案就是用户点击点赞按钮后,前端做一个动态结果,切换按钮状态即可,但此时如果用户革新了视频,按钮状态就会被复原。所以,缓存中应当保存用户的点赞列表。
用户点赞后,先存入缓存中的点赞列表里,即便用户革新页面,前端加载后会调取缓存中的点赞列表,传入该视频的ID和点赞列表中的内容ID对比,如果存在,则点赞按钮被点亮。
详细是不是如此呢,我们展开B站点赞系统的团体架构过细验证一下吧!
B站点赞系统团体架构
整个点赞服务的系统可以分为五个部门
- 流量路由层(决定流量应该去往哪个机房)
- 业务网关层(同一鉴权、反黑灰产等同一流量筛选)
- 点赞服务(thumbup-service),提供同一的RPC接口
- 点赞异步使命(thumbup-job)
- 数据层(db、kv、redis)
黑灰产:搬运洗稿,刷粉刷量,养号生意业务等
下文将重点分享下**数据存储层、点赞服务层(thumbup-service)**与 **异步使命层(thumbup-job)**的系统计划
三级数据存储
根本数据模子:
- 点赞记录表:记录用户在什么时间对什么实体进行了什么类型的操纵(是赞还是踩,是取消点赞还是取消点踩)等
- 点赞计数表:记录被点赞实体的累计点赞(踩)数量
第一层存储:DB层 - (TiDB)
重点是两张表:点赞记录表(likes)和点赞计数表(counts)
第二层存储
缓存层Cache:点赞作为一个高流量的服务,缓存的设立肯定是必不可少的。点赞系统主要利用的是CacheAside模式。这一层缓存主要基于Redis缓存:以点赞数和用户点赞列表为例
什么是CacheAside模式:Cache Aside(旁路缓存)是一种用于进步数据访问性能的策略,通过在数据堆栈和缓存之间进行数据同步。这种模式处理了缓存数据一致性和过期的题目,但无法确保强一致性。比如在应用程序中,当须要访问某个数据时,Cache Aside 首先尝试从缓存中获取数据。如果缓存中不存在该数据,它会从数据库等数据源中获取数据,并将数据写入缓存。
①点赞数
- key-value = count:patten:{business_id}:{message_id} - {likes},{disLikes}用业务ID和该业务下的实体ID作为缓存的Key,并将点赞数与点踩数拼接起来存储以及更新
复制代码 模拟数据如下:
详细的数据示比方下:
- 键:count:patten:123:456
- 值:42,10
②用户点赞列表
- key-value = user:likes:patten:{mid}:{business_id} - member(messageID)-score(likeTimestamp)* 用mid与业务ID作为key,value则是一个ZSet,member为被点赞的实体ID,score为点赞的时间。当改业务下某用户有新的点赞操作的时候,被点赞的实体则会通过 zadd的方式把最新的点赞记录加入到该ZSet里面来为了维持用户点赞列表的长度(不至于无限扩张),需要在每一次加入新的点赞记录的时候,按照固定长度裁剪用户的点赞记录
复制代码 第三层存储
LocalCache - 当地缓存
当地缓存的创建,目标是为了应对缓存热点题目
缓存热点是指大部门甚至全部的业务哀求都掷中同一份缓存数据。固然缓存自己的性能比较高,但对于一些特殊热点的数据,如果大部门甚至全部的哀求都掷中同一份缓存数据,则这份数据所在的缓存服务器的压力也会很大。
将热点数据缓存在客户端的当地内存中,并设置一个失效时间。对于每次读哀求,首先检查该数据是否存在于当地缓存中,如果存在则直接返回,否则再去访问分布式缓存服务器。当地内存缓存彻底“解放”了缓存服务器,不会对其造成压力,但须要注意数据一致性题目
好了,到这里这篇文章的焦点内容就已经先容完了,剩下的根本都是容灾的内容,我简单给各人先容一下:
1.在TiDB数据库的底子上,迁徙数据到B站技能团队自研的TaiShan KV数据库,一方面做容灾备份,另一方面未来用于更换TiDB。
2.**点赞服务层(thumbup-service)**采用两地机房互为灾备。机房1承载全部写流量与部门读流量,机房2承载部门读流量。当DB发生故障时,通过db-proxy(sidercar)的切换可以将读写流量切换至备份机房继承提供服务。Redis也同样有多机房集群互为灾备。通过异步使命消耗TiDB的binLog维护两地缓存的一致性。(binLog,简单理解就是当tidb的数据发生变化时,会触发binLog,我们可以在触发的binLog事件中写入代码逻辑,本文中就是维护两地缓存一致性的逻辑)
读流量,机房2承载部门读流量。当DB发生故障时,通过db-proxy(sidercar)的切换可以将读写流量切换至备份机房继承提供服务。Redis也同样有多机房集群互为灾备。通过异步使命消耗TiDB的binLog维护两地缓存的一致性。(binLog,简单理解就是当tidb的数据发生变化时,会触发binLog,我们可以在触发的binLog事件中写入代码逻辑,本文中就是维护两地缓存一致性的逻辑)
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |