MySQL 复制 - 性能与扩展性的基石 3:常见问题及解决方案

主备复制过程当中有很大可能会出现各类问题,接下来咱们就讨论一些比较广泛的问题,以及当遇到这些问题时,如何解决或者预防问题发生。mysql

1 数据损坏或丢失

问题描述:服务器崩溃、断电、磁盘损坏、内存或网络错误等问题,致使数据损坏或丢失。 问题缘由:非正常关机致使没有把数据及时的写入硬盘。sql

这种问题,通常能够分为几种状况致使:数据库

1.1 主库意外关闭

问题未发生,避免方案:设置主库的 sync_binlog 选项为 1。此选项表示 MySQL 是否控制 binlog 的刷新。当设置为 1 时,表示每次事务提交,MySQL 都会把 binlog 刷下去,是最安全,性能损耗也最大的设置问题已发生,解决方案:指定备库从下一个二进制日志的开头从新读日志。可是一些日志事件将永久性丢失。可使用 Percona Toolkit 中的 pt-table-checksum 工具来检查主备一致性,以便于修复。安全

1.2 备库意外关闭

备库意外关闭重启时,会去读 master.info 文件以找到上次中止复制的位置。可是在乎外关闭的状况下,这个文件存储的信息多是错误的。此外,备库也可能会尝试从新执行一些二进制文件,这可能会致使惟一索引错误。咱们能够经过 Percona Toolkit 中的 pt-slave-restart 工具,帮助备库从新执行日志文件。bash

若是使用的是 InnoDB 表,能够在重启后观察 MySQL 的错误日志。InnoDB 在恢复过程当中会打印出恢复点的二进制日志坐标,可使用这个值来决定备库指向主库的偏移量。服务器

1.3 主库二进制日志损坏

若是主库上的二进制日志损坏,除了忽略损坏的位置外,别无选择。在忽略存货位置后,咱们能够经过 FLUSH LOGS 命令在主库开始一个新的日志文件,而后将备库指向该文件的开始位置。网络

1.4 备库中继日志损坏

若是主库上的日志是无缺的,有两种解决方案: 1) 手工处理。找到 master binlog 日志的 pos 点,而后从新同步。工具

2) 自动处理。mysql5.5 考虑到 slave 宕机中继日志损坏这一问题,只要在 slave 的的配置文件 my.cnf 里增长一个参数 relay_log_recovery=1 便可。性能

1.5 二进制日志与 InnoDB 事务日志不一样步

因为各类各样的缘由,MySQL 的复制碰到服务器崩溃、断电、磁盘损坏、内存或网络错误时,很难恢复当时丢失的数据。几乎都须要从某个点开始重启复制。优化

2 未定义的服务器 ID

若是没有再 my.cnf 里定义服务器 ID,虽然能够经过 CHANGE MASTER TO 来设置备库,但在启动复制时会遇到:

mysql> START SLAVE;
ERROR 1200 (HY000): The server us bit configured as slave; fix in config file or with CHANGE MASTER TO
复制代码

这个报错可能会让人困惑。由于咱们可能已经经过 CHANGE MASTER TO 设置了备库,而且经过 SHOW MASTER STATUS 也确认了,为何还会有这样的报错呢?咱们经过 SELECT @@server_id 能够得到一个值,要注意的是,这个值只是默认值,咱们必须为备库显式地设置服务器 ID。也就是在 my.cnf 里显示的设置服务器 ID。

3 对未复制数据的依赖性

若是在主库上有备库上不存在的数据库或数据表,复制就很容易中断,反之亦然。 对于前者,假设在主库上有一个 single_master 表,备库没有。在主库上对此表进行操做后,备库在尝试回放这些操做时就会出现问题,致使复制中断。

对于后者,假设备库上有一个 single_slave 表,主库没有。在主库上执行建立 single_slave 表的语句时,备库在回放该建表语句时就会出现问题。

对于此问题,咱们能作的就是作好预防:

  1. 主备切换时,尽可能在切换后对比数据,查清楚是否有不一致的表或库。
  2. 必定不要在备库执行写操做。

4 丢失的临时表

临时表和基于语句的复制方式不相容。若是备库崩溃或者正常关闭,任何复制线程拥有的临时表都会丢失。重启备库后,全部依赖于该临时表的语句都会失败。

复制时出现找不到临时表的异常时,能够作:

  1. 直接跳过错误,或者手动地建立一个名字和结构相同的表来代替消失的的临时表。

临时表的特性:

  1. 只对建立临时表的链接可见。不会和其余拥有相同名字的临时表的链接起冲突;
  2. 随着链接关闭而消失,无须显式的移除它们。

4.1 更好使用临时表的方式

保留一个专用的数据库,在其中建立持久表,把它们做为伪临时表,以模拟临时表特性。只须要经过 CONNETCTION_ID() 的返回值,给临时表建立惟一的名字。

伪临时表的优劣势 优点:

  1. 更容易调试应用程序。能够经过别的链接来查看应用正在维护的数据;

劣势:

  1. 比临时表多一些开销。建立较慢伪临时表会较慢,由于表的 .frm 文件须要刷新到磁盘。

5 InnoDB 加锁读致使主备数据不一致

使用共享锁,串行化更新,保证备库复制时数据一致。

某些状况下,加锁读能够防止混乱。假设有两张表:tab1 没有数据,tab2 只有一行数据,值为 99。此时,有两个事务更新数据。事务 1 将 tab2 的数据插入到 tab1,事务 2 更新 tab2。

两个事务更新数据,使用共享锁串行化更新

  1. 事务 1 使用获取 tab2 数据时,加入共享锁,并插入 tab1;
  2. 同时,事务 2 更新 tab2 数据时,因为写操做的排它锁机制,没法获取 tab2 的锁,等待;
  3. 事务 1 插入数据后,删除共享锁,提交事务,写入 binlog(此时 tab1 和 tab2 的记录值 都是 99);
  4. 事务 2 获取到锁,更新数据,提交事务,写入 binlog(此时 tab1 的记录值为 99,tab2 的记录值为 100)。

上述过程当中,第二步很是重要。事务 2 尝试去更新 tab2 表,这须要在更新的行上加排他锁(写锁)。排他锁与其余锁不相容,包括事务 1 在行记录上加的共享锁。所以事务 2 须要等待事务 1 完成。备库在根据 binlog 进行复制时,会按一样的顺序先执行事务 1,再执行事务 2。主备数据一致。

一样的过程,若是事务 1 在第一步时没有加共享锁,流程就变成:

两个事务更新数据,未使用共享锁串行化更新

  1. 事务 1 无锁读取 tab2 数据,并插入 tab1(此时 tab1 和 tab2 的记录值 都是 99);
  2. 同时,事务 2 更新 tab2 数据,先与事务 1 提交事务,写入 binlog(此时 tab1 的记录值为 99,tab2 的记录值为 100);
  3. 事务 1 提交事务,写入 binlog(此时记录值无变化);

mysqldump --single-transaction --all-databases --master-data=1 --host=server1 | mysql --host=server2 要注意的是,上述过程当中,事务 2 先提交,先写入 binlog。在备库复制时,一样先执行事务 2,将 tab2 的记录值更新为 100。而后执行事务 1,读取 tab2 数据,插入 tab1,因此最终的结果是,tab1 的记录值和 tab2 的记录值都是 100。很明显,数据和主库有差别。

建议在大多数状况下将 innodb_unsafe_for_binlog 的值设置为 0。基于行的复制因为记录了数据的变化而非语句,所以不会存在这个问题。

6 复制延迟过大

产生延迟的两种方式

  1. 忽然产生延迟,而后再跟上;
  2. 稳定的延迟增大

前者一般是因为一条执行时间过长的 SQL 致使,然后者即便在没有慢语句也会出现。

对于前者,咱们能够经过备库上的慢查询日志来进行优化。在备库上开启 log_slow_slave_statement 选项,能够在慢查询日志中记录复制线程执行的语句。

而对于后者,没有针对性的解决方案,只能经过各类方式提升备库的复制效率。而当咱们想去对备库作优化时,会发现,除了购买更快的磁盘和 CPU,并无太多的调优空间。只能经过 MySQL 选项禁止某些额外的工做以减小备库的复制。能够经过下面几种方式:

  1. 使用 InnoDB 引擎时,设置 innodb_flush_log_at_trx_commit 值为 2,来使备库不要频繁的刷新磁盘,以提升事务提交效率。
  2. 禁止二进制日志记录。把 innodb_locks_unsafe_for_binlog 设置为 1,并把 MyISAM 的 delay_key_write 设置为 ALL。要注意的是,这些设置是以安全换取速度,在将备库提高为主库时,记得把这些选项设置回安全的值。
  3. 拆分效率较低的复制 SQL,分离复杂语句中的 SELECT 和 UPDATE 语句,下降复制消耗,提升效率。

总结

  1. 复制问题要分清楚是 master 的问题,仍是 slave 的问题。
  2. master 问题找 binlog,slave 问题找 relaylog。
相关文章
相关标签/搜索