下面开始我们今天的主要内容, 今天主要是通过什么, 为什么, 怎么做, 这条思路跟大家呈现 MySQL 的高可用.
首先介绍一下什么是高可用? 在我看来就是业务在高质量的情况下, 对用户提供服务的可运行的总时长. 其实我们从事 MySQL 相关的工作, 大家对 9 这个数字比较敏感, 大家选择云厂商云产品的时候, 首先会看它的数据库有几个 9. 目前腾讯云 MySQL 可以做到 99.95, 全年在 25 分钟的样子.
据我了解, 高可用最高是能做到 3 个 9,1 个 6, 做到 4 个 9 很困难, 做到 5 个 9 就是极限了.
为什么我们要做高可用? 因为我们不可控的因素太多了, 比如说, 挖挖机, 我记得基本上每隔一年都会出现这种类似的事件, 让我记忆犹新的是 2015 年杭州萧山的某个主干网被刮断, 导致阿里的部分服务不可用. 另外, 还有类似的一些停电, 或者一些天灾等等. 值得一提是, 运维人员的一些操作失误案例, rm 整个目录或者 drop 表, 民间有说法叫从删库到跑路. 不可控制的因素很多, 你的数据, 用户是你的, 如果不可控的话, 你的业务上不去.
一般来说, 有两个指标会被当作衡量的标准, 第一是 RPO, 第二是 RTO.RPO 从故障开始到业务恢复所丢失的数据量, RTO 就是从故障开始到业务恢复所耗费的时长, 两者都是越短越好.
我们怎么做呢? 一般来说业界有三种方式, 左边是基于单机存储方式, 这种方式在游戏场景比较多, 大家上层是用单独的计算机节点, 下层用三副本保证数据的可靠性. 在计算节点发生故障以后可以快速迁移到另一个计算节点, 当然我们腾讯云的 MySQL 已经推出了这种模式, 相对来说非常廉价, 是基础版, 大家在官网都可以购买到这种模式. 第二种是基于共享存储方式, 也叫 share disk 模式, 这种比较典型的是 oracle 的 RAC 架构. 底层基于共享存储的方式, 上层采用多个计算节点, 某个计算节点故障可立即从 ip 列表中提出, 不影响用户访问. 第三种就是基于数据复制模式, 也就 share nothing 模式, 通过数据传输, 复制协议达到两台主机数据一致性, 也是本次讲解的重点. 另外, 除了存储节点的高可用, 其整个链路也需要高可用, 比如, 咱们的 IDC 机房, 交换机, 以及主机服务器等.
下面我们介绍下基础设施的高可用. 大家经常听到几个术语, 第一是同城双活, 第二是两地三中心, 两地三中心对于金融相关的场景是个强需求, 其实说白了就是说我们在同城两个节点相差十公里之外有两个数据中心, 在 100 公里异地以外有另个灾备中心, 保证了机房的高可用. 另外包括网络, 主机, 其实架构上是这样的, 至少说你的交换机网络都有备份, 一个倒了以后, 另一个需要替换上去.
下面进入我们的重点, 基于数据复制的高可用, 首先介绍一下备份, 备份确实是非常重要的, 而且备份是一个实在没办法最后的一个保障, 所以说建议大家不管是在云上用的业务, 还是自己的 IDC 尽量做好备份.
MySQL 备份基本上是这两种: 逻辑备份, 物理备份. 逻辑备份通常使用官方的 MySQLDump 与第三方工具 MyDumper,MyDumper 优势在于多线程备份, 速率快. 物理备份使用 Percona 的 xtrabackup, 可以不落盘, 通过基于流式并发与压缩, 生产出成功率较高, 速率较快并且暂用存储空间较低的备份. 最后一种就是快照, 我们腾讯云的基础版的备份就是通过快照生成的.
那基于数据复制方式, 一般是主从两个节点, 数据怎么保证一致性呢? 其实是通过复制协议进行数据传输, 通过 Switch 切换保证故障以后服务能够尽快恢复. 右边的图基本和腾讯云 MySQL 差不多的架构, 我们采用了一主一从的方式, 从节点只负责故障的转移, 当主节点挂了以后, 通过自动故障探测与自动切换, 从而做到业务尽快恢复. 另外针对读写分离, 腾讯云 MySQL 现可以支持一主挂 5 个只读节点.
下面介绍一下复制, 在介绍复制之前有必要介绍一个重要的概念: binlog,binlog 是二进制文件, 主要记录用户对数据库更新的 sql 信息, binlog 是什么样子呢? 它是在磁盘上是这个样子, 使用 show binlog events 后它是这样的, 里面会记录一些元信息, 比如位点, 事件等等, 我们通过 MySQL 官方解析工具 mysqlbinlog 解析后是这样的, 里面 sql 语句是使用 base64 编码的, 解码后是这样的, 可以看到这里是条插入语句. 那什么时候写 binlog 呢? 大家来看这个图, 我们知道事务提交有两个阶段: prepare 与 commit, 请问是哪个阶段写 binlog 呢? binlog 其实是在 prepare 后 commit 前写入的, 同时写事务过程中, 会产生 redolog 与 undolog, 那这两者有什么区别呢? 我们知道 MySQL 是多引擎的关系型数据库, binlog 是 MySQL Server 层的日志, 而 redolog 是 MySQL 引擎 InnoDB 层的日志; 另外一个不同是两者写入时机不同, redolog 是 prepare 阶段每执行 sql 语句就写 redo 了, 而 binlog 是在 prepare 完 commit 前写的. 那 MySQL 在主从架构下怎么保证数据一致性呢? 众所众知, MySQL 为了保证性能, 数据是先写内存后落盘的. 当你数据库运行的时候, 发生了宕机, 机器再次恢复的时候可能是部分数据落盘了, 部分未落盘. 这时, MySQL 是找到 binlog 最新同步的位点或 GTID, 来确定 redolog 或者 undolog 中哪些实例需要回滚, 哪些事务需要重做. 另外, 在写日志的时候, 比如 redolog 或 binlog,MySQL 为保证高性能, 也是先写内存后落盘的, 所以日志的落盘策略也会影响数据的一致性. 为保证数据的一致性, 建议大家将涉及日志的参数配置为 "双 1", 也就是如图上所示.
下面我们来看看复制整个流程, 其实很简单, Master 通过 dump 线程将 binlog 落盘, 在 Slave 上会有两个线程, 分别是 IO 线程和 SQL 线程. IO 线程接受来自 Master 的 binlog 并落地形成 Relaylog,SQL 线程并行读取 relaylog 中的 sql 信息, 执行回放动作. 一般来说, 复制分三种: 异步复制, 半同步, 强同步. 这三者的区别在于何时将 sql 执行的结果反馈给客户端. 异步复制, Master 不管 Slave,Master 执行 sql 完毕后立即返回给客户端, 这种方式性能最好, 但是存在数据不一致的可能; 强同步呢, Master 完全关心 Slave, 等待 Slave 将 relaylog 回放后才返回给客户端, 这种方式能够保证数据强一致, 但是其性能有一定损耗; 半同步则是 Master 部分关心 Slave, 认为只要 binlog 传输到 Slave 侧, 落为 relaylog 后, 即可以返回给客户端了. 半同步是一种兼顾的实现, 一方面保证数据一致性, 另一方面兼顾了数据库的性能.
在复制过程中, 我们经常遇到延迟问题, 大家看图中所示, 复制经历三个阶段: Dump 线程落盘 binlog,IO 线程落盘 relaylog, 以及 SQL 线程回放, 请问三个步骤里面哪个步骤是一个瓶颈? 是 SQL 线程, 是因为 SQL 线程在回放日志过程中是串行执行 sql 的, 而 Master 对外是并行提供服务的. 所以这里瓶颈是 SQL 线程. 大家可用通过开启并行复制来解决延迟问题, MySQL5.6 基于库级别并行复制; MySQL 5.7 基于逻辑时钟并行复制, 也就是表级别的并行; 而 MySQL8.0 则是行级别的并行复制, 粒度更细, 复制效率更高.
刚才是说在协议级别进行复制, 其实还有一种方式是块级别的数据复制, 其不关心上层是什么, 只需要保证在磁盘层面数据复制即可. 当然这种方式的话, 应用的比较少. 说完复制后, 咱们来说一下切换, 其实 MySQL 官方之前并没有提供故障自动发现与转移的能力, 基本上靠第三方工具来实现.
第一种是 Keepalived,Master 和 Slave 相互探测对方, 时刻询问对方存活状态. 当出现网络抖动或者网络出现问题的时候, 可能会出现脑裂问题, 变成了两主, 数据就写错乱了. 第二种就是 MMM 的方式, M1M2 互为主备, 再加上一个 Slave 节点做冗余. 从图上看, 虽然是双主, 但该模式下同一时间点下只能有一个节点可以写, 当发现这个主写节点出现故障, 会将 vip 切换到另一个主上比. 整体看, 这种方式比较老, 问题比较多. 第三种是 MHA, 其应用广泛, 这种方式是由复制组与管理节点组成, 每个复制组里是由至少三个数据节点组成, 数据节点上部署监控 agent, 定时上报到管理节点, 当主节点出现问题时, 由管理节点裁决是否切换到从节点. 腾讯云是自己实现了一套故障检测, 结构如右边的图, 由高可用保证的 Monitor 节点来进行故障检测与切换. 另外, 目前我们还在做 MySQL 高可用的重构, 届时能够做到故障检测恢复 30 秒钟以内, 大大提高了高可用.
下面我们来说下集群的高可用架构, 比较有名的就是 PXC,MGC,MGR,PXC 和 MGC 是结构比较类似, MGR 是官方提供的, 具有故障转移的高可用架构. 大体的层级是这样的, MGR 以插件的形式存在的, MGR 主要是把复制协议进行改造, 因为 MGR 支持多活, 所以这里另一个重点是冲突检测, 若多个节点同时写同一主键时, 依照哪个为准呢? MGR 是采用基于 Paxos 协议实现的冲突检测. 下面, 我们大致看下结构, MGR 是支持多个节点写, 即多活, 支持某个节点挂了后自动剔除, 恢复后自动加入集群. 这张图是介绍一下 MGR 数据流逻辑, 图上有三个节点构成最小 MGR 集群. 假设 DB1 有一次写提交, 在 Prepare 阶段, MGR 插件会生成一个叫 WriteSet 的集合, 并将其广播给其他节点. 这个 WriteSet 集合包含此次提交的 binlog 和更新的唯一键, 此唯一键由 db 名, 表名和主键组成. 这里可以看出 MGR 有个限制, 表中必须要有主键, 要不无法进行冲突检测. 我们再说回来当节点收到这一信息时, 会进行比对, 每个节点都有一个缓存, 保存当前同步情况, 即唯一键对应的 GTID SET. 通过比对后将结果返回给 DB1, 只要多于半数的节点返回说 OK, 可以提交, 那 DB1 接下来就会执行 binlog 的落盘操作, 然后返回 OK 到客户端. 其他节点则执行写 Relaylog 的动作, 接下来进行回放的动作. 若多数节点返回冲突, DB1 则执行回滚操作, 其他节点会 drop 掉复制过来的 binlog.
其实 PXC 和 MGC 思路是差不多, 应该说是 MGR 借鉴的, 因为 PXC 和 MGC 是比较早就出来的, 这里大同小异, 主节点将 WriteSet 写集合广播出去, 广播完后进行验证与裁决.
最后我们说一下 NewSQL 高可用架构, 首先对 AWS 表示致敬, 孵化出非常优秀的 NewSQL 产品 - Aurora. 那 Aurora 是怎么产生出来的呢? 这与 AWS 数据库架构有关. 我们来看看这个图, AWS 数据库是架构在虚拟机与云盘上的, 我们都知道 MySQL 的 log 比较多, 所以很多 IO 是通过耗时较高的网络来完成的, 因此 AWS 这种架构网络 IO 是它的瓶颈, 性能也跑不上去. 在此基础上, 我们来认识下 Aurora.
Aurora 是计算与存储分离的架构, 典型的 share disk 的结构. 底层存储采用 6 副本, 部署在三个不同的 AZ 上, 可以保证一个 AZ 挂了, 或者至多两个 AZ 的一个副本丢失的情况下数据不丢失, 业务可以正常对外服务. Aurora 的理念是 "日志即数据库", 其把 MySQL 存储层进行了彻底的改造, 摒弃了很多 LOG, 只留下了 Redolog, 具备将 redolog 转换到 Innodb page 的能力. 通过这种方式, Aurora 宣称其减少至少 85% 比例的 IO. 另外其把备份和回档下沉到存储节点, 使得备份恢复更快并得到保障. Aurora 整体感觉相对比较接地气, 成本相对比较低.
另一个就是阿里云的 Polar, 理念和 AWS 不同, 阿里云觉得未来网络不是问题, 未来网络可以接近总线的质量, 所以是架构在 RDMA 网络的机房里, 日志方面大动作较少, 保证后续 MySQL 社区新特性可快速迭代近来. Polardb 也是 share disk 的架构, 其存储节点是通 ParallelRaft 协议保证数据的完备性. 可见这也是个伟大的架构, 但是相对来说成本比较高一些.
我们腾讯云自己的 NewSQL 在研发中, 只是目前还没有正式上线, 我们的名字叫 CynosDB, 相比来说我们的理念是兼顾两者, 未来在高网络新硬件的基础实施下, 会发挥更大的性能, 更稳健的服务和更高的可用性. 请大家拭目以待.
Q & A
Q: 我想问一下在腾讯游戏的高并发行业里面, 我们主要采用哪种架构?
A: 腾讯内部有很多自研项目, 但基本上我们是基于数据复制的方式. 内部有 phxsql 等分布式集群架构.
Q: 如何在高并发情况下, 保证总库的定延时呢?
A: 可以开启并行复制, 业务做分库分表, 分散到多个实例上.
Q: 比如说像游戏类的, 在游戏高峰期的话会有很多人同时在线, 这种情况下怎么在后台看数据呢?
A: 可以对比较热的数据进行分层, 前一层可以通过 KV 方式缓存, 比如 Redis, 来提高热数据的读取, 后一层使用 MySQL, 定期将数据同步落盘.
Q: 这种情况下怎么保证数据库是一致的呢?
A: 写数据可以不经过 KV 缓存, 直接写 MySQL 数据库, 读取时, 缓存内没有数据, 需要从 DB 中捞取出来. 另外, KV 缓存也有落地能力, 非关键数据也可以不使用 MySQL 落地.
来源: https://juejin.im/post/5bed397e6fb9a04a072fefd8