如何避免这10类常见故障?B站数据库架构设计做了这5步……
十年以上数据库运维经验,曾在多家大型互联网公司任职,主导和参与了多家数据库私有云建设、数据库多活、数据库架构从商业数据库到开源数据库的迁移。目前主要专注于B站数据库多活、数据库服务治理等。
温馨提醒:本文约4900字,预计花费8分钟阅读。
回复 “交流” 进入读者交流群;
一、什么是数据库故障
1、MySQL
- 硬件故障:硬件故障是生产环境中无法避免问题。常见的硬件故障包含了CPU、内存、磁盘、主板等。
- 系统故障:系统故障包含操作系统bug和数据库bug。
- 网络故障:网络故障包含网络设备故障和专线故障。
- 其他:如资源分配不合理导致的OOM。
- 主库执行了一个非常大的数据变更事务;
- 主库变更频率过快, 导致从库跟不上;
- 使用原生的表结构变更语句进行大表的DDL ;
- 从库配置参数较低、硬件性能较差;
- 从库负载过高,导致性能变差 ;
- 从库MDL 锁 ;
- 主从之间网络问题 ;
- 主从复制Bug (多线程复制 bug 最为常见)。
- 上游从库延迟
- Kafka性能瓶颈
- canal类组件性能瓶颈
- 未开启双一参数,在宕机场景下丢失;
- 未使用半同步复制,在宕机场景下丢失;
- 使用三方工具进行DDL的某些特殊场景下可能会丢, 比如pt-osc、gh-ost;
- 误操作、误删除数据或者蓄意删除,也就是常说的运维人员删库跑路。
- 新业务上线 SQL 效率较差或者没有合适的索引;
- 业务场景发生改变, 边缘场景被触发;
- 数据倾斜, 导致未走合适的索引;
- Innodb buffer pool 命中率低, 触发大量物理读;
- 优化器 bug, 导致未走最优索引宿主机磁盘异常。
- 突发流量;
- 上游缓存失效;
- 大型活动中流量超过预期。
2、缓存
- 自身业务代码或者数据出现问题(例如:set 和 get 的key不一致)。
- 一些恶意攻击、爬虫等造成大量空命中。
- 当前key是一个热点key(例如一个秒杀活动),并发量非常大。
- 重建缓存不能在短时间完成,可能是一个复杂计算,例如复杂的SQL、多次IO、多个依赖等。在缓存失效的瞬间,有大量线程来重建缓存,造成后端负载加大,甚至可能会让应用崩溃。
- 数据库瞬间被大量的流量打死
- 该服务接口访问时间过长,导致耗尽了线程资源,从而引起整个系统的崩溃。
三、B站在数据库故障治理方面做了啥?
上面讲述了常见的数据库故障,下面我们来说说如何从数据库架构设计上来规避故障,这里会结合B站的一些实践来跟大家分享。
1、高可用
2、扩容
扩容一般分为垂直扩容和水平扩容两种,对于传统MySQL来讲,垂直扩容的收益比通常不高, 而基于其本身状态的水平扩容又很难做到快速,所以如何做应急扩容一直是业内比较头疼的问题。 这时候引入云原生数据库是一个不错的选择,业内的云原生数据库大都进行了存储计算分离改造,对于计算层可以通过 k8s 的HPA能力进行快速扩容,而存储层则依赖于底层共享存储或者分布式存储也有不同的扩容方式。 在 B 站我们大量引入了 TiDB, 在一些适合弹性伸缩的场景下有不错的收益。
- 垂直扩容:增大 buffer pool、 cpu 限制等配额,需要平台侧完善 Quota 管理和变更能力。
-
水平扩容:对于MySQL来讲, 应急快速水平扩容只能用于读负载,我们会有一些异地机房的实例,一般情况下用作灾备以及离线用途, 在极端场景下可以通过平台操作一键接入读负载池;对于 TiDB 来讲,我们把计算节点放入 k8s,通过k8s 的HPA能力进行水平弹性伸缩。
3、多活建设
-
异地灾备: 当主机房的实例全部不可用时进行切换,一般是由高可用组件触发切换,高可用组件本身应该是一个分布式跨机房的架构。 比如在B 站, 我们的高可用组件是三机房投票决策的,同时应当注意避免跨机房专线异常造成的误判以及误切换熔断策略。
-
同城双活: 主库在同城某一个 zone,从库则分布在同城的不同 zone,每个 zone 的读请求实现单元内闭环,写请求则跨 zone 访问对应的主库,需要在数据库proxy 或者sdk层实现请求路由的自动感知和判断。
-
异地多活: 依赖业务单元化改造,读写请求全部单元内闭环,要求整体架构具备单元流量调度以及异常流量矫正的能力。属于同一个 global cluster 的集群间通过 DTS 进行双向数据同步,DTS 层需要解决数据回环问题以及冲突检测机制,对于数据冲突的场景提供不同的策略,比如覆盖策略、暂停同步策略等。对于冲突数据也可以考虑写入消息队列,便于业务侧矫正处理,同时应具备全局发号器主键填充能力,避免双向同步的主键冲突。
-
两地三中心: 可以简单理解为同城多活加上异地灾备架构,其中灾备机房应当根据灾难承受程度和数据保护程度来权衡地理距离。
4、Proxy数据库代理
数据库代理(又名proxy)是位于数据库服务和应用服务之间的网络代理服务,用于代理应用服务访问数据库时的所有请求。 数据库代理一般包含以下核心功能:
- 读写分离
- 拦截
- 限流
- 熔断
5、慢查询预警
6、一个具体的小案例分享
下面分享一个针对复制bug的处理过程。
-
问题背景:
-
核心处理步骤:
-
后续优化:
- 增加心跳表以规避复制bug导致的常规复制监控不可用。
- 基于高可用、心跳表、数据库负载(QPS、CPU、网络等)等信息,实现在从库异常情况下将流量切换到主库和数据订阅的数据源切换到主库。
-
说明:
四、数据库故障演练应该怎么做?
故障演练是一种遵循混沌工程实验原理的解决方案,提供丰富的故障场景模拟(如宕机、CPU负载过高、网络延迟、丢包、磁盘写满、I/O飙高、OOM)等,能够帮助分布式系统提升容错性和可恢复性。 故障演练需要建立一套标准的演练流程,包含准备阶段、执行阶段、检查阶段和恢复阶段。
万物流变,无物常驻。博尔赫斯 · 赫拉克利说过:没有人能两次踏进同一条河流,故障也是类似的,每次故障发生的时间、地点、原因、影响范围、处理的人都不同。若从这个角度来看故障治理,它是在解决过去某一个时刻的问题,那为什么还要做故障治理呢? 简单来说,故障治理的目的就是通过技术手段提前规避类似的故障问题,不让系统在同一个地方跌倒两次,而且大量的实践也证实了这些做法的有效性。