MySQL 主从复制和 PhxSQL 强一致

为了实现 MySQL 高可用,保证数据一致,往往会设计出一种基于 MySQL 主从复制的架构。看了一些文章,了解到 MySQL 主从复制在数据同步上存在一些缺陷,可能导致数据不一致,而 PhXSQL 集群是为了克服它的缺陷而设计的一种 MySQL 集群。

MySQL 主从复制

MySQL 主从复制示意图:
MySQL 主从复制过程

谈到 MySQL 的主从复制,就必须对 binlog,异步复制,半同步复制有所了解。

binlog

binlog 可以被认为是一种日志文件,它保存了在 MySQL 服务器实例上数据修改的信息。

binlog 有两个重要的作用:

  1. 用于数据复制
    在主从结构中,binlog 作为操作记录从 master 被发送到 slave。slave 主机将从 master 接收到的日志保存到 relay log 中。

  2. 用于数据备份
    在数据库备份文件生成后,binlog 保存了数据库开始备份后的一些事件。它能将数据库从备份点更新的最新。

binlog 有三种格式:Statement、Row 以及 Mixed。

  1. Statement 是基于 SQL 语句级别的 binlog。Statement 格式的 binlog 保存会修改数据的 SQL 语句。

  2. Row 是基于行级别的 binlog。Row 格式的 binlog 保存哪一行或哪几行数据被修改。

  3. Mixed 是 Statement 和 Row 混合模式的 binlog。

异步复制

异步复制的大致过程如下:

  1. master 提交事务后,并且写入 binlog,返回事务成功标记;

  2. master 通知 slave 拉取 binlog,转储成relay log;

  3. slave 重放 relay log,更新数据。

步骤 1 和步骤 3 之间是异步进行的,无需等待确认各自的状态。

半同步复制

下面两幅图表示的是 5.7.2 和 5.7.2 之前版本的 MySQL 半同步示意图。

5.7 之前

在 MySQL 5.5 和 5.6 中,Master 在 Engine Commit 之后再 Wait ACK。

5.7.2

在 MySQL 5.7.2 中,为了提高数据的完整性,Wait ACK 将会提前。Master Wait ACK 之后才会进行 Engine Commit。

半同步复制在异步复制的基础上,进行了完善,大致过程如下:

  1. 作为 Slave 的主机的 IO 线程连接上 Master,并请求从指定日志文件的指定位置(或者从最开始的日志)之后的日志内容;

  2. Master 接收到来自 Slave 的 IO 线程的请求后,通过负责复制的 IO 线程根据请求信息读取制定日志指定位置之后的日志信息,返回给 Slave 的 IO 线程。返回信息中除了日志所包含的信息之外,还包括本次返回的信息已经到 Master 端的 binlog 文件的名称以及 binlog 的位置;

  3. Slave 的 IO 线程接收到信息后,将接收到的日志内容依次添加到 Slave 端的 relay log 文件的最末端,并将读取到的 Master 端的 binlog 的文件名和位置写到 master-info 文件中,以便在下一次读取时候能快速从 Master 指定位置以的日志内容;

  4. Slave 的 SQL 线程检测到 relay log 中新增加了内容后,将解析 relay log 的内容,将数据更新到本地数据库。

自 MySQL 5.6.5,引入了 GTID 来完成复制。GTID (Global Transaction Identifiers)是全局事务标识符,master 和 slave 上的每个事务都可以用它来标识和追踪。

这也就意味着,使用 GTID 时没必要再去记录 binlog 文件名和位置。因为基于 GTID 的复制是完全基于事务的,容易判断主从是否同步,只要提交到 master 的事务也提交到 slave,它们之间的同步就得到保证。

PhxSQL 一致性

了解了 MySQL 主从复制后,便知道 MySQL 是通过复制 binlog 来保证主从数据同步。 现有的 MySQL 主从结构中,至少有一台 slave 返回 ACK 到 master,master 就进行 engine commit。MySQL 的半同步存在缺陷:binlog 可能不一致,切换 master 后可能导致数据不一致。

PhxSQL 是一个兼容 MySQL,具有强一致、高可用的关系型数据库集群。它的设计是为了解决 MySQL 半同步复制的不足,促使 MySQL 集群在 master 切换时还能保证数据一致。

原理

PhxSQL 集群构造了一个可靠的 binlog,来保证 PhxSQL 集群的数据一致。

在 PhxSQL 集群中,master 接收到多数派 slave 的 ACK 才会进行 engine commit ;而且,它还确保了至多只有一个 master 在运行,从而避免了 master 切换时出现了多 master 而导致的数据不一致。

PhxSQL 架构图如下:

PhxSQL 架构

它与 MySQL 集群不同之处在于:增加了 2 个组件和 1 个插件。

在 MySQL 集群中,由 MySQL 服务来负责 binlog 的复制;而在 PhxSQL 集群中,由插件 phxbinlogsvr 来负责复制 binlog。MySQL master 不再直接复制 binlog 到 slave,而是通过插件 phxsync 复制到 binlogsvr 集群,然后 MySQL slave 去本地的 phxbinlogsvr 取 binlog。

phxbinlogsvr 集群通过 paxos 协议来复制 binlog。paxos 协议可以认为是一个分布式一致性协议,它通过多数派数据一致保证分布式集群的数据一致。因此,集群可以保证 binlog 的一致,从而形成可靠的 binlog。

另外,PhxSQL 实现了 master 的自动切换,避免同一时刻出现多个 master 存在的情况。

master 有一个租约时间 t,即在一台主机成为 master 后的 t 时间内,它一直是 master。在等到租约过期后,其它主机将可以发起申请成为新的 master。

租约未到期时,不会有其它的主机申请成为新的 master。而且,master 的租约时间是从它开始申请成为新 master 开始计算的;而 slave 的租约时间是它们知道新的 master 产生的那一刻开始计算。因此,master 会比 slave 先知道自己租约到期,也就先于 slave 申请成为新的 master。

假如当前的 master crash 了,那么新一轮的 master 最迟将会在租约时间 t 加上选举新 master 时间后产生。

slave 意识到租约到期后,检查自己是否已完成 binlog 重放。若完成,便可以发出申请成为 master 。发出申请后将进行选举。

保证了 binlog 可靠和 master 唯一,就克服了 MySQL 集群的缺陷,使 MySQL 集群 (PhxSQL) 在 master 切换过程中保证数据的一致。

参考

  1. mysql binlog 系列(一)—- binlog 介绍、日志格式、数据查看等
  2. 浅谈 MySQL Replication 基本原理
  3. Replication with Global Transaction Identifiers
  4. MySQL 背后的数据一致性分析
  5. 微信自研生产级 paxos 类库 PhxPaxos 实现原理介绍
  6. Loss-less Semi-Synchronous Replication on MySQL 5.7.2
  7. Paxos 理论介绍 (3): Master 选举
  8. FAQ系列 | 如何保证主从复制数据一致性