写点什么

PhxSQL 设计与实现(详细版)

用户头像
OpenIM
关注
发布于: 17 小时前

之前发表过一篇 ppt 版的“PhxSQL设计与实现”,本文是在 ppt 的基础上,加上解说的文字内容,形成一篇详细版。

本文详细描述了 PhxSQL 的设计与实现。从 MySQL 的容灾缺陷开始讲起,接着阐述实现高可用强一致的思路,然后具体分析每个实现环节要注意的要点和解决方案,最后展示了 PhxSQL 在容灾和性能上的成果。


设计背景


互联网应用中账号和金融类关键系统要求和强调强一致性及高可用性。当面临机器损坏、网络分区、主备手工或者自动切换时,传统的 MySQL 主备难以保证强一致性和高可用性。PhxSQL 将 MySQL 集群构建在一致性完善的 Paxos 协议基础上,保证了集群内 MySQL 机器之间数据的强一致性和整个集群的高可用性。


原生 MySQL 的容灾缺陷


MySQL 容灾方案


MySQL 有两种常见的复制方案,异步复制和半同步复制。


  1. 异步复制方案

Master 对数据进行 commit 操作后再将数据异步复制到 Slave。 但数据无法保证成功复制,也就无法保证 MySQL 主备间的数据一致性,如图 1 所示。


图 1 MySQL 异步复制流程


  1. 半同步复制方案

Master 对数据进行 commit 操作前将数据复制到 Slave,确认复制成功后再对数据进行 commit 操作。

绝大多数情况下,半同步复制能保证 MySQL 主备间的数据一致性,如图 2 所示。

图 2 MySQL 半同步复制流程


MySQL 重启流程

半同步方案中的“半”是指 Master 在等待 Slave 的 ACK 失败时将退化成异步复制。同时,MySQL 在重启时也不会执行半同步复制。


如图 3 中的 id(Gtid)=101 数据是 Master 机器中新写入到 Binlog File 的 Binlog 数据。但 Master 在复制数据到 Slave 的过程中 MySQL 宕机导致复制失败。MySQL 重启时,数据(id=101)会被直接进行 commit 操作,随后再将数据异步复制到 Slave。(下文将已经写入到 Binlog File 但未进行 commit 操作的数据(id=101)称为 Pending Binlog。)


图 3 MySQL 重启时直接提交 Pending Binlog


该情况下 MySQL 容易出现 Master-Slave 之间数据不一致的情况,官方也描述了该问题。


  1. http://bugs.mysql.com/bug.php?id=80395

  2. https://mariadb.atlassian.net/browse/MDEV-162


MySQL 重启缺陷

下面将解释 MySQL 在重启时不执行半同步会产生数据不一致的原因。


当对上述例子中的 Pending Binlog(id=101)进行复制时 Master 宕机导致复制失败,随后 Slave1 切换成新 Master 并开始提供服务(写入 id=201 的数据)。此后,当旧 Master 重启时,Pending Binlog(id=101)不会被重新进行复制而直接进行 commit 操作,从而导致旧 Master 比新 Master 多了一条数据,旧 Master 无法成为新 Master 的 Slave,需要人工处理掉这条数据之后,才能让旧 Master 作为 Slave 提供服务,如图 4 所示。

图 4 MySQL 重启缺陷导致主备数据不一致


上述 case 只对旧 Master 的数据造成影响,不会使得 MySQL Client 读取到错误数据。但当 Master 连续出现两次宕机后产生 Master 切换,两次宕机间隔较短使得 Pending Binlog 未能及时复制到 Slave,且期间有查询请求时(Master 宕机→Master 重启→查询数据→Master 宕机→Master 切换),MySQL Client 会产生如图 5 所示的幻读(两次读到的结果不一致)。


图 5 MySQL 重启缺陷导致 Client 产生幻读


MySQL Client 分裂

当 Master 出现故障且产生 Master 切换时,由于原生 MySQL 缺乏调用端的通知/重定向机制,使得不同的 Client 可能访问不同的 Master,导致数据的错误写入和读取,如图 6 所示。

图 6 MySQL 进行 Master 导致 Client 端分裂


MySQL 缺乏自动选主机制

由于半同步复制不需要等待所有 Slave 的 ACK,因此当 Master 出现故障时,需要选有最新 Binlog 的 Slave 为新的 Master;而 MySQL 并没有内置这个选主机制,如图 7 所示。

图 7 MySQL 缺少自动选主机制


MySQL 的容灾缺陷总结

MySQL 在容灾方面存在的问题:


  1. Master 切换时主备数据不能保证一致:Master 重启并切换可能导致 MySQL 主备间数据不一致。Master 重启并切换可能导致 MySQL Client 产生幻读。

  2. 原生 MySQL 缺乏高可用机制:Master 切换导致调用端分裂。缺乏自动选主机制。


对于原生 MySQL,在高可用和强一致两个特性中,只能二选一:


  1. 要求 MySQL 主备间的数据强一致,不做主备自动切换。

  2. 借助 MHA 实现高可用,容忍 MySQL 主备间的数据不一致。


因此 MySQL 在容灾上无法同时满足数据强一致和服务高可用两个特性。


PhxSQL 设计思路


可靠日志存储

实现一个以可靠日志存储为中心的架构来解决 MySQL 数据复制时产生的数据不一致问题。

Master 将 Binlog 发送到 BinlogSvr 集群(可靠日志存储),Slave 从 BinlogSvr 集群获取 Binlog 数据完成数据复制。

Master 在重启时,根据 BinlogSvr 集群的数据判断 Pending Binlog 是否已经被复制。如果未被复制则从 Binlog File 中删除。


利用 BinlogSvr 集群(可靠日志存储),使得 Master(重启时检查本地 Binlog 是否和 BinlogSvr 集群的数据一致)和 Slave(从 BinlogSvr 集群中获取 Binlog)的数据保持一致,从而保证了整个集群中的 MySQL 主备间数据的一致性,如图 8 所示。

图 8 实现一个可靠日志存储保证各 MySQL 的数据一致


请求透传


在 Master 进行切换时,切换操作可能会导致部分 MySQL Client 仍然访问旧 Master 并读到旧数据。

最直观的方法是修改 MySQL Client API,在每一次进行查询时,先确认当前 Master 的位置。但此方法有以下缺点:


  1. 需要维护一个 MySQL Client API 的私有版本,维护成本高。

  2. 所有的调用端需要集成这个私有的 MySQL Client API,操作成本很高。


为了避免修改 MySQL Client API,可通过增加 Proxy 进行请求透传来解决上述问题。在每一个 MySQL 结点上增加一个 Proxy,MySQL Client 的请求不再直接访问 MySQL 而直接访问 Proxy。Proxy 根据 Master 的位置,将访问 Slave 机器的请求透传到 Master 机器,再进行 MySQL 操作。


通过增加 Proxy 进行请求透传,解决了 MySQL Client 分裂导致有可能读取到旧数据的问题,如图 9 所示。

图 9 实现一个可靠日志存储保证各 MySQL 的数据一致


自动选主

多机自动选主最常见的实现方式是由各个参与者发起投票,获得多数派支持的机器为 Master,同时把 Master 信息记录到可靠存储。Master 机器定期到可靠存储延长租约;非 Master 机器定期检查 Master 租约是否过期,从而决定是否要发起选举自己为 Master 的投票。


为了避免修改 MySQL 代码,在 MySQL 机器上增加一个 Agent,由 Agent 来替代 MySQL 发起选主投票和续期租约;可靠存储继续由 BinlogSvr 承担。


Agent 完成以下功能:


  1. Master 机器的 Agent 监控本机 MySQL 是否正常服务;如果正常服务,则定期到可靠存储延长租约,否则停止续约。

  2. 非 Master 机器的 Agent 定期从可靠存储检查 Master 租约是否过期;如果过期,再检查本机 MySQL 是否已经执行了所有 Binlog。如果已经执行了所有 Binlog,则发起选举自己为 Master 的投票,如图 10 所示。 图 10 可靠日志存储和 Agent 共同实现自动选主机制


PhxSQL 架构和实现


从上述思路可以得出 PhxSQL 的简单三层架构。对于每一个节点,部署 3 个模块(PhxSQLProxy,MySQL,PhxBinlogSvr)。多个节点上的 PhxBinlogSvr 组成一个可靠的日志存储集群和可靠的 Master 信息存储集群;PhxBinlogSvr 同时承担 Agent 的责任。PhxSQLProxy 负责请求的透传。Master 结点上的 PhxSync 负责将 MySQL 的 Binlog 发送到 PhxBinlogSvr,如图 11 所示。


图 11 PhxSQL 基本架构


Proxy(PhxSQLProxy)


请求透传


请求透传是 Proxy 主要的功能。主要解决在进行 Master 切换的时候,MySQL Client 会被分裂,不同的 Client 可能连接到不同的 MySQL。导致出现 MySQL Client 写入数据到错误的 Master 或者从错误的 Master 读取到错误的数据。


Proxy 的请求透传分两种:


  1. 读写端口请求透传:Slave 节点收到的请求透传给 Master 节点执行。Master 节点收到的请求直接透传给本机 MySQL 执行。

  2. 只读端口请求透传:Master 节点收到的请求透传给 Slave 节点执行。Slave 节点收到的请求直接透传给本机 MySQL 执行,如图 12 所示。

    图 12 Proxy 请求透传流程


高性能:由于 Proxy 接管了 MySQL Client 的请求,为了使整个集群的读写性能接近单机 MySQL,Proxy 使用协程模型提高自身的处理能力。


Proxy 的协程模型使用开源的 Libco 库。Libco 库是微信团队开源的一个高性能协程库,具有以下特点:


  1. 用同步方式写代码,实现异步代码的性能。

  2. 支持千万级的并发连接。

  3. 项目地址https://github.com/tencent/libco


完全兼容 MySQL:为了已有的应用程序能够不做任何修改就能迁移到 PhxSQL,Proxy 需兼容 MySQL 的所有功能。


兼容 MySQL 事务

MySQL 事务管理基于连接,同一个事务的所有请求通过同一个连接通信。在事务处理中连接丢失,事务将被 rollback(http://dev.mysql.com/doc/refman/5.6/en/innodb-autocommit-commit-rollback.html)。


Proxy 使用 1:1 连接模型完全兼容 MySQL 事务。每当 MySQL Client 发起一个连接到 Proxy,Proxy 都会相应地发起一个连接到 MySQL。两条连接中,任意一个中断,另外一个也相应断开,对应的事务会被 rollback,如图 13 所示。

图 13 Proxy 的 1 对 1 事务连接模型


兼容 MySQL 权限

MySQL 的权限管理基于(用户,源 IP)对,源 IP 是通过 socket 句柄反查获取。当请求通过 Proxy 连接到 MySQL 时,源 IP 为 Proxy 本地 IP,权限管理会出现异常。


Proxy 利用 MySQL 协议 HEAD 保留字段透传真实源 IP 到 MySQ,MySQL 再从 HEAD 保留字段获取正确的源 IP 进行权限管理,如图 14 所示。

图 14 Proxy 通过修改 MySQL 协议兼容 MySQL 权限


PhxSync

PhxSync 的功能和 MySQL 的 semisync 插件类似。经过调研,对 semisync 插件的接口做少量的调整,就可以使用这些插件接口来实现 PhxSync。


PhxSync 功能主要是:


  1. 正常运行时提交 Binlog:MySQL 在正常写入或者更新数据时,会调用 after_flush 接口。PhxSync 插件通过实现 after_flush 接口将 MySQL 新写入的 Binlog 提交到本机的 BinlogSvr,由本机 BinlogSvr 通过 Paxos 协议同步到 BinlogSvr 集群。

  2. 重启时校准本地 Binlog:MySQL 在重启时通过查询 BinlogSvr 集群判断本地 Pending Binlog 的状态。如果 Pending Binlog 未复制到 BinlogSvr 集群则从本地删除,保持本地的 Binlog 数据和 BinlogSvr 集群的 Binlog 数据一致。


由于 MySQL 没有提供在重启时的插件接口,为了后续维护方便,在 MySQL 代码层抽象出了一个新插件接口 before_binlog_init 用于校准 Binlog。


上述对 after_flush 接口的调整,和新增的 before_binlog_init 接口已经提交补丁给 MySQL 官方(http://bugs.mysql.com/bug.php?id=83158)。


PhxBinlogSvr


PhxBinlogSvr 主要负责存储 Binlog 和 Master 信息的维护。在数据复制阶段,通过 Paxos 协议保证 PhxBinlogSvr 各节点的数据一致性(下文称 PhxBinlogSvr 为 BinlogSvr)。


  1. PhxPaxos 库:BinlogSvr 使用 PhxPaxos 库进行数据的复制。PhxPaxos 库是微信团队开源的 Paxos 类库,具有以下特性:1. 保证各节点的数据一致。

  2. 保证集群机器超过一半存活还能服务。

  3. 高性能。

  4. 功能完善。

  5. 稳定性经过大规模验证。

  6. 接口方便易用。

  7. 项目地址https://github.com/tencent/phxpaxos


BinlogSvr 异常情况处理


防止 Slave 的节点提交数据


当旧 Master 在提交数据时由于网络问题数据包被卡在网络,且新 Mater 已经成功切换时,或者人为错误直接往 Slave 节点的 MySQL 写入数据时,则会出现 Slave 节点提交数据的情况。多节点同时提交数据会出现 BinlogSvr 的 Binlog 数据和 MySQL 存储的 Binlog 数据不一致的情况。


BinlogSvr 存储了集群内的 Master 信息。当其收到 MySQL 提交的数据时,可根据 Master 信息拒绝非 Master 节点的提交,如图 15 所示。

图 15 BinlogSvr 通过 Master 信息拒绝非 Master 节点的提交


防止 Master 提交错误数据


在某些情况下,Master 可能会重新发送数据或者发送错误数据。譬如在网络不好的情况下 Master 由于提交数据超时而重发数据。磁盘发生故障或者数据被错误回滚或者修改的时候,Master 会提交错误的数据。


BinlogSvr 使用乐观锁机制来防止 Master 的异常提交。在 MySQL 提交数据给 BinlogSvr 时,以本机 MySQL 已经执行的 GTID 为乐观锁,提交的内容为(本机 MySQL 已经执行的最新 GTID,本次要提交的 Binlog)。BinlogSvr 通过检查请求中(本机 MySQL 已经执行的最新 GTID)和自身保存的最新 GTID 是否匹配来拒绝重新发送或者异常发送的数据,如图 16 所示。

图 16 BinlogSvr 使用乐观锁拒绝 Master 在数据异常的情况下提交数据


  1. 支持 MySQL 原生复制协议:为了让 Slave 能从 BinlogSvr 获取 Binlog,最好的方式就是 BinlogSvr 支持 MySQL 原生的复制协议,这样不用对 Slave 做任何修改,如图 17 所示。

    图 17 BinlogSvr 支持 MySQL 使用原生复制协议获取 Binlog 数据


  2. Master 管理:BinlogSvr 除了存储 MySQL 的 Binlog 数据,还存储了 Master 信息。同时还承担了 Agent 的角色,负责监控 MySQL 的状态,必要时发起选举自己为 Master 的投票。


BinlogSvr 通过 Paxos 协议进行 Master 选举,选举成功后成为 Master 并拥有租约。通过 Paxos 协议选举保证了最终只产生一个 Master 且每个节点记录了一致的 Master 信息。


PhxSQL 效果


PhxSQL 数据一致性


通过比较 PhxSQL 集群中各节点的数据(MySQL Binlog,PhxPaxos,BinlogSvr) 判断各节点数据是否一致,如图 18 所示。

图 18 PhxSQL 3 机数据对比


Master 自动切换


通过观察 Master 宕机时各节点的流量变化判断 Master 是否顺利切换。下图中的红线代表流量。当 Master 宕机时,流量会随之转移,代表 Master 顺利切换,如图 19 所示。

图 19 PhxSQL 进行 Master 切换时各节点的写入流量变化


PhxSQL 性能


  • MySQL 版本:Percona 5.6.31-77.0

  • 机器信息:

    CPU :Intel Xeon CPU E5-2420 0 @ 1.90GHz * 24。

    Memory : 32G。

    Disk:SSD Raid10。

    Ping Costs:Master→Slave:3 ~ 4ms; client→Master :4ms。

  • 工具和参数:

    sysbench。

    –oltp-tables-count=10 –oltp-table-size=1000000 –num-threads=500。

    –max-requests=100000 –report-interval=1 –max-time=200。


PhxSQL 的写性能比 MySQL 的半同步好,读性能由于多了一层 Proxy 导致比 MySQL 的半同步稍差。

图 20 PhxSQL 和 MySQL 的性能对比


本文转自微信后台团队,如有侵犯,请联系我们立即删除


OpenIMgithub 开源地址:

https://github.com/OpenIMSDK/Open-IM-Server

OpenIM 官网 : https://www.rentsoft.cn

OpenIM 官方论坛: https://forum.rentsoft.cn/


更多技术文章:

开源 OpenIM:高性能、可伸缩、易扩展的即时通讯架构 https://forum.rentsoft.cn/thread/3

【OpenIM 原创】简单轻松入门 一文讲解 WebRTC 实现 1 对 1 音视频通信原理 https://forum.rentsoft.cn/thread/4

【OpenIM 原创】开源 OpenIM:轻量、高效、实时、可靠、低成本的消息模型 https://forum.rentsoft.cn/thread/1

用户头像

OpenIM

关注

还未添加个人签名 2021.08.30 加入

还未添加个人简介

评论

发布
暂无评论
PhxSQL设计与实现(详细版)