MySQL 主从一致介绍

MySQL Replication 可以用于数据备份,负载平衡,高可用,容错等。基本方式是,将 master 的数据(包括 SQL等)同步复制到 slave(s), 并重新执行。master 会将所有的改变以 binlog 文件保存,并异步传输到 slave 端。这样做的好处包括:

  • 扩展解决方案:在多个 slave 负载分布,提高性能。
  • 数据安全: 我们可以在不打断master上的数据请求的情况下,在 slave 可以暂停 replication 进程,从而完成数据的备份。
  • 分析:数据在master上创建,一些数据分析的工作在slave上进行,这不影响master的性能。
  • 远距离的数据分布: 就近使用 slave 数据,而不是每次请求远端 master。

Replication Models

传统的复制基于复制 master 上的 binary log中的事件,这需要记录日志文件及它们的位置。新的复制是基于全局事务ID(GTIDs),它能保证所有在 master 上提交的事务都可以在 slave 上执行一遍,从而完成复制。
复制模式包括 asynchronous(异步), semisynchronous(半同步)。有些场景我们需要同步复制,可以使用 MySQL 集群

Asynchronous

Asynchronous 为默认模式,在 master 上提交的updates执行成功并写入 binlog 之后,master 会继续处理其他的write请求。 之后任何时刻,slave 都可能与 master 建立链接,复制新增的变更日志,然后在本地执行,这必然导致在同一时间点备库上的数据可能与主库的不一致,并且无法保证主备之间的延迟。

Semisynchronous

Semisynchronous 需要在 master和slaves上安装插件来实现复制,且通过配置文件开启“半同步”,否则采用 asynchronous 复制。

  1. 当slave与master建立连接时会表明其是否开启了“半同步”特性,
  2. 需要master和至少一个slaves同时开启,否则仍将采用“异步”复制,master 执行事务提交的线程,在事务提交后堵塞,直到至少一个“半同步”的slave返回确认消息(ACK)或者所有的半同步slave都等待超时。
  3. slave 将接收到事务的信息写入到本地的 relay log文件且 flush 到磁盘后,才会向master返回确认消息。
  4. 当所有的半同步slaves均在指定的时间内没有返回确认消息(即timeout),那么此后master将转换成异步复制模式,直到至少一个半同步slave完全跟进才会转换为半同步模式。

如第4条描述,如果出现 timeout,比如网络问题,就会导致复制类型从 Semisynchronous 变为 Asynchronous。同时,采用半同步复制,会导致 master上的 tps 性能下降非常严重。选型时候,需要留意。

详细配置信息参见官方文档

Replication Format

Replication 之所以能够工作,归功于 binary log 的读写。不同的事件类型,决定了有不同的 binary log。主要的格式包括:

  1. statement-based repication(SBR): master将SQL statements语句写入binlog,slave 将 statements 复制到本地执行。

    • 优点: binlog 写入更少数据。备份,恢复效率更好。包含所有产生变更的操作,便于审查数据库。
    • 缺点:部分变更操作不安全。当使用SBR时,一些非确定性的操作很难复制。例如以下修改数据的 DML 操作:

      1. statement中使用了UDF,而结果可能依赖于SQL执行的时机和系统变量。
      2. DELETE,UPDATE statements 使用 LIMIT 语句但没用 ORDER BY。
      3. statement 中使用下面函数的: LOAD_FILE(),UUID(), UUID_SHORT(),USER(),FOUND_ROWS(),SYSDATE() (除非 master 和 slave 启动时增加了 –sysdate-is-now 选项),GET_LOCK(),IS_FREE_LOCK(),IS_USED_LOCK(),MASTER_POS_WAIT(),RAND(),RELEASE_LOCK(),SLEEP(),VERSION()。

      4. INSERT … SELECT 使用更多的 row-level locks。 扫描表的UPDATE statements 需要锁更多行。

      5. 对InnoDB: 使用 AUTO_INCREMENT 的 INSERT statement 会阻塞其它非冲突的 INSERT statements。
      6. 对于复杂的SQL语句,在slaves上仍然需要评估(解析)然后才能执行,而对于RBR,SQL语句只需要直接更新相应的行数据即可。
      7. 在slave上评估、执行SQL时可能会发生错误,这种错误会随着时间的推移而不断累积,数据一致性的问题或许会不断增加。
  2. row-based replication(RBR): master 将每行数据的变更信息写入 binlog,每条binlog信息表示一行(row)数据的变更内容,slaves 会复制binlog信息,以单条或批量执行变更操作。
    • 优点:
      1. 所有的变更操作,都可以被正确的replication。
      2. 对于 INSERT … SELECT,包含 AUTO_INCREMENT 的inserts,没有使用索引的UPDATE/DELETE,相对于SBR将需要更少的行锁。
    • 缺点:
      1. 记录更多日志。 复制 DML statement, RBR 会把每条变更的行写到 binary log,对于回滚,同样产生日志。 此外,为了写数据,可能会长时间锁定 binary log,可能会产生一致性问题。使用 binlog_row_image=minimal 来减小问题发生的可能性。
      2. 产生 BLOB 类型的 UDF 需要长时间复制。
      3. 在 slave 上不能看到 master上哪些 statement 被接受和执行。 (可以使用 mysqlbinlog –base64-output=DECODE-ROWS –verbose 查看变更的数据,或使用 binlog_rows_query_log_events。)
      4. 不支持 MyISAM 表的 insert 一致性,因为 slave在更新非事务性表时,server被关闭,将会导致数据不一致性问题。
  3. mix-format replication: master 将根据存储引擎、变更操作类型等,从SBR、RBR中来选择更合适的日志格式,默认为SBR。

ps: 在 MySQL 5.7.7 之前 SBR 为默认类型,MySQL 5.7.7 及之后, RBR 为默认。

Replication Implementation

Replication 功能通过三个线程实现,一个在master上,另外2个在slave上。

  1. Binlog dump 线程: 当slave 连接时,master 创建线程,向 slave 发送 binary log。 可以在 master 上通过 SHOW PROCESSLIST 查看到 Binlog Dump 线程。
    dump 线程会对 binlog 文件获取一个读锁,并读取内容发送给slave,一个变更操作读取完毕后,锁即释放,即使内容还没有发送给slave。

  2. Slave I/O 线程: 当在slave上执行 START SLAVE 后,将会创建一个I/O线程,它负责与master建立连接,请求需要的binlog,并将它们保存在本地的relay log中。在slave上通过SHOW SLAVE STATUS可以查看 Slave_IO_running 的状态。

  3. Slave SQL 线程: slave 创建 SQL 线程来读取 由slave I/O 线程写的 relay log 并执行它们。

像上面的描述,每个 master/slave 将建立 3个线程。 master为每个slave建立一个单独的Binlog dump线程,slave使用2个单独的线程来完成replication,以便Slave I/O 线程,与 Slave SQL 线程互不影响。

Improving Replication Performance

随着 slave 的增加,即使 minimal 配置,负载也会增加,因为每个 slave 都需要客户端连接到 master。同时,每个 slave 都会全量复制 master 的 binary log,master 的网络负载也会增加,甚至成为瓶颈。

官方文档提供了一种提升 replication 进程性能的方案,即创建一个深层次的 replication 结构—-即,master 只 replicate 到一个 slave,剩下的 slave 连接到它上:

submaster-performance

为了保证能够运行,需要如下配置 MySQL 实例:

  • Master 1 为主 master,所有的变更操作都写到此数据库,保证 binlog 可用。

  • Master 2 为 Master 1 的slave, 作用是其他 slave 提供 replication。 只有 Master 2 被允许连接到 Master 1,并且其 binlog 也可用。 设置 --log-slave-updates,使得 Master 1 的 replication 指令也可以写到 Master 2 的 binlog 中,从而使它们能被复制到实际的 slave 上。

  • Slave 1, Slave 2, Slave 3 作为 Master 2 的 slave, 能够复制 Master 2 的信息–确切的说,是 Master 1 上的变更操作。

Replication Issue

一致性保证

在MySQL中,一次事务提交后,需要写undo,写redo,写 binlog,写数据文件等步骤。在这个过程中,如果在某个步骤发生crash,就有可能导致主从数据的不一致。为了避免这种情况,可以通过调整 master,slave 的my.cnf 中相关选项配置,确保即便发生crash了,也不会发生主从复制的数据丢失。

  1. master

    1
    2
    3
    $ vim my.cnf
    innodb_flush_log_at_trx_commit = 1
    sync_binlog = 1 # 保证每次事务提交后,都能实时刷新到磁盘中,尤其是确保每次事务对应的binlog都能及时刷新到磁盘中
  2. slave

    1
    2
    3
    4
    $ vim my.cnf
    master_info_repository = "TABLE"
    relay_log_info_repository = "TABLE" # 确保在slave上和复制相关的元数据表也采用InnoDB引擎,受到InnoDB事务安全的保护
    relay_log_recovery = 1 # 开启relay log自动修复机制

ps: 此方法不能绝对保证一致性,因为某些SQL 本身存在不确定因素,或者人为在slave上修改数据,最终导致主从数据不一致。

核对及修复

使用 MySQL Replication 过程中,很可能会因为网络问题,参数配置不当,statement 操作不当等,出现 master 与 slave 出现不一致的情况。可以通过 pt-table-checksum, pt-table-sync 两个工具来校验并修复数据。 因为此部分还没有实践过,而且网上可以找到大量相关资料,暂略。

参考资料

  1. Mysql Replication基本原理(一)
  2. sbr, rbr 优缺点
  3. replication 实现细节
  4. 如何保证主从复制数据一致性