MySQL 复制 - 性能与扩展性的基石 4:主备切换

一旦使用 MySQL 的复制功能,就很大可能会碰到主备切换的状况。也许是为了迭代升级服务器,或者是主库出现问题时,将一台备库转换成主库,或者只是但愿从新分配容量。不过出于什么缘由,都须要将新主库的信息告诉其它备库。mysql

对于主备切换,若是是计划内的操做,较为容易(至少比紧急状况下容易)。只需在备库简单的使用 CHANGE MASTER TO 命令,并指定合适的值便可。并且大多数的值是可选的,只要指定须要改变的配置项接口。sql

备库将抛弃以前的配置和中继日志,并重新的主库开始复制。同时,新的参数会被更新到 master.info 文件中,这样就算重启,备库配置信息也不会丢失。服务器

整个过程当中最难的是获取新主库上合适的二进制日志位置。这样备库才能够从老主库相同的逻辑位置开始复制。工具

把备库提高为主库要较为麻烦,咱们把备库提高主库分为计划内切换计划外切换两种场景。spa

1 计划内切换

备库提高为主库,简单来讲,有如下步骤:日志

  1. 中止向老主库写入。
  2. 让备库追遇上主库(可选,能够简化后续的步骤)。
  3. 将一台备库配置为新主库。
  4. 将备库和写操做指向新主库,而后开启主库写入。

但上面的过程当中还所以着不少细节。一些场景可能依赖于复制的拓扑结构。更深刻一点,下面是大多数配置须要的步骤:blog

  1. 中止当前主库上的全部写操做。若是能够,最好能将全部的客户端程序关闭(除了复制链接)。
  2. 经过 FLUSH TABLE WITH READ LOCK 命令在主库上中止全部活跃的写入。也能够在主库上设置 read_only 选项。意味着从这一刻起,禁止向老主库作任何写入操做。由于一旦切换的新主库,老主库的写入就意味着数据丢失。要注意的是,即便设置了 read_only 也不会阻止当前已存在的事务继续提交。所以,能够 kill 全部打开的事务,真正的结束全部写入。
  3. 选择一个备库做为新的主库,并确保它已经彻底跟上主库(例如,让它执行完全部从主库得到的中继日志)。
  4. 确保新主库和老主库数据一致。
  5. 在新主库上执行 STOP SLAVE。
  6. 在新主库上执行 CHANGE MASTER TO MASTER_HOST='',而后再执行 RESET SLAVE,使其断开与老主库的链接,并丢弃 master.info 里记录的信息(若是链接信息记录在 my.cnf 里,会没法正常工做,所以咱们建议不要把复制链接信息写到配置文件里)。
  7. 执行 SHOW MASTER STATUS 记录新主库的二进制日志坐标。
  8. 确保其它备库已经追遇上老主库。
  9. 关闭老主库。
  10. 将客户端链接到新主库。
  11. 在每台备库上执行 CHANGE MASTER TO 语句,使用以前得到的二进制日志坐标,指向新的主库。

2 计划外切换

当主库崩溃时,须要将一台备库提高为主库。这个过程就比较麻烦。若是只有一台备库,能够直接使用这台备库。但若是有超过一台的备库,就须要作一些额外的工做。接口

另外,还有潜在的丢失复制事件的问题。可能有主库上已发生的修改尚未更新到它任何一台备库上的状况。甚至可能一条语句在主库上执行了回滚,但在备库上没有回滚,这样备库可能就超过主库的逻辑复制位置。若是能在某一点恢复主库的数据,也许就能够取得丢失语句,并手动执行他们。事件

在如下描述中,须要确保在服务器中使用 Master_Log_File 和 Read_Master_Log_Pos 的值。事务

2.1 主备结构之备库提高

  1. 肯定哪台备库的数据最新。检查每台备库上 SHOW_SLAVE_STATUS 命令的输出,选择其中 Master_Log_File 和 Read_Master_Log_Pos 的值最新的那个。
  2. 让全部备库执行完全部从老主库崩溃前得到的中继日志。
  3. 在新主库上执行 STOP SLAVE。
  4. 在新主库上执行 CHANGE MASTER TO MASTER_HOST='',而后再执行 RESET SLAVE,使其断开与老主库的链接,并丢弃 master.info 里记录的信息。
  5. 执行 SHOW MASTER STATUS 记录新主库的二进制日志坐标。
  6. 比较每台备库和新主库上的 Master_Log_File 和 Read_Master_Log_Pos 的值。
  7. 将客户端链接到新主库。
  8. 在每台备库上执行 CHANGE MASTER TO 语句,使用以前得到的二进制日志坐标,指向新的主库。

若是已经在全部备库上开启了 log_bin 和 log_slave_updates,就能够将全部备库恢复到一个一致的时间点,若是没有开启这两个选项,则很难作到这一点。

上面过程当中比较重要的一点是肯定日志位置。接下来,咱们就来看看如何却。

3 肯定日志位置

若是有备库和新主库的位置不相同,则须要找到该备库最后一条执行的事件在新主库的二进制日志中对应的位置,而后再执行 CHANGE MASTER TO。能够经过 mysqlbinlog 工具来找到备库执行的最后一条查询,而后再主库上找到一样的查询,进行简单的计算便可获得。

为了便于描述,假设每一个日志事件都有一个自增数字 ID。新主库在老主库崩溃时得到了编号为 100 的事件,另外两条备库:R2 和 R3。R2 已结获取了 99 号事件,R3 获取了 98 号事件。

若是把 R2 和 R3 都指向新主库的同一个二进制日志位置,它们将从 101 号事件开始复制,从而致使数据不一样步。但只要新主库的二进制日志已结经过 log_slave_updates 打开,就能够在新主库的二进制日志中找到 99 号 和 100 号事件,从而将备库恢复到一致的状态。

因为服务器重启,不一样的配置,日志轮转或者 FLUSH LOGS 命令,同一个事件在不一样的服务器上可能有不一样的偏移量。咱们能够经过 mysqlbinlog 从二进制日志或中继日志中解析出每台备库上执行的最后一个事件,并还有该命令解析新主库上的二进制文件,找到相同的查询,mysqlbinlog 会打印出该事件的偏移量,在 CHANGE MASTER TO 命令中使用这个值。

更快的方法是把新主库和中止的备库上的字节偏移量相减,它显示了字节位置的差别。而后把这个值和新主库当前二进制日志的位置相减,就能够获得指望的查询位置。

一块儿来看个栗子。

假设 s1 是 s2 和 s3 的主库。其中 s1 已经崩溃。根据 SHOW SLAVE STATUS 得到 Master_Log_File 和 Read_Master_Log_Pos 的值,s2 已结执行完了 s1 上全部的二进制日志,但 s3 尚未。如图 1:

图 1:s1 崩溃,s2 已追遇上,s3 落后

咱们能够确定 s2 已经执行完了主库上的全部二进制日志,由于 Master_log_File 和 Read_Master_Log_Pos 的值和 s1 上最后的日志位置相吻合。所以,咱们能够将 s2 提高为新主库,并将 s3 设置为 s2 的备库。

应该在 s3 上为须要执行的 CHANGE MASTER TO 语句赋予什么参数呢?这里须要作一点计算。

s3 在偏移量 1493 处中止,比 s2 执行的最后一条语句的偏移量 1582 要小 89 字节。

s2 正在向偏移量为 8167 的二进制日志写入,所以,理论上咱们应该将 s3 指向 s2 日志的偏移量为 8167-89=8078 的位置。

最后在 s2 日志中的 8078 位置,肯定该位置上是不是正确的日志事件。

若是验证没问题,能够经过下面命令将 s3 切换为 s2 的备库:

CHANGE MASTER TO MASTER_HOST="s2 host", MASTER_LOG_FILE="mysql-bin.000009", MASTER_LOG_POS=8078;

若是服务器在它崩溃时已经执行完成并记录了一个事件 a。由于 s2 仅仅读取并执行到了 1582,所以可能会失去事件 a。可是若是老主库的磁盘没有损坏,仍然能够经过 mysqlbinlog 或者从日志服务器的二进制日志中找到丢失的事件。

总结

  1. 备库提高区分计划内和计划外场景。
  2. 备库提高,找到新主库准确的二进制日志位置是关键。
相关文章
相关标签/搜索