12-MySQL DBA笔记-MySQL复制

第12章 MySQL复制
本章将为读者讲述MySQL的复制技术,
首先,介绍最基础的主从复制,它是其他所有复制技术的基础,
接着再为读者讲述各种复制架构的搭建,
最后,列举了一些常见的复制问题及处理方式。
复制技术是大部分MySQL高可用技术的基础,熟练掌握各种复制架构有助于制定适合自己公司的高可用方案,
第13章将讲述MySQL的迁移、升级、备份和恢复,这些技能同样极大地依赖于对复制架构的理解。

12.1 基础知识
12.1.1 原理及注意事项
MySQL支持单向、异步复制,复制过程中一个服务器充当主服务器,而一个或多个其他服务器充当从服务器。
有时我们也称从库为从服务器或从实例,意义上大致是类似的,不需要进行细致的区分。
(1)复制的基本原理
在主库的二进制日志里记录了对数据库的变更,从库从主库那里获取日志,然后在从库中重放这部分日志,从而实现数据的同步。
基本步骤类似如下。
1)主服务器将更新写入二进制日志文件,并维护文件的一个索引以跟踪日志循环。
2)从库复制主库的二进制日志事件到本地的中继日志(relay log)。
3)从库重放中继日志。
将从服务器设置为复制主服务器的数据后,它将连接主服务器并等待更新过程。
如果主服务器失败,或者从服务器与主服务器之间失去了连接,那么从服务器将保持定期尝试连接,直到它能够继续侦听更新为止。
由--master-connect-retry选项控制重试间隔,默认时间为60s。
如果你想要设置链式复制服务器,那么从服务器本身也可以充当主服务器。
MySQL使用3个线程来执行复制功能,其中1个在主服务器上,另两个在从服务器上。
当从服务器发出START SLAVE命令时,从服务器将创建一个I/O线程,以连接主服务器并让它发送记录在其二进制日志中的语句。
主服务器可创建一个线程将二进制日志中的内容发送到从服务器中。该线程可以识别为主服务器上SHOW PROCESSLIST输出中的BinlogDump线程。
从服务器I/O线程读取主服务器BinlogDump线程发送的内容并将该数据复制到从服务器数据目录中的本地文件中,即中继日志。
第3个线程是SQL线程,由从服务器创建,用于读取中继日志并执行日志中所包含的更新。
由上可知,这样读取和执行语句将被分成两个独立的任务。每个从服务器都有自己的I/O和SQL线程。
即使SQL线程执行得很慢,远远落后于主库,但I/O线程仍然可以从主库上获取所有二进制日志的内容,
这样就可以允许主库清空二进制日志了,因为不再需要等待从库来读取二进制日志的内容。

(2)复制的用途
复制有很多用途,比如跨IDC备份数据,使用读写分离架构扩展读,在从库上进行备份,使用从库测试数据库版本升级,高可用自动故障冗余切换等。
生产中使用最广泛的用途无疑是进行数据备份,在备份过程中主服务器可以继续处理更新,并在主库不能提供服务的情况下接管服务。

(3)复制的注意事项
一般情况下,少量的从库,对于主库来说没有什么开销,但是如果部署了很多从库,就需要考虑从库对主库的影响了,网络带宽或I/O可能都会存在瓶颈。
如果只是传送最新的二进制日志到从库,那么从库一般不会对主库有冲击,但如果由于某种原因,需要读取高并发主库上旧的日志,那就可能会带来严重的性能问题,
因为主库要读取大量的旧日志,而这些日志没有被操作系统缓存,因此将导致主库I/O瓶颈,同时还有一个潜在的影响,会阻碍主库事务提交,
因为MySQL 的XA事务有其特殊性,在事务日志提交之前,需要确保二进制日志已写入。
复制架构中的从库一般用于扩展读,对于扩展写没有什么用处,复制对于频繁读和少量写的系统好处最大。
回答下面的问题应该能够帮助你确定复制是否和在多大程度上能够提高系统的性能。
1)系统上的读写比例是什么?
2)如果减少读取操作,一个服务器可以多处理多少写负载?
3)网络带宽可满足多少从服务器的需求?
由于目前MySQL5.1、5.5的复制是单线程的,所以复制可能会成为瓶颈,建议使用SSD来突破瓶颈。
复制的架构和配置应尽量保持简单。
复制有一些限制和坑,但大部分都可以避免,很多会触发问题的高级特性普通用户根本用不着。所以保持自身的数据库配置简单是最好的规避出现复制问题的方法。
比如,不要使用环状的复制架构,不要使用Blackhole引擎来实现复制,不要在配置文件内指定复制的过滤。
建议生产环境保持简单,所有主从都是完全复制过去,同步所有的数据和权限。
保持主从的完全一致,可以减少很多不必要的麻烦。
建议将从库配置为只读,因为应用程序可能会配置错误,对从库进行写操作,将会导致数据的不一致性,甚至丢失数据。
互为主从的环境,一定要保证同一时刻只写一个数据库。
单向复制是健壮性最强的复制架构,但在实际中,可能会为了方便切换,往往是互为主从的环境。
在这种情况下,一定要保证同一时刻只写一个数据库,以防止数据库同时写入相同的键值,导致主键冲突,复制失败。
有些人使用双向复制,互为主从的两个库更新不同的表,认为这样可以加速复制,
但实际上,双向复制并不能提高什么性能,服务器仍然要做同样的事情,只是锁的竞争更少些,因为源于另一个服务器的更新被序列化了,
由于单线程复制,可能还会导致I/O瓶颈问题更突出。
MySQL复制目前不支持主服务器和从服务器之间的任何锁定协议来保证分布式(跨服务器)更新的原子性。
这也意味着,在双向复制关系中,不应该同时写入主主配置的两个库,除非你确信任何顺序的更新都是安全的,或者除非你在客户端代码中知道怎样才能避免更新顺序错误。
互为主从的复制模式,需要小心处理好自增键及主键的冲突,程序和表的设计应确保不会导致键冲突。
由于存在很多约束和风险,所以,现实中的主主复制架构,我们一般采用的是Active-Passive模式而不是Active-Active模式。
主从架构,如果从库太多,或者同时有很多从库要求传输日志,那么可能会导致主库负载上升。可以解决的方案是再配置一个从库,专门用来传递日志给其他从库。
对于判断主从是否一致的问题,目前官方并没有一个成熟的解决方案,可以利用第三方的工具pt-table-checksum进行判断。

12.1.2 常用命令
我们可以使用SHOW BINARY LOGS查看当前主库的日志,在从库上执行SHOW SLAVE STATUS\G检查当前的复制状态,
在主库上执行SHOW PROCESSLIST显示当前连接过来的从库线程,综合使用如上命令,我们可以大概判断当前的复制情况。
一般配置主从的大致步骤具体如下。
1)如果当前已经有主从配置了,那么在从库上运行命令STOP SLAVE以停止复制。
2)在从库上运行CHANGE MASTER命令设定连接主库的信息,配置主从。
3)在从库上运行START SLAVE命令启动同步。
以下我们开始逐项介绍一些重要的命令。
在主服务器上,SHOW PROCESSLIST的输出看上去应该如下所示。
mysql> SHOW PROCESSLIST\G
*************************** 1. row ***************************
Id: 2
User: root
Host: localhost:32931
db: NULL
Command: Binlog Dump
Time: 94
State: Has sent all binlog to slave; waiting for binlog to be updated
Info: NULL
其中,线程2是一个连接从服务器的复制线程。该信息表示所有主要的更新都已经被发送到从服务器上了,主服务器正在等待更多的更新出现。
在从服务器上,SHOW PROCESSLIST的输出看上去应该如下所示。
mysql> SHOW PROCESSLIST\G
*************************** 1. row ***************************
Id: 10
User: system user
Host:
db: NULL
Command: Connect
Time: 11
State: Waiting for master to send event
Info: NULL
*************************** 2. row ***************************
Id: 11
User: system user
Host:
db: NULL
Command: Connect
Time: 11
State: Has read all relay log; waiting for the slave I/O thread to update it
Info: NULL
该信息表示线程10是同主服务器通信的I/O线程,线程11是处理保存在中继日志中的更新的SQL线程。
SHOW PROCESSLIST运行时,两个线程均是空闲的,都在等待其他更新。
请注意,Time列的值可以显示从服务器比主服务器滞后了多长时间。

1.SHOW MASTER STATUS、SHOW SLAVE STATUS命令解析
(1)SHOW MASTER STATUS
该命令用于提供主服务器二进制日志文件的状态信息,它需要SUPER或REPLICATION CLIENT权限,举例如下。
mysql> show master status;
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+-------------------------+------------+---------------------+-------------------------+
| mysql-bin.000360 | 310 | | |
以上命令显示了当前正在写入的二进制文件,以及当前的Position。
(2)SHOW SLAVE STATUS
该命令用于提供有关从库线程的关键参数的信息。
如果你使用的是mysql客户端发布此语句,则可以使用一个\G语句终止符来获得更便于阅读的竖向输出版面。
SHOW SLAVE STATUS\G的输出类似如下。
mysql> show slave status \G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 11.11.11.11
Master_User: replic_user
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.001672
Read_Master_Log_Pos: 315022991
Relay_Log_File: relay-bin.005035
Relay_Log_Pos: 315023136
Relay_Master_Log_File: mysql-bin.001672
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 315022991
Relay_Log_Space: 315023328
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
其中各参数及说明如下。
Master_Host:当前的主服务器主机。
Master_User:被用于连接主服务器的当前用户。
Master_Port:当前的主服务器接口。
Connect_Retry:--master-connect-retry选项的当前值。
Master_Log_File:I/O线程当前正在读取的主服务器二进制日志文件的名称。
Read_Master_Log_Pos:在当前的主服务器二进制日志中,I/O线程已经读取的位置。
Relay_Log_File:SQL线程当前正在读取和执行的中继日志文件的名称。
Relay_Log_Pos:在当前的中继日志中,SQL线程已经读取和执行的位置。
Relay_Master_Log_File:由SQL线程执行的包含多个近期事件的主服务器二进制日志文件的名称。
Slave_IO_Running:I/O线程是否被启动并成功地连接到主服务器上。
Slave_SQL_Running:SQL线程是否被启动。 以上Slave_IO_Running和Slave_SQL_Running在正常情况下应该均为Yes。
Replicate_Do_DB、Replicate_Ignore_DB: 使用--replicate-do-db和--replicate-ignore-db选项指定的数据库清单。
Replicate_Do_Table、Replicate_Ignore_Table、Replicate_Wild_Do_Table、Replicate_Wild_Ignore_Table:
使用--replicate-do-table、--replicate-ignore-table、--replicate-wild-do-table和--replicate-wild-ignore_table选项指定的表清单。
Last_Errno、Last_Error:多数最近被执行的查询返回的错误数量和错误消息。错误数量为0并且消息为空字符串,则意味着“没有错误”。
如果Last_Error值不是空值,它也会在从库的错误日志中作为消息被显示。
Skip_Counter:最近被使用的用于SQL_SLAVE_SKIP_COUNTER的值。
Exec_Master_Log_Pos:来自主服务器的二进制日志的、由SQL线程执行的、上一个时间的位置(Relay_Master_Log_File)。
主服务器的二进制日志中的 (Relay_Master_Log_File,Exec_Master_Log_Pos)对应于中继日志中的(Relay_Log_File,Relay_Log_Pos)。
Relay_Log_Space:所有原有的中继日志结合起来的总大小。
Until_Condition、Until_Log_File、Until_Log_Pos:在START SLAVE语句的UNTIL子句中指定的值。
Seconds_Behind_Master:是从库“落后”多少的一个指示。一般是基于同一集群内网的主从集群,此值应为0。
本字段用于测量从库SQL线程和从库I/O线程之间的时间差距,单位以秒计。
如果主服务器和从库之间的网络连接较快,则从库的I/O线程会非常接近主服务器,所以本字段能够十分近似地指示从库SQL线程比主服务器落后多少。
如果网络较慢,则这种指示不准确;从库SQL线程经常能赶上读取速度较慢的从库I/O线程,
因此,Seconds_Behind_Master的值经常显示为0,即使从库I/O线程落后于主服务器时也是如此。
换句话说,本列只对速度快的网络有用。
由于根据SHOW SLAVE STATUS\G的输出估算具体的主从差异时间可能会不准,
异常情况下Seconds_Behind_Master的值为NULL,或者显示不正常,所以生产环境的实际监控一般是在主从中配置一个心跳表,通过此心跳表来监控主从之间的时间差异。

2.CHANGE MASTER命令
这个命令在从库中执行,可以配置所要连接的主库,以及从哪里开始同步。
常用的语法如下:
CHANGE MASTER TO MASTER_HOST='11.11.11.11', MASTER_PORT=port, MASTER_USER='replic_user', MASTER_PASSWORD='your_password', MASTER_LOG_FILE='log file name', MASTER_LOG_POS=position;
我们可以在正在运行中的数据库从库中动态修改连接主库的信息。例如修改复制用户的密码。
mysql> STOP SLAVE; -- if replication was running
mysql> CHANGE MASTER TO MASTER_PASSWORD='new3cret';
mysql> START SLAVE; -- if you want to restart replication
没有必要指定未发生改变的参数(主机、接口、用户等)。
MASTER_HOST和MASTER_PORT指定了主库的IP和PORT。
MASTER_LOG_FILE和MASTER_LOG_POS指定了主库的二进制日志的名称和位置。
MASTER_USER和MASTER_PASSWORD指定了复制用户的账号和密码,将使用这个账号去连接主库,所以主库需要给予这个账号REPLICATION SLAVE的权限来复制数据。
CHANGE MASTER会删除所有的中继日志文件并启动一个新的日志,除非您指定了RELAY_LOG_FILE或RELAY_LOG_POS。在此情况下,中继日志将被保持。
CHANGE MASTER TO会去更新master.info和relay-log.info文件的内容。

3.START SLAVE和STOP SLAVE命令
我们常用的START SLAVE语句有3种。
(1)START SLAVE不带任何参数
不含选项的START SLAVE会同时启动两个从库线程。I/O线程从主服务器中读取查询,并把它们存储到中继日志中。SQL线程读取中继日志并执行查询。
START SLAVE要求SUPER权限。
如果START SLAVE成功地启动了从库线程,则会返回,不会出现错误。
但是,即使在此情况下,也有可能会出现这样的现象——服务器线程启动了,然后又停止了,START SLAVE对此不会发出警告。
(例如,因为它们没有成功地连接到主服务器上,或者没有能够读取二进制日志,或者出现了其他问题)。
必须检查从库的错误日志,查看是否有由从库线程产生的错误消息,或者使用SHOW SLAVE STATUS检查它们运行是否正常。
对于这种情况,我们在编写脚本的时候一定要留意。
(2)START SALVE启动单个服务器线程
START SLAVE IO_THREAD
START SLAVE SQL_THREAD
(3)START SALVE指定到某个位置自动终止
可以添加一个UNTIL子句,指定从库应启动并运行,直到SQL线程达到主服务器二进制日志中的一个给定点为止。
当SQL线程达到此点时,它会停止。
如果在该 语句中指定了SQL_THREAD选项,则它只会启动SQL线程。否则,它会同时启动两个从库线程。
一般情况下,我们仅操控SQL线程。
START SLAVE [SQL_THREAD] UNTIL MASTER_LOG_FILE = 'log_name', MASTER_LOG_POS = log_pos
如下的一个例子,我们使用START SLAVE SQL_THREAD UNTIL命令,把从库指向新的主库,新主库需要打开log_slave_updates。
假设有A、B、C、D 4个实例,A是主库,B、C、D是从库。现在需要调整架构为C、D是B的从库。我们可以按照如下的步骤进行调整。
1)分别关闭B、C、D从库的SLAVE。
STOP SLAVE;
2)查看主库A上的日志位置信息命令如下。
show master status;
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
| mysql-bin.000003 | 307827324 | | |
3)在所有从库B、C、D上运行命令。
START SLAVE SQL_THREAD UNTIL MASTER_LOG_FILE = ' mysql-bin.000003', MASTER_LOG_POS = 307827324;
等同步到指定的位置时,自动断开SQL线程,此时B、C、D上面的数据应该是一致的。都同步到了A库的某个日志点。
在B库上运行SHOW MASTER STATUS记录下日志的位置信息log file name、log_file_positiion。
在C、D从库运行如下命令,指向新的主库B。
mysql > STOP SLAVE;
mysql > RESET SLAVE;
mysql > CHANGE MASTER TO MASTER_HOST='11.11.11.11', MASTER_PORT=3306, MASTER_USER='replic_user', MASTER_PASSWORD='password', MASTER_LOG_FILE='log file name', MASTER_LOG_POS=log_file_positiion;
最后,在B库上运行START SLAVE命令,这样就可以调整A为主库,B为A的从库,C、D是B的从库。
主库可以运行如下命令显示从库。 SHOW SLAVE HOSTS;
mysql> SHOW SLAVE HOSTS;
MySQL 5.1版本需要在从库上配置好report_host、report_port,并需要重启从库,才能看到从库,这个过程比较繁琐,
但新的5.5版本已经更友善了,可以直接看到连接到主库的从库信息,命令如下。
mysql> show slave hosts;
| Server_id | Host | Port | Master_id |
| 13584 | | 3307 | 13582 | | 132110737 | | 3307 | 13582 |

12.1.3 参数设置
1.slave_exec_mode
复制冲突解决和错误检测可采用如下两种模式。
STRICT默认。
IDEMPOTENT忽略duplicate-key、no-key-found错误,一般在主主配置、环形复制等其他特殊情况下才使用,不推荐使用。
2.max_allowed_packet
默认的设置太小了,生产环境中建议配置大于16MB。如果太小了,可能会导致从库不能接收主库发过来的包,主从建议设置成一样的值。
如果你有大的BLOB字段,可能还需要增加这个阈值。
3.请不要使用过滤选项
请不要使用复制过滤参数,除非你真的明确知道你在做什么。即使要使用,也建议只在从库上进行设置。
不要使用binlog-do-db、binlog-ignore-db这两个参数,如果在主库上设置了复制相关的过滤参数,它们可能会导致你不能进行时间点恢复,还可能导致你丢失数据。
如果你只是因为I/O瓶颈,希望减少一些日志的写入,而临时禁用部分日志的写入,那么,建议你升级你的硬件。
binlog-do-db、binlog-ignore-db这两个参数的含义具体如下。
(1)binlog-do-db=db_name
告诉主服务器,如果当前的数据库(即USE选定的数据库)是db_name,应将更新记录到二进制日志中。其他所有没有明确指定的数据库都将被忽略。
如果使用了该选项,你应该确保只对当前的数据库进行更新。
一个不能按照期望来执行的例子:
如果用binlog-do-db=sales启动服务器,并且执行“USE prices;UPDATE sales.january SET amount=amount+1000;”,那么该语句将不会被写入二进制日志。
(2)binlog-ignore-db=db_name
告诉主服务器,如果当前的数据库(即USE选定的数据库)是db_name,那么不应将更新保存到二进制日志中。
一个不能按照期望来执行的例子:
如果用binlog- ignore-db=sales启动服务器,并且执行“USE prices;UPDATE sales.january SET amount=amount+1000”,那么该语句将写入二进制日志。
建议不要使用replicate-do-db、replicate-ignore-db参数,这两个参数是在从库上进行设置的,这两个参数类似上面的两个参数,其实并不符合我们的预期。 *_do_db和*_ignore_db参数其实都仅仅只是针对当前的数据库,也就是说,如果我们USE到指定的库,然后执行了一条更新其他库的SQL,那么这些参数将都不 起作用。

下面让我们详细解析下在从库上设置过滤的一些参数,我们来看下--replicate-ignore-db=db_name参数。
(1)--replicate-ignore-db=db_name
这个选项告诉从服务器不要复制默认数据库(由USE来选择)为db_name的语句。
要想忽略多个数据库,则应多次使用该选项,且每个数据库使用一次。
如果正在进行跨数据库更新并且不想复制这些更新,那就不要使用该选项。
如果使用--replicate-ignore-db=sales启动从服务器,并且在主服务器上执行下面的语句,那么UPDATE语句是不会复制的。
USE prices; UPDATE sales.january SET amount=amount+1000;
如果需要跨数据库更新,则应在从库上使用--replicate-wild-ignore-table=db_name.%。
下面我们再来看其他两个参数。
(2)--replicate-ignore-table=db_name.tbl_name
它将告诉从服务器线程不要复制更新指定表的任何语句(即使该语句可能更新其他的表Y)。
要想忽略多个表,则应多次使用该选项,且每个表使用一次。
同-- replicate-ignore-db对比,该选项可以跨数据库进行更新。
所以,如果我们需要在从库上忽略一些表的复制,或者使用--replicate-ignore-table参数,
或者使用如下的--replicate-wild-ignore-table=db_name.tbl_name参数,它可以使用通配符进行匹配,功能也更强大。
(3)--replicate-wild-ignore-table
它告诉从服务器线程不要复制匹配给出的通配符模式的语句。
要想忽略多个表,则应多次使用该选项,且每个表使用一次。
该选项可以跨数据库进行更新。
例如:--replicate-wild-ignore-table=foo%.bar% 表示不复制数据库名以foo开始和表名以bar开始的表的更新。
如果表名模式为%,则可匹配任何表名,选项也适合数据库级语句(CREATEDATABASE、DROP DATABASE和ALTERDATABASE)。
例如,使用--replicate- wild-ignore-table=foo%.%时,如果数据库名匹配模式foo%,则不复制数据库级语句。
有时我们会选择忽略复制mysql库来限制权限,不让主库的权限复制到从库,但这可能会带来很多后续问题,比如存储过程、events的权限问题等。
提示:强烈建议不要设置复制过滤选项。
如果你一定要使用复制过滤,那么建议采用replicate-wild-*选项,它在绝大部分场合更适用。
如果我们需要临时禁用复制特性,那么我们还可以在会话级设置变量SET sql_log_bin=0,使当前的一些操作不被复制到从库。

4.slave_compressed_protocol
请慎重对待跨集群复制。
跨集群配置的时候,可启用slave_compressed_protocol=1压缩传输数据,需要在主库进行压缩,在从库解压缩,由于压缩需要额外的CPU消耗,所以需要留意CPU资源是否充裕。
5.read-only
可以考虑为从库配置read-only选项,以保障数据安全,要注意SUPER权限的用户仍然可以写数据库。
6.slave_net_timeout
由于生产环境网络异常,可能会导致复制异常。
即使SHOW SLAVE STATUS的输出正常,但此时可能也已经停止复制了,slave_net_timeout的默认设置是1小时,
因此很难避免因网络问题导致的复制异常中断,特别是跨IDC的复制,建议将其设置小于1分钟。
7.--slave-skip-errors
通常情况下,当出现错误时复制会停止,这个选项可以给你一个机会手动解决数据中的不一致性问题。
当语句返回slave-skip-errors所列的错误时,该选项将会告诉从服务器SQL线程继续复制。
如果你不知道为什么会发生复制错误,那么请不要使用该选项。
如果复制设置和客户程序中没有Bug,并且MySQL自身也没有Bug,那么应该是不会发生停止复制的错误的。
滥用该选项会使从服务器与主服务器不能保持同步,将会导致数据不一致。
对于错误的代码,你应使用从服务器错误日志中错误消息提供的代码和SHOW SLAVE STATUS输出的错误代码。
如下设置将忽略1062和1053错误。--slave-skip-errors=1062,1053
错误代码的具体释义请参考官方文档,这里不再赘述。
也可以(不建议)设置all值忽略所有的错误消息,例如: --slave-skip-errors=all
8.skip-slave-start
skip-slave-start可以在命令行下或配置文件中使用,目的是在MySQL启动的时候不要启动Slave,这在某些故障情况下很有用,
比如宕机后,有时我们希望先观察下情况,再启动Slave。
或者有时我们希望能够进行手动调整,自己控制启动Slave的时刻,比如配置延时的从库。
或者,有时我们使用START SLAVE THREA UNTIL 命令,让从库成段地处理已复制的查询,
使用--skip-slave-start选项来启动从库,可以防止当从库启动时,SQL线程开始运行。
最好在一个选项文件中使用此选项,而不是在命令行中使用,这样,即使发生了意料之外的服务器重新启动,它也不会被忘记。

12.1.4 配置文件
默认情况下,中继日志使用host_name-relay-bin.nnnnnn形式的文件名,其中host_name是从服务器主机名,nnnnnn是序列号。
用连续的序列号来创建连续的中继日志文件,从000001开始。
从服务器跟踪索引文件中目前正在使用的中继日志。中继日志索引文件名默认为host_name-relay-bin.index。
默认情况下,可在从服务器的数据目录中创建这些文件。
可以用--relay-log和--relay-log-index服务器选项覆盖默认文件名。
强烈建议指定默认文件名,即日志文件名不要有主机名前缀,文件名中不要带有主机名是为了方便迁移操作和故障处理。
中继日志与二进制日志的格式相同,并且可以用mysqlbinlog读取。
SQL线程执行完中继日志中的所有事件并且不再需要中继日志之后,会立即自动删除它。
没有直接删除中继日志的机制,因为SQL线程可以负责完成。
从服务器在数据目录中会另外创建两个小文件。这些状态文件的默认名为master.info和relay-log.info。
它们包含了SHOW SLAVE STATUS语句的输出所显示的信息。
状态文件保存在硬盘上,因此从服务器关闭时不会丢失状态文件。
下次启动从服务器时,读取这些文件以确定它已经从主服务器读取了多少二进制日志,以及处理自己的中继日志的程度。
由I/O线程更新master.info文件。master.info文件中的行和SHOW SLAVE STATUS显示的列的对应关系如表12-1所示。
表12-1 master.info文件中的行和SHOW SLAVE STATUS显示的列的对应关系
由SQL线程更新relay-log.info文件。relay-log.info文件中的行和SHOW SLAVE STATUS显示的列的对应关系如表12-2所示。
图12-2 relay-log.info文件中的行和SHOW SLAVE STATUS显示的列的对应关系
当备份从服务器的数据时,你还应备份这两个小文件及中继日志文件。它们可用来在恢复从服务器的数据后继续进行复制。
如果丢失了中继日志但仍然有relay- log.info文件,那么你可以通过检查该文件来确定SQL线程已经执行的主服务器中二进制日志的程度,例如如下命令。
cat relay-log.info
/usr/local/mysql/log//relay-bin.015161
401289501
mysql-bin.006800
401289356
如上信息表示,当前从库正好执行到主库日志文件mysql-bin.006800,执行的位置是401289356。
然后我们可以用Master_Log_File和Master_LOG_POS选项执行CHANGE MASTER命令来告诉从服务器需要重新从该点读取二进制日志。
当然,要求二进制日志仍然在主服务器上,例如如下命令。
CHANGE MASTER TO MASTER_HOST='11.11.11.11', MASTER_PORT=3306, MASTER_USER='replic_user', MASTER_PASSWORD='your_password', MASTER_LOG_FILE='mysql-bin.006800', MASTER_LOG_POS=401289356;
MySQL 5.1的中继日志和relay-log.info、master.info文件并不是crash-safe的,
也就是说,它们默认是不会实时刷新到磁盘的,那么在发生崩溃灾难的情况下,文件记录的信息可能是错误的,将会导致复制异常。
如果使用的是MySQL 5.5,那么可以设置如下选项:
sync_master_info = 1
sync_relay_log = 1
sync_relay_log_info = 1
注意:设置如上的参数将会带来很多开销。对于高并发写操作很频繁的业务,建议不要设置如上参数,否则将会严重影响性能。安全和效率往往不能兼得。
MySQL 5.5还有一个选项relay_log_space_limit,这个选项设置了所有中继日志可以使用的空间。
意思是如果中继日志占用的空间超过了这个变量设置的阈值,那么 I/O线程就会关闭,等待SQL线程应用日志释放空间。

12.1.5 复制模式
1.概述
MySQL可以使用如下3种复制模式。
1)基于SQL语句的复制(statement-based replication)
2)基于行的复制(row-based replication)
3)混合模式复制(mixed-based replication)
对应地,我们可以设置3种类型的二进制日志格式,使用参数--binlog-format=type进行设置。
type的值可以是如下的值。
1)STATEMENT:基于语句的日志。
2)ROW:基于行记录的日志。
3)MIXED:混合日志模式,即默认是基于语句的日志,当需要的时候,将会使用基于行的日志。
MySQL在日志模式的选择上不同的版本默认值可能会不一样,建议在生产环境中使用MIXED,即混合模式的日志,一般情况下,它可以工作得很好。
我们可以在运行时动态修改日志格式,命令如下。
mysql> SET GLOBAL binlog_format = 'STATEMENT';
mysql> SET SESSION binlog_format = 'STATEMENT';
在复制环境中,从库可以进行自动调整,以适应主库的row-base语句。
下面将详细介绍各种复制模式及其优缺点。

2.基于SQL语句的复制
基于SQL语句的复制(statement-based replication),也就是逻辑复制,MySQL 3.23开始支持。
基于语句的复制,复制将执行主库上所执行的语句,也就是说,在从库上执行的语句和在主库上执行的语句是一样的。
基于SQL语句的复制,其优点具体如下。
1)相对于基于行记录的日志,它更简单,也更容易实现。
2)数据库的二进制日志更小,因此,主从库之间传输的日志数据也更小。
3)二进制日志的可读性更好,我们可以使用mysqlbinlog方便地读取二进制日志。
4)更有利于排查问题,从库上执行的是和主库一样的语句。
基于SQL语句的复制,其缺点具体如下。
1)有些操作将无法正确复制到从库,因为对于主库的操作,并不仅仅取决于SQL文本,还有一些不确定性的因素。
不确定性的因素有如下之点:
带LIMIT子句但没有使用ORDERBY的操作。
修改数据的查询语句里用到了返回不确定性值的自定义函数和存储过程。
一些函数在主从上执行的结果不一样,如UUID()、SYSDATE()、RAND()、VERSION()…… 还有很多,这里就不一一列举了。
2)从库需要锁住更多的记录,
比如INSERT…SELECT…操作会需要锁定比基于行记录的复制多得多的记录,
比如UPDATE一个表,如果没有索引,就会锁住整 个表。
3)复杂的、代价昂贵的语句需要在从库上再次执行,也就是运行整个语句,这样可能会比较慢,
而基于行记录的复制,只需要修改指定的记录即可,不需要执行整条语句。
4)对于非核心特性的功能支持力度有限,存储过程和触发器相关的复制Bug较多。

3.基于行的复制
MySQL 5.1开始支持基于行的复制(row-based replication),它的适用范围更广泛,也可靠得多。
基于行记录(row-based)的复制,其格式比较难以阅读,即使MySQL官方一直在改进其可读性。
基于语句的复制很难处理各种高级特性,如视图、存储过程、触发器。如果你需要应用各种高级特性,那么推荐你使用基于行的复制模式。
基于行的复制优点具体如下。
1)所有改变均被复制,对比基于语句的复制,这是一种更安全、更精确的复制。
2)更少的锁定记录。
3)对于存储器、触发器、自定义函数的特性也完善支持。
4)二进制日志更有利于进行数据恢复,因为二进制日志里记录了数据的详细变更信息。
5)更容易发现数据的不一致。比如主库中更改了1笔记录,而从库中不存在这笔记录,那么基于行记录的复制会报错而基于语句的复制则不会报错。
基于行的复制缺点具体如下。
1)产生更多的二进制日志数据。
2)二进制日志不易阅读,不方便使用mysqlbinlog解读日志。
3)要求主从表结构一致,这样就限制了它的灵活性,因为生产环境有时需要临时修改从库的表结构,提升从库为主库。

4.混合日志模式
笔者个人的建议是使用混合模式,即binlog_format=mixed,默认是使用基于语句的复制,
但一旦MySQL检测到满足了一定的条件,那么它就会自动切换到基于行的复制。
例如,在函数内使用了UUID(),更新了有自增列的表且调用了触发器或存储过程等情况,将会自动切换到基于行的复制。

12.1.6 复制兼容性
MySQL目前已经有了多个版本,有时我们需要在各个版本之间进行复制。
如下是一些复制兼容性的注意事项。
建议从库的版本高于主库,绝大部分情况下MySQL都支持此类复制。但也可能会碰到有些主库的语法,反而不支持更高版本的从库。
从库建议使用主版本的最新版。
主从之间不要跨越两个大版本号,那样可能会出问题,比如从MySQL4.1复制到MySQL5.1,为了降低风险,可以考虑在中间插入一个MySQL5.0版本的从库以避免一些意外问题的发生。
高版本到低版本的复制可能是可行的,但官方不保证支持。
升级生产环境之前,建议配置一个更高版本的从库运行一段时间,以验证复制功能。
即使主从复制数据正常,也不代表稳定性良好,在性能压力、网络异常的情况下,仍然可能会导致复制异常。
生产环境大版本有差异的复制架构不要长期并存,应该尽量调整到一致,以免后续碰到其他问题。

12.2 配置主从复制
对于未上线的主机,即在主库没有任何写入的情况下,可以采用如下方式配置主从。
1)在主从主机上部署好MySQL,并在主库上启用二进制日志,
注意主从server-id必须不一样,server-id的设置可以使用IP的后8位加上端口(port)等其他标识信 息,主库的配置文件类似如下。
[mysqld]
log-bin=mysql-bin
server-id=1
2)记录主库的日志文件名File和日志文件Position,命令如下。
mysql> show master status;
| mysql-bin.000362 | 310 | | |
3)在主库中创建复制账号,允许从库来访问,命令如下。
grant replication slave,replication client on *.* to replic_user identified by 'xxxxxxxxxxx';
如果账户仅用于复制,那么replication slave的权限就足够了,但在本地查看从库(slave server)信息,还需要replication client权限。
4)从库编辑配置文件,运行命令,配置主从。
编辑从库的配置文件。
下面将展示一个从库的配置文件示例:只有server-id必须设置,其他选项是可选的,具体命令如下。
log_bin = mysql-bin
server_id = 2
relay_log = /path_to_mysql_log/mysql-relay-bin
log_slave_updates = 1
read_only = 1
其中的参数及其说明如下:
log_bin=mysql-bin:建议主从配置一样的名字,不然在以后的配置中,处理问题会复杂很多。
log_slave_updates=1:log_slave_updates决定了是否将从主库接收的更新写入从库自身的二进制日志里。
将这个值设置为1,是方便以后以将这个从库提升为主库后,根据需要再配置一个从库,也方便数据恢复。
我们可以设想如下的场景。
如果主从复制的架构,主库提供服务,从库每天凌晨备份。
如果你的生产环境从库log_slave_updates是关闭的。
那么主库宕机后不能启动,你需要把数据库流量切换到从库,此时你需要在新的主库的基础上再制作一个从库。
但是,请注意,你不能用从库的备份转储文件 (dump文件)来做从库,因为凌晨备份的文件从凌晨到主库宕机这个时间段的日志并没有写入从库的日志,
如果你使用这个转储文件,将会丢失很多数据,那么你需要在线重新导出一份数据来制作从库。
如果你设置了log_slave_updates,那么从库的日志里就包含了所有时刻的数据更改,你就可以使用从库凌晨的备份文件在其他机器上直接制作从库了。
当然,设置这个变量也有弊端。如更大的I/O写入,不容易发现错误等。
设置了log_slave_updates可能不易发现错误,比如应用程序误写从库时,我们不能及时发现,因为我们可能会以为这是正常的更新。
为了安全,我们需要在从库上设置read_only选项,设置了read_only=1之后,将只有SUPER权限的用户才可以修改数据。
在从库上执行如下语句,其中MASTER_LOG_FILE和MASTER_LOG_POS是第二个步骤记录的值。
mysql > CHANGE MASTER TO MASTER_HOST='11.11.11.11', MASTER_PORT=3306, MASTER_USER='replic_user', MASTER_PASSWORD='xxxxxxxxxxx', MASTER_LOG_FILE=' mysql-bin.000362', MASTER_LOG_POS=310;
注意:千万不要使用在配置文件里指定master_host、master_port的方式,这些配置只在第一次启动MySQL时才生效。
5)在从库上执行如下命令启动slave。
mysql > start slave
6)在从库上确认复制正常。
mysql> SHOW SLAVE STATUS \G;
Slave_IO_Running: Yes S
lave_SQL_Running: Yes
Seconds_Behind_Master 0 -------------- 前两项应该都是Yes。Seconds_Behind_Master应该不是NULL。

12.3 配置主主复制
配置为主主复制,需要解决的主要问题是自增键/主键冲突。
当将多个服务器配置为复制主服务器时,如果要使用自增列(AUTO_INCREMENT),那么应采取特殊的步骤以防止键值冲突,否则插入行时多个主服务器会试图使用相同的自增列值。
服务器变量auto_increment_increment和auto_increment_offset可以帮助协调多主服务器复制和自增列。
其中,auto_increment_increment用于控制自增列值增加的间隔。
auto_increment_offset用于确定自增列值的起点。
假设有两台主机A、B,它们互为主从,那么配置可以如下。
A主机: auto_increment_increment=3 auto_increment_offset=1
B主机: auto_increment_increment=3 auto_increment_offset=2
我们还需要注意,除了自增字段不能互相冲突之外,所有表的键值也不能互相冲突,同一时刻的操作需要保证不会插入相同的键值。
还要留意复制的时序问题,一定要确保任一时刻只写一个库,主主复制更多的是为了故障冗余而不是为了能够多点写入。
一般配置为Active-Standby,而不是 Active-Active。
一般而言,配置为主主复制会导致维护更加复杂,可能还会带来隐患,需要更完善的监控措施和自动化手段。
配置主主复制的步骤这里不再赘述,对每个库分别执行配置主从复制的步骤即可。

12.4 配置级联复制、环形复制
(1)配置级联复制
假如需要配置成A→B→C→D→E这样的形式,箭头表示复制到,那么可按如下步骤进行。
1)首先打开各实例的log_slave_update选项,首尾两个实例也可以不用打开。
2)确保各主机的server-id不同。
3)配置每一对主从,A→B,B→C,C→D,D→E。
注意:节点越多,健壮性越差,建议不要超过4~5个节点。
(2)环形复制
有一个现象需要留意:如果E又复制到A,就会成为环形复制,可以实现多点写入,此时也需要和“配置主主复制”一样关注键值冲突等问题。
环形复制存在一个问题,如果某个节点被摘下,那么这个节点的写入事件将会在环内永远循环。
因为只有最开始发起事件的节点才能过滤这类事件,所以摘下节点之前,应该确保已停止对其写入。
MySQL 5.6实现了GTID,这点大大提高了链式复制的健壮性。
有兴趣的同学可以参考http://dev.mysql.com/doc/refman/5.6/en/replication-gtids-concepts.html。

12.5 跨IDC复制
跨IDC复制架构的部署与单机房部署链式复制(级联复制)的从库并没有区别,
但由于网络的不稳定,可能会导致复制的不稳定,维护代价较高,而且可能需要外网IP才能进行复制,降低了安全性。
但现实中,这种架构也有人使用,相对于使用应用程序实现的数据同步,数据库在某种程度上成本更低,也更容易确保数据的一致性。
下面将简述一些跨IDC进行复制的注意事项。
跨IDC的复制,建议还是采用普通的主从架构,而不要采用链式的复制架构,简单的主从架构更稳健。
尽量只在中心主库进行写入,其他机房只用于读,这样既可以简化架构,也可以避免多点写入带来的维护一致性的难题。
如果是M-M的架构,也应该将一个机房作为备用(Standby),仅作容灾。
数据量较大的时候,网络可能会成为瓶颈,建议使用混合日志的复制模式。可在从库中设置slave_compressed_protocol=1压缩传输数据,此选项可进行动态设置。
由于跨IDC的主从复制,重新搭建代价比较大,在明确知道数据库出现何种错误时,可以忽略此错误,
可使用“slave-skip-errors=error_code1,error_code2...|all”,但不要滥用,否则容易导致主从不一致而不自知。
由于跨IDC的复制,网络可能会不稳定,应用程序应该处理网络延时对用户体验的影响。

12.6 多主复制
关于多主复制,MySQL目前可以实现的思路和方法如下。
1)使用一些开源的工具,如tungsten-replicator。
2)自己写脚本对不同的主库进行轮询,获取日志,要跟踪每个主库的位置,此种方式比较复杂。
3)MySQL5.7开始支持多主。可参考https://www.percona.com/blog/2013/10/02/mysql-5-7-multi-source-replication/ 。

12.7 延时复制
MySQL同步在快速的网络中是毫秒级的,如果有误操作,从库也会马上变更,对于一些频繁进行,而没有经过严格测试的升级,可能会带来风险。
可考虑配置一个延迟复制的副本,以改善故障情况下的可恢复性。
MySQL5.6已经可以支持延迟复制,如果是5.1版本,可以用Percona公司出品的一个工具pt-slave-delay来实现延时复制。
下载地址为wget percona.com/get/pt-slave-delay
安装步骤此处省略。
语法格式为 pt-slave-delay [OPTION...] SLAVE-HOST [MASTER-HOST]
选项值一般可以用默认的,默认是延迟1小时。
下是一个设置延时的例子。
pt-slave-delay u=xxxx,S=/tmp/mysql.sock,p=password --delay 1m --interval 15s --run-time 10m --log /path/to/delay.log – daemonize
以上命令表示后台运行这个工具10分钟(默认是永久运行的),从库保持一直滞后主库1分钟,间隔15秒每检查一次,那么理论上是延迟了1分钟15秒。
延时复制的原理为检查主库的日志到了哪里了(可以用SHOW SLAVE STATUS命令查看中继日志),对比已经应用的日志,就知道延迟的时间了。
每隔1分钟检查一次(默认),不断启动、关闭replication SQL thread来保持主从一直延时固定的时间。
如果正在运行这个工具,那么按Ctrl+C退出后,它是友好地退出的,意思是它会启动复制SQL线程。

12.8 半同步复制
MySQL 5.5开始支持半同步复制(semi-syncreplication),半同步复制提供了更好的灾难恢复性。
半同步的原理是,主库和它的从库都启用半同步特性,
当一个从库连接主库时要标识自己是否支持半同步,如果主库启用了半同步,且拥有至少一个半同步从库,
那么一个事务提交会阻塞直到确认至少一个半同步从库已经“接收到事务事件(event)”为止,否则会发生一个“超时”。
半同步从库在写入事件到中继日志(relay log)时,刷新到磁盘后才确认“接收到事务事件”。
如果发生一个“超时”,即没有任何一个半同步从库确认“接收到事务事件”,那么主库将自动切换到异步复制模式。
当至少一个半同步从库追赶上主库,主库又会自动切换到半同步模式。
这里的“半同步”,可以按如下这样理解。
对于传统的异步同步,主库写事务事件到二进制日志里,从库索取主库日志,这还不能确保事务事件被传送到从库。
而对于全同步复制(fully synchronous replication),主库提交事务,必须等待从库也成功提交这个事务,才能完成这个事务,这样容易造成事务的延迟。
所以,出现了半同步,半同步是介于异步和全同步之间的同步。
需要留意到是,半同步对于网络的要求很高,它仅适用于高速内网。
虽然MySQL 5.5的半同步表现不佳,但是,据MySQL官方文档称,在新的5.7版本中,它已经得到了改善。

12.9 在线搭建从库
我们有多种方式可以在数据库提供服务的时候搭建从库,而不影响线上数据库或对其影响很小。
在线搭建从库一般可分为两类,一种是在操作系统下做快照,另一种是利用自带的备份工具mysqldump制作备份。
如下记录的是主从配置的一些常规步骤,一些基本的设置,比如参数的设置,这里将不再赘述,如果不加以说明,那么我们备份的库都是InnoDB引擎的表。

12.9.1 操作系统下对打包文件配置主从
1.已经有一主一从,增加一个从库
如果我们已经有了主从库,那么另外再搭建一个从库会比较简单,大概的步骤如下所示。
1)关闭从库。
2)打包相关文件到另外一台主机。
包括数据文件,如ibdata*、InnoDB事务日志文件ib_logfile*、master.info文件、relay-log.info文件和my.cnf配置文件。
3)在新的数据库主机上配置相应的参数,注意server-id不要和其他数据库实例相同。
4)一般来说,master.info的信息和relay-log.info的信息是一致的,你可以直接删除relay-log.info文件,重新启动,新的从库会按照master.info里的信息重新同步数据库。
5)一些情况下即使正常关闭了数据库,也可能存在信息不一致的情况,relay-log.info里记录了当前应用到数据库主库的二进制日志的位置,
这个值不同于 master.info里记录的当前读取到的主库日志的位置,这种情况下,我们可以删除master.info文件,然后重新启动数据库实例,
并按照relay-log.info里记录的信息,运行 CHANGE MASTER命令重新同步主库的数据。

2.仅有主库,增加一个从库
如果我们只有一个主库,这个时候,我们希望制作从库,而主库正在提供服务,我们还希望对主库的影响最小,那么我们可以采用如下的方式制作从库,
注意,这种方式仅适合MyISAM引擎的表,对InnoDB数据库不要使用这种方式制作从库。
1)主库赋予从库访问权限。
mysql> GRANT REPLICATION SLAVE ON *.* TO 'replic'@'xxx' IDENTIFIED BY 'xxxxxxxx';
2)主库施加全局读锁,禁止更新和提交数据,并记录当前主库二进制日志的位置信息。
FLUSH TABLES WITH READ LOCK;
SHOW MASTER STATUS;
3)另外打开一个会话,打包文件,一般情况下我们需要打包数据文件ibdata*和日志文件ib_log*,比如使用tar命令进行打包。 tar cvf data.tar *
4)打包所有需要的文件后,在主库上进行解锁。 UNLOCK TABLES;
5)将打包后的文件传递到远程主机,并删除多余的文件,InnodB的数据文件ibdata*和日志文件ib_log*需要保留,配置文件my.cnf可能也需要保留,如下命令将远程传输文件。
scp data.tar [email protected]:/home/mysql/
6)启动从库,使用change命令配置要从哪个主库进行同步,并启动slave。
CHANGE MASTER TO MASTER_HOST='11.11.11.11', MASTER_PORT=3306, MASTER_USER='replic_user', MASTER_PASSWORD='yourpassword', MASTER_LOG_FILE='mysql-bin.000084', MASTER_LOG_POS=521259880;
show salve status \G
start slave;
show salve status \G
以上的MASTER_LOG_FILE和MASTER_LOG_POS就是第2)个步骤记录的主库二进制日志的位置信息。
注意FLUSH TABLES WITH READ LOCK这条语句必须等待其他查询语句的完成,所以可能会耗时很长才执行完这条语句,甚至超时退出。
以上制作从库的方式,对于都是MyISAM引擎表的数据库比较适用,
但InnoDB的后台进程即使是施加了全局锁,在tar打包文件的过程中,也仍然会去写InnoDB的数据文件,可能最终的数据文件并不能被用作从库,或者不能启动,
所以,请不要对InnoDB数据库使用这种方式制作从库。
如果不使用tar命令,而是使用操作系统下的快照技术,那么对于InnoDB引擎的数据库采用如上的方式也可以制作从库,我没有去验证过,但理论上是可行的。
因为我们在施加全局锁的时候,可以获取数据库文件的一个快照,
但要注意,所有的文件都应该放在同一个分区里,这种方式下获得的快照所有文件都将是一致的。

12.9.2 利用mysqldump制作从库
1.在主库上执行mysqldump制作从库
如果我们希望在主库上导出所有数据,制作从库,那么我们可以在主库上运行mysqldump命令先制作一个备份文件。
mysqldump的命令类似如下。
mysqldump --flush-logs --master-data=2 --single-transaction --hex-blob -R -f --all-databases > /path/to/dir/databases.sql
我们通常称mysqldump导出的数据文件为转储文件或dump文件,
--master-data=2 会生成被注释掉的CHANGE MASTER TO语句,存储在转储文件里,我们可以利用这个信息来创建从库。
--master-data的默认值是1,会生成自动执行的语句,由于我们一般不希望自动执行,所以我们将该值设置为2。
--single-transaction参数表示制作一个一致性的备份集,对于InnoDB,制作一致性备份集的时候不会锁表,仍然可以读写数据,这点对于在线备份很重要。
对于 MyISAM表,仍然会需要锁表。
mysqldump --flush-logs --master-data=2 --single-transaction --hex-blob -R -f --all-databases这个命令,
它只是在一开始的瞬间会请求锁表,所以它对系统的影响很小。
对于--master-data=2,如果不加--single-transaction参数,那么它在自动启用--lock-all-tables备份的过程中会锁表。
实际创建数据库从库的步骤具体如下。
1)部署好从库实例,此时数据为空。
2)在主库上执行mysqldump命令,将数据导出为SQL文件。
3)在从库上导入此SQL文件。
4)配置主从。
根据转储文件(SQL文件)的CHANGE MASTER语句提供的信息,可以生成相应的CHANGE MASTER命令,并在从库中执行。
我们也可以使用另外一种方法,在主库上运行命令“FLUSH TABLES WITH READ LOCK;”
然后使用mysqldump导出数据,利用此转储文件来制作从库,这种方式不仅对MyISAM有效,对于InnoDB也有效,具体步骤如下。
1)在主库上运行命令锁表、备份。
FLUSH TABLES WITH READ LOCK;
SHOW MASTER STATUS;
记录SHOW MASTERSTATUS命令的输出结果。
此时,不要关闭如上的会话连接。
另外再开一个会话,运行mysqldump命令备份。
mysqldump -uroot -p --all-databases > /a/path/mysqldump.sql
mysqldump命令执行完毕之后,在主库上原来的会话连接里,运行如下的命令解锁。
UNLOCK TABLES;
2)向从库导入dump文件。
如果之前启动了SLAVE,则关闭SLAVE。 STOP SLAVE;
运行如下命令导入数据。 mysql -uroot -p < mysqldump.sql
运行如下命令,修改要连接的主库信息。
RESET SLAVE;
CHANGE MASTER TO MASTER_LOG_FILE=’ mysql-bin.000001’ , MASTER_LOG_POS=98;
START SLAVE;
以上MASTER_LOG_FILE和MASTER_LOG_POS的信息就是上述第1)个步骤中运行SHOW MASTERSTATUS记录的值。
3)运行命令“SHOW SLAVE STATUS;”检查复制状态,以下两项的值应该都是YES。
Slave_IO_Running: Yes
Slave_SQL_Running: Yes

2.在从库上执行mysqldump制作从库
如果不能关闭从库,那么我们一般采取关闭Slave的SQL线程,然后导出数据的方式制作从库。
原理: 关闭从库的复制SQL线程后,从库将不再被更新,这个时候,可以认为我们获得了一个一致性的快照。
关于这个快照和原来主库的主从关系,我们可以运行SHOW SLAVE STATUS\G命令来查看。具体步骤如下。
1)关闭Slave的SQL线程,并获取SHOW SLAVE STATUS的信息。
mysql> STOP SLAVE SQL_THREAD;
mysql> SHOW SLAVE STATUS;
2)对于SHOW SLAVE STATUS命令的输出,我们关注的是如下两项。
Relay_Master_Log_File
Exec_Master_Log_Pos
新的从库应该从主库的如上位置开始重新同步。
3)导出数据库,命令如下。
shell> mysqldump –master-data=2 – all-databases > dumpfile
4)重新启动Slave,命令如下。 mysql> START SLAVE;
5)向新的slave机器导入数据,命令如下。 shell> mysql < dumpfile
6)对新的slave实例,运行如下命令恢复同步,file_name、file_pos就是我们第2)个步骤中记录的Relay_Master_Log_File和Exec_Master_Log_Pos的值。
mysql> CHANGE MASTER TO MASTER_LOG_FILE = 'file_name', MASTER_LOG_POS = file_pos;

12.10 配置日志服务器
如果配置主从,从库需要读取的是比较旧的日志,而这些日志没有被主服务器操作系统缓存,必须从磁盘中进行读取,
那么可能会导致大量的磁盘读写,从而严重影响数据库繁忙的生产系统,
可以考虑的办法是不要直接从主库中进行日志传递,而是专门搭建一个日志服务器。
配置完日志服务器之后,把从库索取的日志文件放到日志服务器中,然后再从这个日志服务器进行同步。
关于日志服务器的配置,可参考http://mysqlsandbox.net ,
我们也可以使用日志服务器来进行时间点恢复,利用复制来进行时间点恢复而不是用mysqlbinlog来进行, 注意原因如下。
复制是被久经考验的,而mysqlbinlog则不是那么可靠。
复制的速度更快。
复制可以更方便地查看进度。
复制有更多控制,更容易处理复制错误,也可以过滤事件。
配置日志服务器进行时间点恢复的步骤如下。
1)部署安装。
下载MySQL二进制官方安装包。
wget mysql-5.1.58-linux-x86_64-glibc23.tar.gz
下载sandbox,创建安装用户。
wget https://launchpad.net/mysql-sandbox/mysql-sandbox-3/mysql-sandbox-3/+download/mysql-Sandbox-3.0.28.tar.gz
useradd sandbox
usermod -G mysql sandbox
解压到目录/home/sandbox/pkgs/mysql-Sandbox-3.0.28/,进行安装。注意不要将解压的二进制包的目录删除了。
# as normal user
export PATH=$HOME/usr/local/mysql/bin:$PATH
##export PERL5LIB=$HOME/usr/local/lib/perl5/site_perl/5.8.8
perl Makefile.PL PREFIX=$HOME/local/sandbox
make
make test
make install
将/home/sandbox/local/sandbox/bin路径添加到PATH变量。
执行如下命令安装MySQL实例。
make_sandbox /home/sandbox/pkgs/mysql-5.1.58-linux-x86_64-glibc23.tar.gz
安装成功后,/home/sandbox/sandboxes/msb_5_1_58下同时生成了很多便于管理的脚本,如start、stop、use等。
配置字符集,并添加日志临时目录(默认生成的实例是没有日志的)。 ./stop
修改配置文件。
[client]
default-character-set = utf8
[mysqld]
character-set-server = utf8
default-storage-engine=innodb
./start
./use
> status
2)以下命令将查找最近的二进制文件,并且将日志传递到sandbox主机,准备测试。
find ./ -type f -name "mysql-bin.*" -newer mysql-bin.025050 |xargs ls -lrt > /tmp/all_files.txt
scp -P 9922 -p `cat /tmp/all_files.txt` [email protected]:/home/sandbox/mysqllog_tmp
3)配置日志服务器。
通过以上步骤,日志目录放在/home/mysql/mysqllog_tmp处,我们可运行如下命令生成日志索引文件mysql-bin.index。
cd /home/sandbox/mysqllog_tmp
ls -1 /home/sandbox/mysqllog_tmp/mysql-bin.[0-9]* > mysql-bin.index
./stop
修改配置文件my.sandbox.cnf,添加如下配置项。
log_bin = /home/sandbox/mysqllog_tmp/mysql-bin
log_bin_index = /home/sandbox/mysqllog_tmp/mysql-bin.index
./start
启动成功。
./use
> show binary logs
4)配置复制用户。
./use --user=root
> GRANT REPLICATION SLAVE ON *.* TO 'rsandbox'@'10.%' IDENTIFIED BY 'rsandbox';
5)配置主从同步,进行时间点恢复,恢复到指定时间。
向从库导入一份历史备份,配置主从同步,然后应用日志服务器的日志,我们可以设置同步到某个时间点。
可使用如下命令,同步到某个指定的位置。
START SLAVE [SQL_THREAD] UNTIL MASTER_LOG_FILE='log_name',MASTER_LOG_POS=log_pos

12.11 常见的复制问题及处理方法
以下将叙述一些常见的复制故障处理。
有些复制故障是因为从库被误操作而导致的,此时可能要修复数据或重做从库,此类故障的处理,这里就不做叙述了。
12.11.1 跳过复制错误
在明确知道数据库出现了何种错误时,可以忽略此错误,但不要滥用,跳过错误的命令如下。
STOP SLAVE;
SET GLOBAL sql_slave_skip_counter = 1;
STARTSLAVE;

12.11.2 临时表和复制
必须保证干净地关闭从库,否则复制可能会出错。
因为在复制的时候,从库的临时表也在进行同步,如果关闭了从库,再重启的时候,就没有临时表了,
那么那些对临时表的更新就不能被复制过来,从而就会复制出错。
所以需要干净地关闭临时表,在没有临时表的时候,干净地关闭数据库。
如下步骤可避免复制出错:
1)运行“STOP SLAVE SQL_THREAD;”命令。
2)运行SHOW STATUS检查Slave_open_temp_tables的值,具体命令如下。
SHOW STATUS like '%slave%';
3)如果Slave_open_temp_tables不等于0,那么运行START SLAVE SQL_THREAD,然后重复以上步骤。
4)如果Slave_open_temp_tables=0,那么,那么可以关闭数据库的实例了。

或者可以用另外一种方式,将临时表的前缀都配置为某个名称(如norep),然后用选项--replicate-wild-ignore-table=norep%将这些表配置为不复制。
临时表的复制问题主要是出在基于语句的复制模式,如果使用row-based复制,那么临时表是不会被复制的,
如果你希望一劳永逸,可以考虑使用基于行的复制模式。

12.11.3 内存表和复制
如果使用的是内存引擎的表,那么从库重启也可能会导致复制中断。
1)如果主库重启,那么内存表将会是空的,会写入一个“DELETE”语句到二进制日志,通知从库清空数据,这种情况下一般不会有复制问题。
2)如果从库重启,那么内存表是空的,这会导致和主库的数据不一致,主库复制过来的操作将无法正常运行,复制将会失败。
基于行的复制可能会出现错 误“Can't find record in'memory_table'”。
此种情况下的复制中断没有太好的解决方案。如果可能,我们可以用InnoDB来代替内存表。
如果你实在需要使用内存表的话,可以考虑设置IDEMPOTENT(这个选项对所有其他表都生效,因此需要谨慎使用),
或者忽略报错的错误号,比如忽略1032错误,或者还可以在从库中忽略要复制的内存表。
另外,基于语句的复制,比如INSERT INTO…SELECT FROM memory_table可能向主从库中插入不一样的数据。
如果设置了内存表的大小,SET global max_heap_table_size value,那么这个变更是不会被复制到从库的,你需要确保主从都做了变更。
虽然主从的内存表的数据可能会不一致,但是如果应用程序逻辑可以确保内存表只是用作缓存,那么一般是不会有太大的问题。

12.11.4 主库宕机重新启动成功,但复制关系中断
主库、从库宕机都可能导致复制关系中断。
一般情况下,生产环境中出现的复制故障主要是主库宕机后,从库找不到同步的主库日志位置信息,需要手动处理。
主库二进制日志并不是实时刷新的,主库宕机后,部分日志丢失了,
但是更多的日志已经被传送到了从库,结果从库的数据比主库的还要新,这种情况下往往会导致主键冲突。
我们需要进行临时设置让从库忽略主键冲突的错误。
待主从之间的复制稳定之后,建议立即取消忽略的错误号,仍然执行严格的复制检查。

12.11.5 主库宕机重启不成功
如果主库宕机重启不成功,则需要选择其中一台从库做主库,具体步骤如下。
1)通过master_log_file、read_master_log_pos可以判断哪个从库是最新的。
2)确认该从库已经应用了所有日志,提升该从库为主库。
3)其他从库自行检查自己最新的日志,判断是哪个时间点中断了,是哪条SQL,
然后通过这条SQL去“新主库”查找具体的位置点(position),并从这个点开始同步。
由于检索相关SQL比较耗时,对于高并发的业务,可能会难以定位到具体的位置点,如果可以接受部分数据误差,
那么我们也可以直接选择故障时刻的大致日志位置点或凭经验决定从哪里开始同步。
如果确实难以定位,但对于数据的一致性要求又很高,那么我们可以对新的主库重新制作从库,以保证数据的准确性。

12.11.6 多个从库的server-id相同
如果主库的一些从库存在server-id相同的情况,那么从库的MySQL错误日志里将会有大量的重连和断开的错误,但它不会明确告知我们server-id有重复。
配置不同的server-id即可解决此问题。

12.11.7 锁定导致的复制延时
如果是基于语句的复制,那么从库上的操作中锁定可能会比较多,从而影响复制的速度,比如INSERT…SELECT这类语句会锁住所有数据。
由于从库上的操作是逐个顺序执行的,因此长时间的查询,可能会导致大的延时。
解决方法具体如下。
1)分割查询,尽早释放资源。
2)可以考虑采用SELECT INTO OUTFILE,然后LOAD DATA INFILE的方式。

12.11.8 对MyISAM引擎的表恢复数据
如果某个MyISAM表的数据有问题,需要恢复到某个时间点的数据,那么我们可以采用如下的便捷方法进行恢复。
1)主库LOCK TABLE table_name READ。
2)主库FLUSH TABLES。
3)复制备份的MyISAM数据文件,覆盖掉主库和从库中的这个表。
4)主库解锁表UNLOCK TABLES。
以上方法在理论上是可行的,主要是为了确保在将备份文件复制到主库上的时候,主从复制也是正常的。
有时我们在误操作表之后,希望能够恢复数据,这时就可以采用这样的办法了。

12.11.9 如何彻底清除Slave设置
MySQL 5.1并不能干净清除复制信息,比如,我们在本机执行如下命令。
STOP SLAVE; RESET SLAVE;
以上命令将清除master.info和relay-log.info。
但我们仍然可以在如下命令中看到一些残留信息。
SHOW SLAVE STATUS \G;
残留信息输出如下。
mysql> SHOW SLAVE STATUS \G;
*************************** 1. row ***************************
Slave_IO_State:
Master_Host: appxxx
Master_User: test
Master_Port: 3306
Connect_Retry: 60........
理论上清除废旧的文件,然后重启MySQL即可,那么有没有更好的不需要重启的办法呢?
可以试试下面这个办法。 CHANGE MASTER TO MASTER_HOST="
这个时候虽然“SHOW SLAVE STATUS\G;”没有任何Slave相关信息的输出了,但是重新生成了master.info和relay-log.info文件,这样很不友好。
然后我们再运行“RESET SLAVE”;命令,这次就可以清除所有信息了。
此时可手动清除残留的*relay*文件。

12.11.10 网络异常导致的复制延时
MySQL在网络异常的时候,也可能会延时很久,然而我们并不知道,
虽然SHOW SLAVE STATUS\G输出里的Seconds_Behind_Master显示为0,但可能已经延时很久了。
建议把slave_net_timeout参数设置得小一些,比如小于1分钟。

小结:
本章介绍了MySQL的复制技术,所有其他复制架构的基础都是主从复制,我们应该熟练掌握主从复制,熟悉复制相关的各种文件。
复制故障是生产环境中比较常见的问题,特别是有大批量机器的时候,
为了减少复制问题的发生,我们应保证复制架构的简单,尽量少用MySQL的各种高级特性,比如内存表、临时表、视图、存储过程、触发器等。

猜你喜欢

转载自www.cnblogs.com/BradMiller/p/12036159.html