概述
可能为了更迭升级服务器,或者主库出现问题,又或者只是希望重新分配容量,此时需要切换主库。
如果这是计划内的切换,会相对容易点。只需要在从库上使用CHANGE MASTER TO命令,并设置合适的值。大多数的值都是可选的,至少要指定需要改变的项即可。从库将抛弃之前的配置和中继日志并从新的主库开始复制。同样新的参数会被更新到master.info文件中,这样就算重启,从库配置也不会丢失。
整个过程中最难得就是获取新的主库上合适的二进制日志位置,这样从库才可以从和老主库相同的逻辑位置开始复制
把从库提升为主库要更困难一点。有两种场景需要将从库替换成主库,一种是计划内的,一种是计划外的
计划内的提升
把从库提升为主库理论上是简单的。
- 停止向老的库写入
- 让从库追赶上主库
- 将一台从库配置为新的主库
- 将从库和写操作指向新的主库,然后开启主库的写入
上述步骤中还是隐藏着很多细节,更深入一点,下面步骤就是大多数配置需要的步骤
- 停止当前主库上的所有写操作。如果可以,最好能将所有的客户端程序关闭 (除了复制连接)。为客户端程序建立一个“do not run”这样的类似标记可能会有所帮助如果正在使用虚拟IP 地址,也可以简单地关闭虚拟IP,然后断开所有的客户端连接以关闭其打开的事务。
- 通过FLUSH TABLES WITH READ LOCK在主库上停止所有活跃的写入,这一步是可选的。也可以在主库上设置 read_only选项。从这一刻开始,应该禁止向即将被替换的主库做任何写入。因为一旦它不是主库,写人就意味着数据丢失。注意,即使设置 read_only也不会阻止当前已存在的事务继续提交。为了更好地保证这一点,可以“kill”所有打开的事务,这将会真正地结束所有写入。
- 选择一个从库作为新的主库,并确保它已经完全跟上主库 (例如,让它执行完所有
从主库获得的中继日志)。 - 确保新主库和旧主库的数据是一致的。可选
- 在新主库上执行STOP SLAVF
- 在新主库上执行 CHANGE MASTER TO MASTER HOST= ’ ',然后再执行 RESET SLAVE,使其断开与老主库的连接,并丢弃 master.info 里记录的信息(如果连接信息记录在my.cnf里,会无法正确工作,这也是我们建议不要把复制连接信息写到配置文件里的原因之一)。
- 执行 SHOW MASTER STATUS记录新主库的二进制日志坐标。
- 确保其他从库已经追赶上
- 关闭旧主库。
- 在MySQL51 及以上版本中,如果需要,激活新主库上事件
- 将客户端连接到新主库。
- 在每台从库上执行CHANGE MASTER TO语,使用之前通过SHOW MASTER STATUS获得的二进制日志坐标,来指向新的主库
计划外的提升
当主库崩溃的时候,需要提升一台从库来代替它,这个过程就不会太容易。如果只有一台从库,可以直接使用这台从库。但如果有超过一台的从库,就需要一些额外的工作。
另外,还有潜在的丢失复制事件。可能主库上已发生的修改还没有更新到它的任何一台设备上的情况。甚至还可能一条语句在主库上执行了回滚,但在从库上没有回滚,这样从库可能超过主库的逻辑复制位置。如果能在某一点恢复主库的数据,也许就可以取得丢失的语句并手动执行他们。
在以下的步骤中,需要确保在计算中使用Master_Log_File和Read_Master_Log_Pos的值
- 确定哪台从库的数据最新。检查每台备库上SHOW SLAVE STATUS 命的输出,选择其中Master_Log File/read_Master_Log_Pos 的值最新的那个。
- 让所有从库执行完所有其从崩溃前的旧主库那获得的中继日志。如果在未完成前修改从库的主库,它会抛弃剩下的日志事件,从而无法获知该从库在什么地方停止。
- 执行前一小节的5~7步。
- 比较每台从库和新主库上的 Master_Log_0File/Read_Master_Log_Pos 的值
- 执行前一小节的10~12 步。
再次需要注意,在配置主从复制的时候,所有从库开启log_bin和log_slave_updates,这样就可以帮助你将所有的从库恢复到一个一致的时间点,如果没有开启这两个选项,则很难可靠的做到这一点。
确定期望的日志位置
如果有从库和新主库的位置不相同,则需要找到该从库最后一条执行的事件在新主库的二进制日志中相应的位置,然后再执行 CHANGE MASTER TO。可以通过mysglbinlog 工具来找到从库执行的最后一条查询,然后在主库上找到同样的查询,进行简单的计算即可得到。
为了便于描述,假设每个日志事件有一个自增的数字ID,最新的从库,也就是新的主库,在旧主库崩溃的时获得了编号为100的事件,假设有另外两台设备:replica2和replica3。replica2已经获得了99号事件,replica3获取了98号事件。如果把这两台从库都指向了新主库的同一个二进制日志的位置,他们将会从101号事件开始复制,从而导致数据不同步。但只要新主库的二进制日志已经通过log_slave_updates打开,就可以在新主库的二进制中找到99和100日志,从而将从库恢复到一致的状态。
由于服务器重启,不同的配置,日志轮转或者FLUSH LOGS命令,同一个事件在不同的服务器上可能有不同的偏移量。找到这些事件可能会耗很长时间并且枯燥,但是通常没有难度。通过mysqlbinlog从二进制日志或中继日志中解析出每台从库上执行的最后一事件,并同样使用该命令解析新主库上的二进制日志,找到相同的查询,mysglbinlog 会打印出该事件的偏移量,在 CHANGE MASTER TO命令中使用这个值
更快的方式是把新主库和停止的从库上的字节偏移量相减,它显示了字节位置的差异。然后把这个值和新主库当前的二进制日志的位置相减,就可以得到期望的查询的位置。然后验证一下就可以据此启动从库。
假设 server1是 server2和 server3 的主库,其中服务器server1已经崩溃。根据SHOW SLAVE STATUS获得 Master_Log_File/Read_Master_Log_Pos 的值,server2 已经执行完了 server1上所有的二进制日志,但 server3还不是最新数据。
如图所示,我们可以肯定 server2 已经执行完了主库上的所有二进制日志,因为Master_Log_File和Read Master_Log_Pos值和server1上最后的日志位置是相吻合的,因此我们可以将 server2 提升为新主库,并将 server3 设置为 server2的从库。
应该在server3上为需要执行的CHANGE MASTER TO语赋予什么样的参数呢?
这里需要做一点点计算和调查。server3 在偏移量 1493 停止,比 server2执行的最后一条语句的偏移量 1582要小89字节。server2正在向偏移量为8167的二进制日志写入,8167-89=8078,因此理论上我们应该将 server3 指向 server2的日志的偏移量为8078 的位置。最好去确认下这个位置附近的日志事件,以确定在该位置上是否是正确的日志事件,因为可能有别的例外,例如有些更新可能只发生在 server2 上。
假设我们观察到的事件是一样的,下面这条命令会将 server3 切换为 server2的从库
mysql > CHANGE MASTER TO MASTER_HOST="server2",MASTER_LOG_FILE="mysql-bin.000009",MASTER_LOG_POS=8078;
如果服务器在它崩溃时已经执行完成并记录了超过一个事件,会怎么样呢?
因为server2仅仅读取并执行到了偏移位置 1582,你可能永远地失去了一个事件。但是如果老主库的磁盘没有损坏,仍然可以通过 mysglbinlog 或者从日志服务器的二进制日志中找到丢失的事件。
上述流程中一个可调整的地方是使用可靠的方式来存储二进制日志,如 SAN 或分布式复制数据库设备 (DRBD)。即使主库完全失效,依然能够获得它的二进制日志。也可以设置一个日志服务器,把从库指向它,然后让所有从库赶上主库失效的点。这使得提升一个从库为新的主库没那么重要,本质上这和计划中的提升是相同的。
主从复制会有哪些问题以及解决方案
数据损坏或丢失的错误
主库意外关闭
描述:
如果没有设置主库的 sync_binlog 选项,就可能在崩溃前没有将最后的几个二进制日志事件刷新到磁盘中。从库I/0 线程因此也可一直处于读不到尚未写入磁盘的事件的状态中。当主库重新启动时,从库将重连到主库并再次尝试去读该事件,但主库会告诉从库没有这个二进制日志偏移量。二进制日志转储线程通常很快,因此这种情况并不经常发生。
解决方案:
解决这个问题的方法是指定从库从下一个二进制日志的开头读日志。但是一些日志事件将永久地丢失,建议使用 Percona Toolkit 中的pt-table-checksum 工具来检查主从一致性,以便于修复。可以通过在主库开启 sync_binlog 来避免事件丢失。
即使开启了 sync_binlog,MyISAM 表的数据仍然可能在崩溃的时候损坏,对于InnoDB事务,如果innodb_flush_log_at_trx_commit没有设为1,也可能丢失数据(但数据不会损坏)。
从库意外的关闭
当从库在一次非计划中的关闭后重启时,会去读 master.info 文件以找到上次停止复制的位置。不幸的是,该文件并没有同步写到磁盘,文件中存储的信息可能是错误的。从库可能会尝试重新执行一些二进制日志事件,这可能会导致唯一索引错误除非能确定从库在哪里停止 (通常不太可能),否则唯一的办法就是忽略那些错误。Percona Toolkit 中的pt-slave-restart 工具可以帮助完成这一点。
如果使用的都是InnoDB表可以在重启后观察MySQL错误日志。InnoDB在恢复过程中会打印出它的恢复点的二进制日志坐标。可以使用这个值来决定从库指向主库的偏移量。Percona Server 提供了一个新的特性,可以在恢复的过程中自动将这些信息提取出来,并更新 master.info 文件,从根本上使得复制能够协调好从库上的事务
主库上的二进制日志损坏
如果主库上的二进制日志损坏,除了忽略损坏的位置外,你别无选择。可以在主库上执行FLUSH LOGS命令,这样主库会开始一个新的日志文件,然后将从库指向该文件开始的位置。也可以试着去发现损坏区域的结束位置。某些情况下可以通过SET GLOBAL SQL_SLAVE_SKIP_COUNTER =1来忽略一个损坏的事件。如果有多个损坏的事件,就需要重复该步骤,直到跳过所有损坏的事件。但如果有太多的损坏事件,这么做可能就没有意义了。损坏的事件头会阻止服务器找到下一个事件。这种情况下,可能不得不手动的去找到下一个完好的事件。
从库上的中继日志损坏
如果主库上的日志是完好的,就可以通过CHANGE MASTER TO 命令丢弃并重新获取损坏的事件。只需要将从库指向它当前正在复制的位置(Relay_Master_Log_File/Exec_Master_Log_Pos)。这会导致从库丢弃所有在磁盘上的中继日志。就这点而言,MySQL 5.5之后的版本做了改进,它能够在崩溃后自动获取中继日志。
二进制日志和InnDB事务日志不同步
当主库崩溃时,InnDB可能将一个事务标记为已提交,此时该事物可能还没有记录到二进制日志当中。除非是某个从库的中继日志已经保存,否则没有任何办法恢复丢失的事务。在MySQL 5.0 版本可以设置 sync_binlog 选项来防止该问题,对于更早的MySQL4.1可以设置 sync_binlog和 safe_binlog选项。