一、业务场景在开始讲解之前,先介绍一下哔哩哔哩的业务场景。B站的业务大致可以分为以下几类:1.点播服务点播服务是人们经常看的视频、文稿等相关服务。这类数据使用场景的特点是:数据一致性要求高耗时、敏感流量、高可用性要求2、直播业务直播业务对应B站S12、跨夜、拜年等,并且具有以下特点:数据一致性对热点数据要求高,比如S12的主播房。流量中等,大型直播流量将呈现爆发式增长。高可用性要求。3、对游戏服务的数据一致性要求高。耗时且敏感的流量。高可用性要求。需求如下:数据一致性要求高热点数据,集中在秒杀场景和热播剧,平时流量中等,热播剧和产品会呈现爆发式增长高可用性要求5.支付业务数据一致性要求极高易用性要求高流量不大2.架构演进介绍完B站的业务场景,接下来就是B站整体数据库架构的演进历史。1.Phase1.0Phase1.0其实所有互联网公司的架构都差不多-简单的主从,所有流量都集中在一个主数据库上。另外,场景和之前使用的商业数据库类似——单实例多库。这种架构在公司刚起步的时候比较方便,便于业务快速迭代,但是随着流量的增长,会出现以下问题:1)单机的性能瓶颈CPU、内存、服务器的存储限制我们不可能一直垂直升级,所以就有了我们第一次架构演进的小版本——读写分离,一主多从。该场景有两个核心需求:低数据一致性要求和低数据敏感度要求以上两个需求场景可以很好的避免MySQL主从复制延迟带来的问题,同时满足快速复制带来的流量压力业务增长。2)各业务的相互影响随着业务的发展,各业务之间的相互影响促使我们架构的第二个小版本的出现——根据业务库进行迁移和拆分。基于读写分离和业务库维度的拆分,无法避免各个功能模块的相互影响。在这种情况下,架构1.0阶段的第三个小版本应运而生——按照业务的功能维度进行拆分,将一个X库拆分为n个库,拆分后分布在不同的实例中。在每个不同的实例下,我们都会有不同数量的从库来支撑业务的流量增长,以满足大部分场景的业务需求。现在哔哩哔哩也有很多业务采用类似的架构,我们可以通过业务垂直拆分来满足我们的业务增长。2.Phase2.0ArchitecturePhase2.0—Horizo??ntalsplit。成熟、稳定、定制化的Proxy是横向拆分的利器,符合要求的Proxy需要时间打磨。为了满足业务的快速发展,我们选择在业务层实现,也就是我们在代码层实现路由。虽然配置会比较繁琐,但是可以满足大部分业务场景。很多互联网公司都有类似的阶段。在业务端水平拆分之后,我们其实面临着一个新的问题——跨实例查询。三、3.0阶段1)第一阶段进入哔哩哔哩进化的3.0阶段。我们引入了TiDB,将之前业务层面的碎片化数据通过TiDB自带的DTS同步到TiDB集群中,从而满足大部分业务的查询需求。同时,我们在一些场景的业务中直接尝试使用TiDB。引入TiDB后,我们根据Bilibili的特点,在本地进行了定制。由于TiDBServer是无状态的,所以对于如何路由到各个节点并没有官方的解决方案。因此,我们结合B站的基础平台能力,将所有TiDBServer容器化到PaaS上,将我们的服务发现能力与TiDBServer集成,并改造对应语言的SDK,从而实现TiDBServer负载均衡,解决了瓶颈TiDBServer本身,比如:故障转移、业务快速感知节点变化、连接数等。2)第二阶段已经到了3.0的第二阶段。我们已经将Proxy打磨成一个非常成熟的产品。同时,为了满足支持多人异地直播的场景,我们还定制了DTS来部署我们的同城数据库。多活直接实现多地异地居住,即两地三中心的结构。首先,在DTS方面,我们也根据B站技术栈的特点做了很多定制,与其他公司的开源组件有一定的区别。例如,对于冲突检测,我们提供了多种可选规则,包括特定字段匹配和全字段匹配。同时,对于冲突字段数据的R数据处理,我们一般有两种方法:一种是直接冲突的场景,把不重要的数据直接放到我们的队列中,也就是我们公司以前的DataBus;其次,业务方可以根据发送到DataBus的数据处理冲突数据,通过DTS提供接口返回数据写入特定机房,因为当数据需要重新写入数据库时??,反还需要loopback处理,所以我们在DTS上提供了一个接口供业务使用。其次,在主从切换的时候,由于两地三中心需要保证数据能够来回切换,虽然是全局切换,但是在一些边缘场景下还是会出现数据冲突的情况。所以我们也提供了主从切换下的数据冲突和相关信息的打捞队列,实现二次处理的功能,这也是我们中间DTS提供的能力。Proxy的能力和其他主流公司差不多,都支持读写分离、分库分表、限流、黑白名单等。对于Proxy的部署,我们采用了两种解决方案:一是集中部署,类似于大家常说的网关模式,可以方便的进行统一限流和资源调控;另一种是Sidecar模式,应用层使用。比较简单,直接配置本地IP即可,但同时带来了全局控制(限流、连接等)、成本等其他问题。3.架构设计接下来介绍B站针对不同数据量场景的架构设计理念。1、大型直播活动整体分为四种:1)高并发写入高并发写入考验的是主库的写入能力和从库的复制能力。2)高并发查询高并发查询一般会引入缓存能力。缓存主要涉及以下内容:分布式缓存主要解决容量问题;LocalCache在应用层提供能力,可以在应用本地缓存一些数据,但是数据可能存在不及时的问题;多级缓存可以缓解流量爆发式增长带来的压力。3)实时排序??实时排序最直观的场景就是观众在直播间刷礼物时显示的排名。为了保证时效性和有序性,我们一般使用Redis有序集合。4)预期意外突发流量对我们来说,预期意外突发流量的考验主要是应用层的快速扩容,以及如何切入流量的峰值,同时保证数据库顺利写入,即异步写入。今年最明显的突发意外流量场景就是佩洛西事件,比我们平时的流量大了近5倍。2、电商推广整体上有以下几个特点:1)秒杀场景秒杀场景主要涉及到合适模型的选择和请求的简化。只有基于公司的基础架构进行定制,才能达到最佳的性能和体验。2)订单订单具有明显的冷热数据特征。一般情况下,我们的订单会分为一年前、两年前和实时订单的不同拆分。对于数据库来说,这个考验的是归档和查询数据的能力。3)存货库存与闪购场景存在一定的相关性,但不完全相关。秒杀的场景会涉及到库存,但是平时库存总是会用到的,所以两者不能强挂钩。库存场景主要是为了保证库存减少的准确性,减少客户端访问时可能存在的冲突。另外就是一个一致性问题,即在闪购和减库存的时候不能出现超卖的情况,以免给商家造成损失。4)流量调峰流量调峰不同于大型直播遇到的突发流量,因为这部分流量是我们已知的,我们已经预估会有多少流量,所以我们一般会进行队列处理并做分层。前面介绍了大型直播和电商促销两个典型场景。我们做了一部分的数据库架构设计,以及与应用端的联动。下面介绍我们在实际设计数据库架构时需要考虑的重点。3.数据首先要考虑数据。根据我们数据类型的使用场景,我们可以将数据分为以下三种:1)配置类型配置类型类似于我们的数据字典和一些权限配置。特点包括:量小,几乎没有Transaction依赖多读少写。如果需要高并发访问配置数据,只需要加缓存即可,不需要做太多处理。2)日志型日志型数据包括交易流水、订单状态等。我觉得日志型数据也可以称为流水型数据,它的特点包括:体量大:不可避免,因为我们需要记录状态中间的每个部门;无事务依赖:我们后续多查询,少改动;多写少读:阅读的比例一般为写作的零点几。3)状态型数据量:与业务相关,状态型数据可以理解为我们的订单,以及直播场景中刷礼物的扣费;交易依赖性强:必须保证用户下单成功后的库存扣款,以及用户打赏主播后平台的扣款和主播收到的礼物;多读多写:与用户进度挂钩,多写多读场景。综上所述,一般通过数据量、事务、读写请求三个维度来判断数据,从而对数据进行整理整理。通过对比我上面列出的三种数据类型,可以得到具体的数据类型。有了数据类型之后,我们就可以考虑下一个阶段,就是对数据库的业务需求。4、业务业务对数据库选型的要求比较多,包括事务、性能、扩缩容、高可用、迁移等。1)事务的事务要求需要根据数据类型判断。2)性能部分业务对时间消耗比较敏感,即对性能要求比较高,要求数据结果在毫秒级内反馈。那么,在选择数据库的时候,我们需要考虑数据库是否能够支持如此高性能的响应。3)扩缩容如果业务要开展新的业务,必须考虑满足一到两年的增长需求,所以扩容和缩容数据库的能力非常重要。如果之前申请的数据量比较大,但是业务发展没有达到预期,那么就需要对数据库进行缩容,所以这方面对数据库的选择也有要求。4)高可用性需要权衡取舍。如果要保证数据的强一致性和性能稳定性,就必须要放弃一些东西。具体需要与业务进行沟通协调,确保实际效果符合业务需求。5)迁移迁移不仅仅是业务代码的改造,还需要考虑数据库的迁移成本,以及从A类数据库迁移到B类数据库时能否支持同构异构。对于业务来说,业务更关心的是迁移带来的业务转型成本。一般来说,业务会更喜欢协议不变、基本操作语法相同的平滑迁移。5、数据库数据库我们需要考虑的重点是:1)事务如果要事务依赖性强,可以使用传统数据库,也可以使用现有的NewSQL,比如TiDB、OceanBase等,如果不考虑事务,数据库的选择会比较多,比如Redis、MongoDB,主要看具体的使用场景和数据库的能力。2)性能每个数据库的性能是不同的。以关系型数据库和非关系型数据库为例,MongoDB和MySQL的性能差异非常大,仍然取决于数据库的容量。3)Scaling,高可用scaling,高可用不需要过多解释,因为高可用是DBA选择数据库的刚性需求。4)这部分的迁移和业务的迁移是有区别的。业务的迁移主要考虑业务转型的成本。数据库的迁移需要考虑以下三点:数据是否一致,数据迁移时是否有增量数据迁移。对业务会有什么影响?如果业务允许直接一刀切,那么解决方案就比较简单;如果业务需求是非破坏性的,那么如何评估解决方案也是需要大家考虑的。5)备份/恢复如果可能存在需要恢复数据的场景,则必须考虑备份/恢复的能力。我们一般比较喜欢做物理备份,因为物理备份恢复起来比较快,但是有些数据库是不具备提供物理备份的能力的,比如MongoDB。我们也不会对Redis做连续备份,因为会导致性能严重下降。6)容灾容灾是我们在第一篇B站数据库架构演进中提到的能力,我们提到了两地三中心,同城多活动。7)稳定性数据库的要求是能够顺畅地对外提供服务,所以稳定性是非常关键的。8)成本我们不可能为了保证性能无限期的往数据库里加机器,因为成本会很高。同时还要考虑开源数据库和商业数据库的选择。在一定程度上,商用数据库的性能优于同规格的开源数据库,但需要考虑维护成本和二次定制能力的成本。9)定制化的商业数据库有时候不允许我们做更多的定制化开发,但是这会给我们的上下游依赖造成一个问题,因为在大多数场景下我们会依赖像MySQL这样的binlog,以及下游的cacheflushing的能力和真正的-大数据的时间数据仓库能力需要依赖binlog向下游走,即CDC能力。那么这方面也是要评估的数据库选择的重要能力。六、策略1)多维度综合考虑数据库架构的选择不从一个维度考虑。每个数据库都有自己的使用场景和特点。因此,数据库架构的选择需要从多个维度综合考虑,包括数据维度、业务的真实需求、DBA团队所能提供的数据库能力、公司对数据库的支持主要由其他团队支持在公司,例如开发和平台。2)满足未来三到五年的需求。数据库架构,比如扩缩容能力,一定要满足未来三五年的需求,而不是频繁的迭代更新,否则对业务不利。3)稳定性主库需要平稳运行,而不是天天宕机,所以数据库架构的选择需要以稳定性为基础。上图右侧是B站数据库团队目前使用的数据库占比,可以看出Redis和MySQL分别占据了第一和第二;MC占第三,因为MC不具备高可用,这个Aspects需要从业务层去设计,比如MC异常后的回源能力;其他数据库的数量相对较少。一般来说,B站的数据库特性主要是Redis和MySQL,其他的数据库主要是根据我们的使用场景来选择和提供的。4.稳定性今天主要想介绍B站万亿级数据库选型和架构设计实践,所以需要考虑数据库如何提供稳定性能力。1、高可用在提供稳定性方面,主要是如何保证数据库的高可用。BRM是我们根据B站业务特点开发的一个MySQL高可用组件,基于这个架构,我们提供了Leader和Follower两个功能节点,可以对集群中的所有节点进行管理和检测。不管注册的是哪个节点,我们都可以把它注册到整个集群。因为里面有一个网关,会把所有的请求转发给master节点,同时分发给剩下的follower节点。Leader和Follower都参与投票决策,避免BRM因为网络抖动问题误判数据库不可用,然后Leader节点根据投票结果判断节点是否宕机。一般来说,我们自研的BRM会有以下六大核心功能:多节点部署:解决MHA单点风险;支持跨机房:跨机房部署,解决网络异常导致误连的风险;支持权重:B站数据库单机房,同城多活,异地多活。如何保证切换到想要的节点。通过为不同的节点设置权重,实现类似MongoDB的基于权重的master选择能力;多节点投票决策:通过多个BRM节点检测同一个实例,只有大多数节点同意才确定该实例不可用;预防:通过多机房、多节点部署,可以防止专线抖动导致主节点误切换,也可以避免跨机房专线异常导致的误判;熔断机构:如果出现机房宕机,我们可以先切掉一部分,检查故障原因,确认没有问题后再解除熔断机构。2、预警保证系统的顺利运行,这也涉及到预警的能力。对于数据库的预警来说,慢查询确实更具有可预测性和可观察性。数据库的CPU和IO也可以作为参考,但是会有一些误判,所以我们的解决方案是针对慢查询的,我们搭建了慢查询预警系统。首先针对DB层的慢查询,我们做了流式采集、上报和实时分析。经过实时分析,可能存在误报,因为如果集群在正常情况下,每天固定时间会出现,比如100条慢查询,那么这个时候是否应该上报,其实,这本身就是一个业务在某个时间点的具体行为,不会影响整体的行为,所以需要屏蔽。针对这方面,我们引入多元线性回归,通过多元线性回归,实现对零星抖动的过滤,不同业务层级的链式倍数,以及持续增长(没有达到阈值倍数,但持续增长或存在)慢查询预警,基于规则引擎实现自定义处理。3.Proxy通过大量使用Proxy,我们可以实现对某些数据库、某些服务、某些类型的SQL指纹的拦截、限流、熔断,防止某些异常流量导致数据崩溃的场景,也可以进行比较分离在放松的状态下阅读和写作。我们还可以做多机房路由,将移动架构下的数据流量转发到主库,同时能够动态发现拓扑变化,增删从库和节点变化更容易发现。同时,我们可以开发更精细化的Sidecar模型,从而减少业务技术和能力。通过Sidecar模型使用Proxy,可以满足大家在大量场景下的能力。4、多活多活就是保证一个机房宕机后,我们可以有另一个机房来支持这方面。前面提到的Proxy、BRM、DTS都是为了满足多活的需求。通过多次激活,我们可以保证最大容量的容灾冗余,同时将对用户的影响降到最低。当一个机房宕机时,可能只有一部分用户受到影响。快速将所有用户转移到另一个机房,可以为用户提供稳定的使用体验。5.效率最后是自动化效率的问题。无论是原生的分布式数据库如TiDB,还是我们自研的基于Proxy和业务层的分布式数据库能力,同时像Redis这样的大规模集群,我们现在往往超过了Redis。本身的上限,由于八卦通信机制,如果节点数量过多,节点之间的心跳请求会占用带宽,那么我们的自动化如何提供效率呢?以下是自动化运维的演进方向:我们还处在自动化运维的阶段。自动化平台能力的核心有四个方面,即自助资源管理研发、运维运营、风险管理。自动化运维平台1.资源管理资源管理简单理解就是资源是如何分配的。有多个维度:主机管理;Operator管理:无论是否安装k8s,都必须提供Operator管理能力;资源池管理:涉及到如何提高机器使用率的问题;资源报表:涉及计费能力,计费可以明确告诉业务哪里使用不合理,哪些成本可以节省,哪些结构可以调整。2.研发自助在日常情况下,研发中要做的事情很多,比如查询、导入、添加字段、健康检查等。资源申请指的是我们做了一些比较简单的常规业务,他们可以根据我们前面说的策略匹配后选择数据库。DBA审计的时候,我们会评估他们写的内容是否合理,确保不会出现架构设计失败导致的重构问题。3、运维操作集群管理、实例管理、数据管理是一些比较日常的运维操作,由平台整体支撑,大部分可以自动解决,无需人工管理。4.风险管理风险管理包括监控告警、健康报告、访问信息脱敏和存储信息脱敏。B站涉及电商和支付,需要对大量数据和用户信息进行脱敏处理,通过数据扫描确保数据合规。以上是我们自动化平台的能力。
