今年3月GitHub在一周内出现了多次服务不可用的情况,每起事件持续时长在 2-5 小时,据有媒体统计,GitHub在一周中多次中断影响的开发者数量高达 7300 万。事后GitHub 高级工程副总裁 Keith Ballinger 发文表示,「我知道这会影响许多客户的生产力,我们也非常重视这一点。过去几周发生的宕机事件根本原因是我们的‘MySQL1’集群中的资源争夺,在负载高峰期,影响了 GitHub 大量服务和功能。」 数据库故障对企业系统服务的影响可见一斑,今天我将结合B站自身的实战经验来跟大家说说数据库故障治理相关的心得:
作者介绍
B站DBA Leader-王志广十年以上数据库运维经验,曾在多家大型互联网公司任职,主导和参与了多家数据库私有云建设、数据库多活、数据库架构从商业数据库到开源数据库的迁移。目前主要专注于B站数据库多活、数据库服务治理等。
温馨提醒:本文约4900字,预计花费8分钟阅读。
回复 “交流” 进入读者交流群;
一、什么是数据库故障
数据库故障顾名思义就是数据库相关的故障,它在学术上没有明确的定义,故各公司一般以对应用的影响范围来量化定义数据库故障。如下图所示:二、常见的数据库故障有哪些?
故障治理就得对症下药,所以治理的第一步就是明确常见的数据库故障有哪些,今天就MySQL、缓存两个大方向来跟大家一起梳理一下。1、MySQL
作为在互联网公司广泛使用的传统意义上的数据库,MySQL的故障可以分为以下几种: 1.1 实例不可用 数据库作为一种特殊的应用,在其生命周期内无法保证100%可用。数据库实例不可用的原因一般有:
- 硬件故障:硬件故障是生产环境中无法避免问题。常见的硬件故障包含了CPU、内存、磁盘、主板等。
- 系统故障:系统故障包含操作系统bug和数据库bug。
- 网络故障:网络故障包含网络设备故障和专线故障。
- 其他:如资源分配不合理导致的OOM。
数据延迟包含数据库的主从延迟和依赖binlog的数据订阅服务的延迟。 1.2.1 主从复制延迟 主从复制延迟是线上经常出现的问题之一,对于读写分离的业务来说,从库延迟的影响可能是致命的。导致主从延迟的原因一般有:
- 主库执行了一个非常大的数据变更事务;
- 主库变更频率过快, 导致从库跟不上;
- 使用原生的表结构变更语句进行大表的DDL ;
- 从库配置参数较低、硬件性能较差;
- 从库负载过高,导致性能变差 ;
- 从库MDL 锁 ;
- 主从之间网络问题 ;
- 主从复制Bug (多线程复制 bug 最为常见)。
- 上游从库延迟
- Kafka性能瓶颈
- canal类组件性能瓶颈
- 未开启双一参数,在宕机场景下丢失;
- 未使用半同步复制,在宕机场景下丢失;
- 使用三方工具进行DDL的某些特殊场景下可能会丢, 比如pt-osc、gh-ost;
- 误操作、误删除数据或者蓄意删除,也就是常说的运维人员删库跑路。
- 新业务上线 SQL 效率较差或者没有合适的索引;
- 业务场景发生改变, 边缘场景被触发;
- 数据倾斜, 导致未走合适的索引;
- Innodb buffer pool 命中率低, 触发大量物理读;
- 优化器 bug, 导致未走最优索引宿主机磁盘异常。
- 突发流量;
- 上游缓存失效;
- 大型活动中流量超过预期。
2、缓存
常见的缓存故障包含以下几种:缓存穿透、缓存击穿、缓存雪崩,下面展开详细说说。 2.1 缓存穿透 缓存穿透是指查询一个根本不存在的数据,缓存层和持久层都不会命中。在日常工作中出于容错的考虑,如果从持久层查不到数据则不写入缓存层,缓存穿透将导致不存在的数据每次请求都要到持久层去查询,失去了缓存保护后端持久的意义。 造成缓存穿透的原因有两个。
- 自身业务代码或者数据出现问题(例如:set 和 get 的key不一致)。
- 一些恶意攻击、爬虫等造成大量空命中。
- 当前key是一个热点key(例如一个秒杀活动),并发量非常大。
- 重建缓存不能在短时间完成,可能是一个复杂计算,例如复杂的SQL、多次IO、多个依赖等。在缓存失效的瞬间,有大量线程来重建缓存,造成后端负载加大,甚至可能会让应用崩溃。
- 数据库瞬间被大量的流量打死
- 该服务接口访问时间过长,导致耗尽了线程资源,从而引起整个系统的崩溃。
三、B站在数据库故障治理方面做了啥?
上面讲述了常见的数据库故障,下面我们来说说如何从数据库架构设计上来规避故障,这里会结合B站的一些实践来跟大家分享。
1、高可用
高可用(High Availability)是系统架构设计中必须考虑的因素之一,它通常是指,通过设计减少系统不能提供服务的时间。所以在做数据架构设计时一定要考虑HA 能力,无论是选用集群模式(MGR、PXC 等),或是使用传统主从复制加上其他高可用组件(Orchestrator、MySQL Replication Manager、MHA等),都可以帮我们在实例不可用时把损失最小化。
2、扩容
- 垂直扩容:增大 buffer pool、 cpu 限制等配额,需要平台侧完善 Quota 管理和变更能力。
- 水平扩容:对于MySQL来讲, 应急快速水平扩容只能用于读负载,我们会有一些异地机房的实例,一般情况下用作灾备以及离线用途, 在极端场景下可以通过平台操作一键接入读负载池;对于 TiDB 来讲,我们把计算节点放入 k8s,通过k8s 的HPA能力进行水平弹性伸缩。
3、多活建设
- 异地灾备: 当主机房的实例全部不可用时进行切换,一般是由高可用组件触发切换,高可用组件本身应该是一个分布式跨机房的架构。 比如在B 站, 我们的高可用组件是三机房投票决策的,同时应当注意避免跨机房专线异常造成的误判以及误切换熔断策略。
- 同城双活: 主库在同城某一个 zone,从库则分布在同城的不同 zone,每个 zone 的读请求实现单元内闭环,写请求则跨 zone 访问对应的主库,需要在数据库proxy 或者sdk层实现请求路由的自动感知和判断。
- 异地多活: 依赖业务单元化改造,读写请求全部单元内闭环,要求整体架构具备单元流量调度以及异常流量矫正的能力。属于同一个 global cluster 的集群间通过 DTS 进行双向数据同步,DTS 层需要解决数据回环问题以及冲突检测机制,对于数据冲突的场景提供不同的策略,比如覆盖策略、暂停同步策略等。对于冲突数据也可以考虑写入消息队列,便于业务侧矫正处理,同时应具备全局发号器主键填充能力,避免双向同步的主键冲突。
- 两地三中心: 可以简单理解为同城多活加上异地灾备架构,其中灾备机房应当根据灾难承受程度和数据保护程度来权衡地理距离。
4、Proxy数据库代理
- 读写分离
- 拦截
- 限流
- 熔断
5、慢查询预警
6、一个具体的小案例分享
下面分享一个针对复制bug的处理过程。
问题背景:
业务研发收到多起创建预约的问题反馈,如:创建预约后拉取不到预约详情
查询从库未读取到相关数据
核心处理步骤:
- 后续优化:
- 增加心跳表以规避复制bug导致的常规复制监控不可用。
- 基于高可用、心跳表、数据库负载(QPS、CPU、网络等)等信息,实现在从库异常情况下将流量切换到主库和数据订阅的数据源切换到主库。
- 说明:
四、数据库故障演练应该怎么做?
五、总结与展望
回复【0922】订阅故障演练内容
回复【交流】进入读者交流群
回复【7161】获取「B站多活容灾建设」资料
声明:本文由公众号「TakinTalks稳定性社区」联合社区专家共同原创撰写,如需转载,请后台回复“转载”获得授权。 更多故障治理内容「好文推荐」 ???? 美图SRE:一次线上大事故,我悟出了故障治理的3步9招 ????监控告警怎么搭建比较合理? ????故障复盘后的告警如何加出效果? ????阿里云弹性计算SRE实践:亿级调用量下的预警治理 ????点击【阅读原文】直达TakinTalks稳定性社区,获取更多实战资料!