[置顶] MySQL复制原理与配置

1复制配置

1.1如何搭建复制

1.1.1master配置

开启二进制日志并创建唯一的Server ID:在my.cnf文件的[mysqld]节加入相应配置信息并重启Server使其生效。

[mysqld]
log-bin=mysql-bin
server-id=1

注意事项

复制组中每台Server的server-id必须唯一以便于识别不同的Server。server-id范围为1-232-1。

log-bin指定了二进制日志名称的前缀,可根据需要自行选择。

使用innodb建立复制时,为了保证最大可能的持久性和一致性需在my.cnf中使用:

innodb_flush_log_at_trx_commit=1
sync_binlog=1 

在master上关闭skip-networking选项,否则salve不能与master通讯。

1.1.2slave配置

创建唯一的Server ID:在my.cnf文件的[mysqld]节加入配置信息并重启Server。

[mysqld]
server-id=2

注意事项

server-id必须在整个复制组内唯一。

也可选在在salve上开启log-bin选项以用于slave的数据备份、故障恢复或者更复杂的复制拓扑。

1.1.3创建复制用户

slave需通过账户和密码连接至master,故需在master上创建此账户,并赋予相应的权限。

mysql> CREATE USER 'repl'@'%.mydomain.com' IDENTIFIED BY 'slavepass';
mysql> GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%.mydomain.com';

注意事项

理论上任何账户均可,但因用于复制的账号和密码会以明文存储于master info相关的文件和表中,出于安全考虑,一般创建专门的账户并仅赋予复制的权限实现复制。

另外,不同的salve可以使用不同的复制账户(但除非有必要才这么做)。

1.1.4获取master二进制日志的坐标

使slave启动复制时可从正确的位置处理二进制日志中的事件。

若在在复制开始前master中已存在需同步到salve中的数据,则需先在master上停止可引起变化的处理之后再获取二进制日志的坐标并备份数据,如下:

先在一个会话中锁定表:

mysql> FLUSH TABLES WITH READ LOCK;

保留上个会话,在另一会话中获取并记下二进制日志坐标:

mysql > SHOW MASTER STATUS;

注意事项:若SHOW MASTER STATUS结果为空说明之前master并未开启二进制日志选项。此时对应的二进制日志坐标(二进制日志文件名和位置)应为’’和4。

1.1.5用mysqldump创建数据快照

确定已为master上的表加了READ锁,并在加锁后记下了二进制日志的坐标。

在另一会话中备份master中的数据库(全部的或者指定的):

shell> mysqldump --all-databases --lock-all-tables >dbdump.db

或者同时加入—master-data选项,可将CHANGE MASTER TO语句自动加到备份文件中,使slave恢复数据后直接开启复制:

shell> mysqldump --all-databases --master-data >dbdump.db

在获取READ锁的客户端释放锁:

mysql> UNLOCK TABLES;

注意事项:备份master时仅选择符合过滤规则(既需要复制到slave)的数据库。

备份所得dbdump.db文件可通过master或直接在slave上执行。

1.1.6用原始数据文件创建数据快照

对于较大的数据库使用原始数据文件创建备份比使用mysqldump更有效率(跳过了因重新执行insert语句带来的更新索引的开销)

确定在master上获取READ锁并获取二进制日志的坐标:

mysql> FLUSH TABLES WITH READ LOCK; mysql> SHOW MASTER STATUS;

在另一会话中关闭master:

shell> mysqladminshutdown 或者 shell>/etc/init.d/mysql stop

获取master的数据文件(下述三选一):

shell>tar cf /tmp/dbdump.tar ./data

shell> zip –r /tmp/dbdump.zip ./data

shell>rsync –recurasive ./data /tmp/dbdump

重启master:

shell>/etc/init.d/mysql start

如果用的不是InnoDB引擎,可在master保持运行的情况下获取数据快照。如下:

在master上获取READ锁并获取二进制日志的坐标:方法同上

获取master的数据文件:方法同上

释放master上的READ锁:

mysql>UNLOCK TABLES;

在获取到master数据库的拷贝后,可在slave开始复制前将master的数据拷贝至各slave。

需要注意:用原始数据文件创建数据快照的方法用于具有复杂缓存或日志算法的存储引擎中的表时可能需要额外步骤来保证快照的即时性,因为最初的拷贝命令可能遗漏缓存信息或者日志更新,即便使用了全局读锁;另外若master和salve的ft_stopword_file,ft_min_word_len,ft_max_word_len分别有不同的值而且拷贝的表拥有全文索引,该方法也不可靠;如果使用InnoDB表可使用MySQL EnterpriseBackup组件的mysqlbackup命令产生一致的快照或者使用上述的coldbackup技术获取InnoDB的二进制快照。拷贝数据时可能要排除一些不必要的信息,如:“mysql”库、master info相关文件、master二进制日志、中继日志等。

1.1.7全新的master/slave之间建立复制

配置master,开启master,创建复制用户,为master加READ锁,获取master的status,释放master表的READ锁(参见前述步骤)。

配置slave,开启slave,在slave执行CHANGE MASTER TO设置master的复制配置,start slave开启复制。在每个slave上重复这些步骤。

若有来自其他数据库的备份需要导入master,此时可按常规方法(原始数据或mysqldump备份数据等)将数据导入master。slave可自动复制这些变化。

1.1.8已存在数据情况下的复制

创建复制用户,配置master(需重启),为master加READ锁,获取master的status,mysqldump备份或者用原始数据备份(需关闭master),若用mysqldump备份则释放READ锁若用原始数据备份则重启master。

配置slave,还原数据到slave(依赖于备份方法,若是mysqldump备份先以—skip-slave-start选项开启slave后还原数据,若是原始数据则先还原数据再以—skip-slave-start选项开启slave)。

在slave上执行CHANGE MASTER TO设置master的复制配置,start slave开启复制。在每个slave上重复这些步骤。

注意事项:slave利用master info repository中的信息跟踪master的二进制日志。依据配置文件中—master-info-repository值的不同,repository可存储在master.info和relay-log.info文件中或者存储在mysql数据库的master_slave_info表中。不论存储在何处都不要随便移动和删除。另外repository中的值会覆盖命令行或者my.cnf中指定的值。

1.1.9在已存在的复制环境中加入新的slave

无需关闭master,可通过拷贝既存的slave(server-id要配置为不同的值)来添加新的slave。

首先关闭既存的slave

shell >mysqladmin shutdown或者shell>sudo/etc/init.d/mysql stop

然后拷贝既存slave的数据目录到新slave的数据目录(可通过tar、cp、scp、rsync),要确保拷贝了日志文件和中继日志文件。

这里可能存在一个问题,若不指—relay-log选项,那么新的主机使用主机名作为中继日志文件的名的一部分,这将导致新的slave无法识别到由既存slave拷贝过来的中继日志文件,--relay-log-index选项亦如此。可通过在既存slave和新slave中使用相同的--relay-log和--relay-log-index选项来避免此问题。若已经出现了问题,则需先在新slave上执行STOP SLAVE,若重新开启过既存的slave那么还要在既存的slave上执行STOP SLAVE,之后重新拷贝相关内容然后执行后续步骤。

从既存slave拷贝包含master info和relay log info信息的文件到新的slave。

开启既存的slave。

在新slave上编辑配置文件,设置唯一的server-id.

启动新的slave。

1.1.10通过slave设置master配置

需告诉slave必要的连接信息它才能与master通讯并从master正确复制,为此要在slave上执行如下语句:

changemaster to

master_host= 'VMS00780',

master_user= 'usvr_replication',

master_password= 'password',

master_port= 3306,

master_log_file= 'VMS00780-bin.000007',

master_log_pos= 120;

注意事项:复制环境中slave不能通过Unix socket文件与slave通讯,必须使用TCP/IP与master通讯。另外CHANGE MASTER TO语句还有其他选项可用,具体参加官方手册中的语法。

1.2复制的格式

有三种格式的复制:基于语句的复制,基于行的复制以及混合复制。基于语句的复制为MySQL5.6版本的默认方式,通过在master和slave之间传播SQL语句来实现复制;基于行的复制通过记录并传递个别表行的变化来实现复制;混合格式的复制可根据事件类型实时改变二进制日志的格式,使用混合格式的复制时,默认使用基于语句的复制,在特定的情况下(由使用的存储引擎和将要执行的语句决定)切换为基于行的复制。

日志格式由binlog-format这一全局的动态参数设置,基于语句的复制和基于行的复制各自存在自己的问题和限制(比如使用基于语句的复制来复制存储过程和触发器)。

1.2.1基于语句的复制及基于行的复制的优缺点

通常可使用混合类型的复制格式来兼顾一致性和性能。在特殊情况下可利用基于语句的复制或基于行的复制的优势来达到特定目的。

Ø  基于语句的复制的优点

ü  存在已久(从MySQL3.23),经过验证

ü  产生的日志文件较小

ü  日志文件包含引起变化的所有语句,可用以审计数据库

Ø  基于语句的复制的缺点

ü  不安全,尤其对于一些不确定性的行为,如:依赖于返回值不确定的UDF或存储过程的语句;使用不带ORDER BY而带LIMIT子句的DELETE语句和UPDATE语句;使用了诸如LOAD_FILE(),UUID(),UUID_SHORT(),USER(),FOUND_ROWS(),SYSDATE()等不能被恰当复制的函数的SQL语句。(若使用基于语句的复制,对于不能被正确复制的语句会在日志文件中记录一条警告信息:”[Warning] Statement is not safe to log instatement format.”也可以在客户端使用SHOW WARNINGS语句显示信息)。

ü  与基于行的复制相比INSERT…SELECT语句需要更多行级锁。

ü  与基于行的复制相比需要扫描表(因为没有在WHERE从句中使用索引)的UPDATE语句需要锁定更多行。

ü  对于InnoDB引擎,使用AUTO_INCREMENT的INSERT语句会阻塞其他费冲突的INSERT语句。

ü  对于复杂的语句,在行被插入和更新前,SLAVE必须评估和执行语句,而基于行的复制SLAVE仅需修改受影响的行,不用执行整个语句。

ü  若在SLAVE上评估语句发生错误,特别是执行复杂语句时,基于语句的复制会在其所影响的行慢慢增加错误的范围。

ü  对于存储函数会使用相同的NOW()值执行,而对于存储过程并非如此。

ü  master与slave中的表定义(几乎)必须相同。

Ø  基于行的复制的优点

ü  安全,所有的变化都可以被复制。(需注意的是mysql数据库不被复制,它被看做是特定于节点的数据库,然而在基于语句的复制中诸如GRANT、REVOKE等会更新mysql数据库信息的语句会被复制。)

ü  与大多数其他数据库管理系统中的技术类似,关于其他DBMS的复制知识可以很容易迁移到MySQL中。

ü  对于下列类型的语句master上需要更少的行锁,因而可以达到较高的协同性。INSERT…SELECT,使用AUTO_INCREMENT的INSERT语句,使用了WHERE从句但没有使用键或者只更改了较少的行的UPDATE语句或DELETE语句。

ü  对于任何任何INSERT,UPDATE或DELETE语句在slave上只需较少的行锁。

Ø  基于语句的复制的缺点

ü  产生的日志文件较大,因而备份和恢复日志需更长的时间,且对二进制日志会锁定较长时间来写数据因而引起协同问题。

ü  产生较大BLOB值的确定性UDF需花更长的时间来复制。

ü  不能通过日志产看执行了什么语句,也不能通过日志查看slave从master接收到并执行了什么语句。

ü  对于MyISAM引擎的表,当INSERT语句被应用于基于行的事件的二进制日志时需要更高级别的锁。

1.2.2基于行的日志及复制的使用

基于行的复制与临时表:使用行格式的复制时,临时表不被复制。当使用混合格式的复制时,涉及临时表的“安全”语句以基于语句的格式被记入二进制日志。

基于行的复制与非事务型表的同步:影响的行数很多时,变化被分为多个事件;当语句提交时,所有这些事件被写入二进制日志。当在slave中执行时,会在涉及的表上加锁,然后以批量模式应用这些行。

延时和日志大小:会将每个行的改变写进二进制日志,因此日志尺寸会急速增加。在复制环境,这会显著增加在slave中应用这些改变的时间。

读取二进制日志:mysqlbinlog工具使用 BINLOG语句显示二进制日志中基于行的事件。该语句以可印刷形式显示一个事件,但是,是以意义并非显而易见的base 64-encoded串表示。通过加--base64-output=DECODE-ROWS 和—verbose选项可以以可读的方式显示二进制日志内容。

缺少二进制日志校验和:基于行的复制不使用checksum,因此在处理过程中不能检测到网络、磁盘或其他原因造成的错误。可考虑使用SSL来确保数据在传输过程没有发生网络造成的损坏。例如在CHANGE MASTER TO语句中使用SSL相关选项。

不支持基于server ID的过滤:某些时候会使用WHERE从句中包含@@server_id <> id_value的UPDATE和DELETE语句来过滤某些slave上的改变,然而这对于基于行的复制并不可用。

数据库级别的复制选项:--replicate-do-db, --replicate-ignore-db和--replicate-rewrite-db选项的行为依据复制格式的不同而不同。推荐避免使用数据库级别的复制选项而使用表级别的选项如:--replicate-do-table 和--replicate-ignore-table

非事务型表和停止的slave:当使用基于行的日志时,若slave在slave线程更新非事务型表表时停止。则slave数据库可能会不一致。因此,若使用了基于行的复制则推荐使用事务型引擎。另外通过在关闭MySQL Server前执行STOP SLAVE或STOP SLAVE_SQL_THREAD可以避免此问题,无论使用是么引擎或复制格式,这都是推荐的方式。

1.2.3确定二进制日志中安全/不安全的语句

所谓语句的“安全性”是指在使用基于语句的复制时该语句能否被正确的复制。一般的,若一条语句时确定的那么它是“安全”的,否则是“不安全”的,但也有一些不确定性的函数被认为是“安全”的。另外使用了由浮点数学函数产生的结果(依赖于硬件)的语句也被认为是“安全”的

Ø  “安全”与“不安全”语句的处理

ü  若使用基于行的复制,对“安全”与“不安全”语句的处理没有区别。

ü  若使用混合型的复制,被标记为“不安全”语句会使用基于行的格式记录日志,被认为是“安全”的语句使用基于语句的格式记录日志。

ü  若使用基于语句复制,被认为是“不安全”的语句会生成一条警告信息。

Ø  “不安全”的语句

ü  包含可能在slave上返回不同值的系统函数(如eFOUND_ROWS(),GET_LOCK(), IS_FREE_LOCK(), IS_USED_LOCK()等等)的语句。也有一些非确定性的函数被认为是安全的,如CONNECTION_ID(), CURDATE(), CURRENT_DATE(),CURRENT_TIME(), CURRENT_TIMESTAMP()等等。

ü  引用系统变量

ü  UDFs(用户定义函数)

ü  触发器或存储过程更新拥有AUTO_INCREMENT列的表

ü  INSERT DELAYED语句

ü  在用于多主键或多唯一键的表上执行INSERT... ON DUPLICATE KEY UPDATE语句

ü  使用带LIMIT的更新语句

ü  访问或引用日志表

ü  事务型操作后的非事务型操作

ü  访问或引用self-logging表

ü  LOAD DATA INFILE语句

1.3Global Transcation Identifier实现复制

1.3.1.           GTID的概念

1.3.2.           通过GTIDs建立复制

1.3.3.           在故障转移和横向扩展中使用GTIDs

1.3.4.           使用GTIDs实现复制时存在的限制

1.4复制以及二进制日志相关的参数和变量

可分为master相关的选项和变量、slave相关的选项和变量、二进制日志相关的选项和变量以及GTID相关的选项和变量几个组。

比较重要的几个有

Ø  server-id

Ø  log-bin

Ø  binlog-format

Ø  log-slave-updates

Ø  sync_binlog

Ø  slave-net-timeout

Ø  rpl_semi_sync_master_enabled

Ø  rpl_semi_sync_master_timeout

Ø  rpl_semi_sync_slave_enabled

具体可参考:http://dev.mysql.com/doc/refman/5.6/en/replication-options-table.html

1.5管理复制

1.5.1检查复制的状态

管理复制时最常见的任务是确定复制正在运行且slave与master之间没有错误发生。确定这些事情选在每个slave上运行SHOW SLAVE STATUS语句。

mysql>show slave status\G

***************************1. row ***************************

               Slave_IO_State: Waiting formaster to send event

                  Master_Host: 192.168.83.37

                  Master_User: usvr_replication

                  Master_Port: 55944

                Connect_Retry: 60

              Master_Log_File:VMS00782-bin.000017

          Read_Master_Log_Pos: 3332154

               Relay_Log_File:VMS00780-relay-bin.000004

                Relay_Log_Pos: 41160

        Relay_Master_Log_File:VMS00782-bin.000017

             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: 3332154

              Relay_Log_Space: 41336

              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:

  Replicate_Ignore_Server_Ids:

             Master_Server_Id: 1

                  Master_UUID:abc28d0a-3562-11e3-9efa-0050569244bc

             Master_Info_File:/data/mysql/master.info

                    SQL_Delay: 0

          SQL_Remaining_Delay: NULL

      Slave_SQL_Running_State: Slave has readall relay log; waiting for the slave I/O thread to update it

           Master_Retry_Count: 86400

                  Master_Bind:

      Last_IO_Error_Timestamp:

     Last_SQL_Error_Timestamp:

               Master_SSL_Crl:

           Master_SSL_Crlpath:

           Retrieved_Gtid_Set:

            Executed_Gtid_Set:

                Auto_Position: 0

1row in set (0.01 sec)

输出中需要检查的关键字段有:

Ø  Slave_IO_State:slave当前状态。

Ø  Slave_IO_Running:读取master的二进制日志的I/O线程是否在运行。

Ø  Slave_SQL_Running:执行中继日志的SQL线程谁否正在运行。

Ø  Last_IO_Error:处理中继日志时I/O线程记录的最后一个错误。

Ø  Last_SQL_Error:处理中继日志时SQL线程记录的最后一个错误。

Ø  Seconds_Behind_Master:slave的SQL线程处理master的二进制日志时延迟的秒数。

Ø  Master_Log_file,Read_Master_Log_Pos:master二进制日志坐标,指示了slave的I/O线程已读取到master的哪个日志和哪个位置。

Ø  Relay_Master_Log_File,Exec_Master_Log_Pos:master二进制日志坐标,指示了slave的SQL线程已执行到master的哪个日志和哪个位置。

Ø  Relay_Log_File,Relay_Log_Pos:slave的中继日志坐标,指示slave的SQL线程已执行到哪个中继日志的的哪个位置。

在master上可以通过执行SHOW PROCESSLIST查看运行的进程来检查连接到master的slave的状态。可以通过执行SHOW SLAVE HOSTS语句获取连接到该master的slave的基本信息,若slave使用了--report-host选项,可以在显示结果中显示出主机名称。

1.5.2在slave上暂停复制

可通过在slave上执行STOP SLAVE语句来停止slave上 的复制。复制停止后I/O线程停止从master读取二进制日志并写进中继日志,SQL线程也停止从中继日志读取并执行事件。

使用STOP SLAVE会同时停止slave的I/O线程和SQL线程,可通过执行STOP SLAVE IO_THREAD或STOP SLAVESQL_THREAD来单独关闭某一个线程。

可通过在slave上执行START SLAVE开启slave。同理,可通过执行START SLAVE IO_THREAD或START SLAVESQL_THREAD来单独开启某一个线程。

单独关闭某一线程可达到一些特定目的。

2复制实现

(1)master记录改变数据库结构、内容的语句或者数据库结构、内容的改变至二进制日志;

(2)slave连接至master请求二进制日志到slave的中继日志(是slave pull不是master push);

(3)slave执行中继日志中的内容。

(每个slave独立进行上述两步,互不影响。master为每个slave创建独立的线程发送二进制日志)

2.1实现复制的细节

共三个线程实现MySQL复制,一个在master(Binlog dump thread)两个在slave(Slave I/O thread,Slave SQL thread)。

Binlog dump thread 负责向slave发二进制日志内容,该线程读取每个即将发往slave的事件时会为master的二进制日志加锁,读取后释放锁。

Slave I/O thread读取master的Binlog dump thread发送来的二进制日志的更新并写进slave本地的中继日志。

Slave SQL thread读取中继日志并执行其中的事件。

每个master/slave连接都独立的存在上述三个线程。可通过在master和slave上执行SHOW PROCESSLIST命令查看相关信息,每个线程会有不同的状态信息。Slave I/O thread和Slave SQL thread可分别由对应命令单独开启,也可由START SLAVE命令一并开启。SHOW SLAVE STATUS命令提供了关于复制过程的额外信息。

2.2中继和状态日志

复制过程中slave创建三种日志文件

Ø  master info日志

保存了slave连接master的状态信息和当前的配置信息,如master主机名、登录信息以及slave以读取到的master二进制日志的坐标。MySQL5.6以后可通过指定-master-info-repository参数选择将这些信息存储在文件(master.info)或表中(mysql.slave_master_info)

Ø  relay log info 日志

记录slave中继日志的执行位置。同样,在MySQL5.6后可通过指定--relay-log-info-repository参数选择将这些信息存储在文件(relay-log.info)或表中(mysql.slave_relay_log_info)

Ø  中继日志

包含了由由I/O线程从master的二进制日志读取到的事件,中继日志内容由SQL_THREAD线程读取执行。

为保证复制是crash-safe的,当使用表保存master info和relay log info时需用事务型引擎如InnoDB,MySQL5.6.6后这些表使用InnoDB创建,之前使用MyISAM引擎创建,故在旧版本中需在复制开启前更新这些表的引擎为InnoDB。除此之外在slave上开启relay-log-recovery选项。

MySQL5.6.6前若mysqld不能初始化复制日志表,则slave不会开启,但在MySQL5.6.6后,发生上述情况是只会给出警告,slave能够开启(这是一个bug需注意)

2.2.1slave的中继日志

包括中继日志文件盒中继日志索引文件,前者包含了从master的二进制日志文件读取到的时间,后者包含了当前正在使用的中继日志文件名称。

中继日志文件格式与二进制日志文件格式相同可通过mysqlbinlog读取。默认中继日志文件名为如下格式:host_name-relay-bin.nnnnnn。host_name为slave所在主机名,nnnnnn为序列号。序列号由000001开始,每产生一个新的中继日志文件则序列号依次增1。中继日志索引文件用来跟踪当前的中继日志文件,其默认名称为如下格式host_name-relay-bin.index。这些默认的名称可通过设置--relay-log和--relay-log-index来改变。

若slave使用了默认的日志文件名,但在复制开启后slave所在主机更改了主机名,那么复制会发生问题” Failed to open the relay log” , “Could not findtarget log during relay log initialization”,可在复制开启前通过上述两个参数明确指明日志文件名称来避免该问题。若果已经发生了问题,那么应先停掉slave,将旧的中继日志索引文件的内容添加到新的中继日志所索引文件的开头,然后重启slave。

shell> cat new_relay_log_name.index >>old_relay_log_name.index
shell> mv old_relay_log_name.index new_relay_log_name.index

新的中继日志文件在下列情况下被创建

Ø  每次开启I/O线程

Ø  日志被刷新,如FLUSHLOGS 或 mysqladmin flush-logs。

Ø  当前中继日志文件过大

ü  若max_relay_log_size大于0,则该值为中继日志文件最大尺寸

ü  若max_relay_log_size为0,则中继日志文件尺寸由max_binlog_size确定

SQL线程会在执行完某个中继日志文件的时事件后自动清除该中继日志文件

2.2.2slave的状态日志

默认包含master.info和relay-log.info两个状态日志文件,其名字和位置可通过--master-info-file和 --relay-log-info-file指定。MySQL5.6后状态信息可存储在表中,通过-master-info-repository和--relay-log-info-repository来指定该行为,信息分别被存储在mysql.slave_master_info和mysql.slave_relay_log_info中。

状态日志文件中存储的信息类似于由SHOW SLAVE STATUS显示的信息,由于状态日志存储在磁盘中因此slave所在主机关闭后日志信息不受影响。master info日志文件需妥善保存,因为其包含连接到master的密码。

slave的I/O线程更新master info日志,SQL线程更新relay log info日志。日志文件中的内容与日志表中的内容存在对应关系(但不是完全一样)。另外,若日志文件中的内容还没有被刷到磁盘那么SHOW SLAVE STATUS限制的值与日志文件中的值可能不匹配。理想情况下,若mysqld正在运行,则应通过SHOW SLAVE STAUTS或通过slave_master_info 和 slave_relay_log_info表来查看状态信息(若配置了的话),一般在slave离线的情况下才查看日志文件。

备份slave的数据时应当把状态日志文件一并备份。

2.3如何评估复制过滤规则

若master没有在二进制日志中记录某条语句则该语句不被复制,若在二进制日志中记录了某条语句,则语句会被发送到所有slave,然后由slave决定是否执行该语句。

在master上可通过—binlog-do-db和—binlog-ignore-db选项控制二进制日志记录的内容。一般不在master上通过这些选项控制哪些库表被复制,而是在slave上来过滤哪些事件被执行。在slave上可通过--replicate-*参数来决定是否执行或忽略从master接收到的事件。在slave上,数据库级别的选项如—replicate-do-db和—replicate-ignore-db首先被检查,若没有匹配的数据库级别的选项则继续检查表级别的选项。

对于仅影响数据库的语句如CREATE DATABASE, DROP DATABASE和ALTERDATABASE,数据库级别的选项优先于任何--replicate-wild-do-table选项,也即,对于这样的语句,仅在没有数据库级别的选项时才检查--replicate-wild-do-table选项。

为了便于确定设置一个选项后的影响,应避免混合使用”do”和”ignore”以及通配符选项和非通配符选项。

若使用了任何--replicate-rewrite-db选项则它们将在--replicate-*选项前被检查。

2.3.1数据库级别的复制和二进制日志选项

slave上:--replicate-do-db或者--replicate-ignore-db,master上:using--binlog-do-db或者--binlog-ignore-db。若使用基于语句的复制,默认的数据库将被检查以确定是否符合过滤规则,若使用基于行的复制,数据将要发生变化的数据库被检查以确定是否符合过滤规则。master上与slave上处理流程类似。

对于master上的二进制日志,步骤描述如下:

1是否有--binlog-do-db或--binlog-ignore-db选项?

       Y:继续2

       N:记录语句并退出

2是否有默认库(通过USE选择的库)?

       Y:继续3

       N:忽略语句并退出

3有默认库,是否有--binlog-do-db选项?

       Y:默认库是否匹配选项?

              Y:记录语句并退出

              N:忽略语句并退出

       N:继续4

4是否有—binlog-ignore-db选项匹配默认数据库?

       Y:忽略语句并退出

       N:记录语句并退出

需要注意的是:对于基于语句的复制,在确定是否记录和忽略更新时CREATE DATABASE, ALTER DATABASE和 DROPDATABASE语句中涉及到的数据库被作为默认库。

2.3.2表级别的复制选项

slave仅在没有匹配的数据库级别的选项值时才检查和评估表级别的选项。包括--replicate-do-table或--replicate-wild-do-table选项以及--replicate-ignore-table或者--replicate-wild-ignore-table选项。

2.3.3复制规则应用

一些典型的复制过滤规则组合

Ø  没有任何—replicate-*选项:slave执行接收到的所有事件

Ø  有--replicate-*-db选项但没表级别选项:根据库级别的选项接受或忽略事件,然后执行这些选项所允许的其他所有事件,没有表级别选项的限制。

Ø  有--replicate-*-table选项但没库级别的选项:仅依据表级别的选项选择执行或忽略事件。

Ø  表级别和库级别选项结合:首先依据数据库级别的选项接受或者忽略事件,然后根据表级别的选项评估这些库级别选项过滤通过的事件。这一组合可能导致反常的结果,结果可能依使用的复制方式不同而不同。

假定master上db1库中有mytbl1表,db2库中有mytbl2表,slave的复制配置中包含:

replicate-ignore-db = db1
replicate-do-table  = db2.tbl2

现在执行如下语句:

USE db1;
INSERT INTO db2.tbl2 VALUES (1);

若使用基于语句的复制,USE语句使db1库成为默认库,因此replicate-ignore-db = db1选项匹配,INSERT语句被忽略,表选项不被检查。

若使用基于行的复制,默认库对于slave如何检查数据库级别的选项没有任何影响。因此USE语句对于--replicate-ignore-db选项如何处理并无影响。--replicate-ignore-db选项指定的数据库与INSERT语句所改变的数据库并不匹配,因此slave处理表级别的选项。而--replicate-do-table选项匹配将要被更新的表,因此行被更新。

3解决方案

复制可在多种不同的环境中使用,实现不同的目的。

3.1用复制实现备份

可暂停或停止slave而不影响master上运行的操作,因此可通过slave创建备份。

备份方法依赖于数据库规模以及是否要备份除了数据之外的其他文件如二进制日志刚和状态日志等。

Ø  若使用复制,且数据库规模较小,则可用mysqldump工具从slave备份数据

Ø  若使用复制,且数据库规模较大,则可从slave备份原始数据文件,这也意味这可以备份二进制日志、中继日志等。

另外一种备份策略是将服务器设置为read-only,然后备份原始数据,这可以在slave上操作也可以在master上操作。之后再将服务器设置回正常状态。

3.1.1用mysqldump备份slave

Ø  首先停止slave。以下语句均可:

shell>mysqladmin stop-slave;
shell> mysql -e 'stop slave SQL_THREAD;'
mysql>stop slave;
mysql>stop slave SQL_THREAD;

Ø  运行mysqldump工具,如:

shell> mysqldump --all-databases >fulldb.dump

Ø  待备份结束,开启复制。与停止复制的语句对应:

shell>mysqladmin start-slave;
shell> mysql -e 'start slave SQL_THREAD;'
mysql>start slave;
mysql>start slave SQL_THREAD;

使用此方法需注意监控slave复制进程以确保备份的时间结束后slave能够追上master中的事件。

3.1.2使用原始数据文件备份slave

需关闭slave所在MySQL Server,否则备份过程中后台任务可能仍在更新数据文件,特别是有后台进程的InnoDB引擎。

Ø  首先关闭slave Server。使用以下语句:

shell>mysqladmin shutdown
shell>sudo /etc/init.d/mysql stop
shell>sudo service mysql stop

Ø  拷贝数据文件。可先打包再拷贝。(可用工具:tar,cp,scp,rsync)

shell> tar cvf /tmp/dbbackup.tar ./data
shell>cp /tmp/dbbackup.tar .

Ø  开启slave Server.

shell>mysql_safe &
shell>sudo /etc/init.d/mysql start
shell>sudo service mysql start

通常备份整个数据目录,包括日志文件(状态日志、中继日志等)。若中继日志文件丢失但有relay-log.info文件且master中二进制日志文件还在,则仍能根据relay-log.info记录的坐标使用CHANGE MASTER TO语句重新指向master从上次的位置开始读取日志。若slave复制LOAD DATA INFILE语句则需备份SQL_LOAD-* files文件。

3.1.3通过设置master或slave为readonly来备份数据

通过设置server为只读可在master上或slave上执行备份(适用于通过Server获取数据的备份方法如mysqldump)。基本流程如下:

1)       首先设置server为只读

2)       然后执行备份

3)       最会恢复server为正常状态

假定现有master M1,slave S1,连接M1的client C1,连接S1的client C2。

Ø  通过只读的master创建备份

ü  首先使M1只读

mysql> FLUSH TABLES WITH READ LOCK;
mysql> SET GLOBAL read_only = ON;

此时C1到M1的更新会阻塞,C1到M1的读请求可正常进行,在M1上执行备份是安全的,在S1执行备份不安全因为S1仍在处理二进制日志或来自C2的更新请求。

ü  用mysqldump等工具执行备份

ü  备份完成后使M1恢复正常状态

mysql> SET GLOBAL read_only = OFF;
mysql> UNLOCK TABLES;

尽管可在M1上执行备份,但不推荐,因为会阻塞更新。

Ø  通过只读的slave创建备份

ü  首先使S1只读,方法同上。

此时M1继续运行可接受更新请求,在M1上创建备份是不安全的, 而S1会阻塞更新请求,因此在其上创建备份时安全的。

ü  用mysqldump等工具执行备份

ü  备份完成后使S1恢复正常状态。方法同上。

后一种方式为推荐方式。

3.2master/slave分别使用不同的存储引擎来实现复制

由master复制到slave的表可以使用不同的存储引擎。事实上,default_storage_engine和storage_engine两个系统标量不会被复制。在master上和slave上使用不同的引擎可实现特定目的,例如,可在master上用InnoDB引擎实现事务特性,而在只读的slave上使用MyISAM引擎,另外在存储日志的slave上还可考虑使用Archive引擎。

在master和salve上配置不同的引擎的方法依赖于复制配置的方式。

Ø  若使用mysqldump来创建master的备份,继而恢复到salve来搭建复制,那么可以通过编辑备份文本文件改变其中表的存储引擎以在slave上使用不同的引擎;对于使用mysqldump创建备份来说,另外一个方法是在使用备份文件恢复数据前在slave上禁用某些引擎,比如添加—skip-innodb选项可禁用InnoDB引擎。若在创建表是所使用的引擎不存在或被禁用则使用默认的引擎。

Ø  若使用原始数据文件来搭建复制,那么可以在slave开启后使用ALTER TABLE语句改变表的引擎。

Ø  对于全新的master/slave复制配置,也即master上还没有表,那么在master上创建表时不要明确指定存储引擎,这样便可以在slave上使用slave的引擎来创建表。

Ø  若复制已经运行一段时间了,此时想更改slave中的引擎,那么可先停slave,然后为需要的表执行ALTERTABLE ... ENGINE='engine_type',最后开启slave。

要注意的是,尽管default_storage_engine变量不被复制,但若在CREATETABLE 或ALTER TABLE语句中明确指定了引擎类型,那么这些引擎类型会被复制到slave中,若slave中存在这些引擎,则会以同样的引擎创建这些表。因此,可先通过default_storage_engine变量设置默认的引擎,而在CREATETABLE 或ALTER TABLE语句中不特别指定所使用的引擎。

3.3用复制实现横向扩展

可以利用复制实现扩展,以便于在大量读少量写的环境中,将数据库的查询负载分布到多个slave中而只在master中写。

[置顶] MySQL复制原理与配置_第1张图片

若应用中用于数据库访问的代码已经很好的抽象化和模块化了,那么调整代码使其运行在复制环境中十分容易。只需将访问数据库的实现修改为发送所有的写请求道master,发送所有的读请求到master或slave。

3.4向不同的slave复制不同的数据库

某些情况下,可能需要将master上不同的数据库复制到不同的slave中,比如将不同的销售数据分配到不同部门的服务器中。

为达到此目的,可先正常配置复制,之后在slave上通过--replicate-wild-do-table选项配置过滤规则已选择复制特定的数据库。例如上图可通过如下配置实现(在start slave前配置):

Ø  slave 1 --replicate-wild-do-table=databaseA.%.

Ø  slave 2 --replicate-wild-do-table=databaseB.%.

Ø  slave 3 --replicate-wild-do-table=databaseC.%.

每个slave均可从master收到完整的二进制日志,但只会执行满足匹配规则的事件,从而复制特定的数据库。

需要注意的是:若使用基于语句的或混合类型的复制,则此处不能使用--replicate-do-db选项,因为此选项的影响会因当前所选择的数据库而变化。然而,若使用的是基于行的复制,可以使用--replicate-do-db选项,因为此时当前锁选择的数据库对选项操作无影响。

另外,如果在复制开启前有需要复制到slave的数据,可以通过以下方法处理:

Ø  先同步所有数据到slave,然后删除不想保持的数据库、表。

Ø  使用mysqldump为每个数据库创建单独的备份文件,然后在每个slave上执行恰当的备份文件。

Ø  使用仅包含需要在某个slave上保持的特定文件和数据库的原始数据备份文件(对于InnoDB数据库不适用,除非开启了innodb_file_per_table选项)。

3.5提升复制的性能

若连接到master的slave较多则会增加master的客户端连接负载和网络负载,因为每个slave需保持一个到master的连接且每个slave需从master读取二进制日志。此时可通过构建更深一层的复制来提升性能(如下图)。该结构中master只复制到一个slave而其他众多的slave从该直接slave中间接复制。这样可有效减小Master1的客户端负责和网络接口负载,从而提升其性能。

上述简单结构可通过如下配置实现:

Ø  Master1上开启二进制日志(--log-bin),记录所有的变化和更新。

Ø  Master2为Master1的slave负责为其他slave提供复制功能。需在Master2上同时开启—log-bin以及--log-slave-updates选项以便于从Master1复制并将更新写进Master2的二进制日志。

Ø  Slave1、Slave2……为Master2的slave从Master2上复制来自于Master1的更新。

若slave在追master的复制进程时较慢,可通过下列途径解决:

Ø  若果可能,将中继日志和数据文件放在不同的物理驱动器中,可通过--relay-log选项指定。

Ø  考虑将不同数据库复制到不同的slave

Ø  考虑在slave中使用不同的存储引擎。

Ø  若slave不用做master可考虑关闭--log-slave-updates选项

3.6故障时切换master

当前尚无官方方案,可通过脚本监控master并在master故障时切换master。

在slave上设置--log-bin选项,但不设置--log-slave-update,这样只要在其他slave上依次执行stop slave、reset master、change master to语句便可指向该slave使其成为master。

假定有如下结构(从slave读数据的client没有显示,因为其在故障转移时无需切到新的slave),每个slave从master读取完整的二进制日志,每个slave开启了--log-bin选项而未开启--log-slave-updates选项,这样每个slave会有一个空的二进制日志文件。若因为某中原因master不可用了,可选择将某个slave提升为新的master。例如将Slave1 提升为master则图中的Web Client需重定向到Slave1,Slave1将此后的更新记入自己的二进制日志。Slave 2和Slave3可从Slave 1复制。之所以不开启--log-slave-updates选项是为了避免slave两次收到同样的更新。假定Slave1开启了--log-bin和--log-slave-updates那么它会将来自Master的更新记入自己的二进制日志。当Slave2由Master指向Slave1时它可能从Slave1收到已经从原Master收到过的更新。

[置顶] MySQL复制原理与配置_第2张图片

要确保所有slave已执行完中继日志中的所有事件。在每个slave上先执行stop slave IO_THREAD,之后检查showprocesslist输出直到看到Has read all relay log。之后选择将要提升为master的Slave1,执行stop slave和resetmaster。最后依次在剩余slave执行stop slave、change master to、start slave,执行change master to时不必指定二进制日志坐标,因为Slave 1上的二进制日志是初始的第一个。一旦复制开始,需将Web Client重定向到Slave1。此后来自Web Client的更新可计入Slave1的二进制日志。

[置顶] MySQL复制原理与配置_第3张图片

当原Master恢复时,可对Master执行与Slave2、Slave3相同的操作,使其变为Slave1的slave。若要想使Master重新成为master可用上述提升Slave1为master时同样的过程。

需要注意的是,不同slave对master的复制可能并不同步,一些slave可能较其他slave要快,这意味着实际情况中上述例子可能不能正确工作。我们在这里实际上是做了一个假设,假设切换时所有的slave是同步的,且都追上了master。这里只是提供了一种思路,实际情况要比这里描述的复杂。另外实际的生产环境中还要考虑IP地址的切换。

3.7通过SSL建立复制

首先,master和slave都必须支持SSL连接。另外,master和slave上必须有合适的安全证书。通过SSL连接配置复制与配置client与server之间的SSL连接类似。

在master上开启SSL,首先要创建或获取恰当的安全证书,然后在配置文件中加入适当选项,分别为CA证书、server公钥、server私钥。

[mysqld]
ssl-ca=cacert.pem
ssl-cert=server-cert.pem
ssl-key=server-key.pem

在slave上有两种选择:在配置文件中添加适当选项或者在change master to语句中指明相关信息。

Ø  在配置文件中加入选项

[client]
ssl-ca=cacert.pem
ssl-cert=client-cert.pem
ssl-key=client-key.pem

之后以--skip-slave-start选项重启slave server。然后使用change master to,在语句中使用MASTER_SSL选项。

mysql> CHANGE MASTER TO
-> MASTER_HOST='master_hostname',
-> MASTER_USER='replicate',
-> MASTER_PASSWORD='password',
-> MASTER_SSL=1;

Ø  通过changemaster to语句指明相关内容

mysql> CHANGE MASTER TO
-> MASTER_HOST='master_hostname',
-> MASTER_USER='replicate',
-> MASTER_PASSWORD='password',
-> MASTER_SSL=1,
-> MASTER_SSL_CA = 'ca_file_name',
-> MASTER_SSL_CAPATH = 'ca_directory_name',
-> MASTER_SSL_CERT = 'cert_file_name',
-> MASTER_SSL_KEY = 'key_file_name';

最后通过start slave开启复制。

若想在复制期间强制使用SSL连接。可创建使用REQUIRE SSL选项的用于复制权限的用户用于复制。

mysql> CREATE USER 'repl'@'%.mydomain.com'IDENTIFIED BY 'slavepass';
mysql> GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%.mydomain.com' REQUIRESSL;

若之前复制用户已经存在,则可直接添加REQUIRE SSL选项。

GRANT USAGE ON *.* TO 'repl'@'%.mydomain.com'REQUIRE SSL;

3.8半同步复制

在MySQL5.6中以插件方式支持。

默认情况下为异步复制,master写事件到二进制日志而不关心slave是否以及何时收到并处理这些日志。此时可能发生的情况是,master崩溃了,而在master上提交的事务还没有通过日志到达任何slave,如果此时将master切换到slave可能导致得到一个丢失了事务的master。

半同步复制中(master和至少一台slave均需开启半同步复制功能),在master上执行了事务提交的线程会在事务提交后阻塞直到至少一个半同步复制的slave确认已经收到了该事务的所有事件或者直到超时。而slave仅在该事务的时间已经写进中继日志且被刷到磁盘后才会确认收到了事务的事件。若直至超时master也没有收到slave确认,则复制将切回异步复制。当至少一台支持半同步复制的slave追上了master,master会重新切回半同步复制。当master中回滚动作被写进二进制日志(当事务修改了非事务型表而又被回滚时会发生,因为这一过程中尽管事务型表不受影响但非事务型表发生了变化)时也会阻塞该线程。对于没有发生在事务环境(指的是没有明确执行start transaction或set autocommit=0)的语句会开启autocommit,每条语句隐含的自动提交,在半同步复制中,master会在每条这样的语句被自动提交后阻塞。

还有一种复制方式为同步复制,master提交事务后,所有的slave必须也提交了该事务,此时master才能返回到执行事务的session继续处理后续语句。该方式明显的缺点是会造成很大的延迟。

与异步复制相比,半同步复制改善了数据完整性,与同步复制相比,半同步复制降低了延迟。半同步复制还能有效放缓繁忙的session,因为它可以限制二进制日志事件发送的速度,这在某些部署环境中可能是有用的。尽管如此,半同步复制中还是有丢失一个事务的可能,比如master提交了事务,但是在等待slave确认时崩溃了,这样该事务可能还没到达任何slave。另外,半同步复制对性能也由一些影响,因为需等待slave确认,事务提交的速度会降低。

3.8.1半同步复制管理接口

Ø  两个插件,分别用于master和slave。

ü  rpl_semi_sync_master:master端半同步复制插件

ü  rpl_semi_sync_slave:slave端半同步复制插件

Ø  三个主要的系统变量,用于控制半同步复制插件行为。

ü  rpl_semi_sync_master_enabled:开启master端半同步复制

ü  rpl_semi_sync_master_timeout:等待确认超时时间

ü  rpl_semi_sync_slave_enabled:开启slave端半同步复制

Ø  五个主要的状态变量,用于监控半同步复制状态。

ü  Rpl_semi_sync_master_clients:半同步复制slave数

ü  Rpl_semi_sync_master_status:master半同步复制状态

ü  Rpl_semi_sync_master_no_tx:slave没有确认的事务数

ü  Rpl_semi_sync_master_yes_tx:slave确认的事务数

ü  Rpl_semi_sync_slave_status:slave半同步复制状态

其余系统/状态变量参考官方手册。

3.8.2半同步复制安装与配置

Ø  前置条件

ü  MySQL5.5或更高版本。

ü  MySQL支持动态载入(通过have_dynamic_loading系统变量判断)以便安装插件。

ü  已搭建好复制关系。

Ø  首先确认plugin目录(plugin_dir变量指定)内存在所需半同步复制插件

Ø  在master上执行:mysql>INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';

Ø  在slave上执行:mysql>INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';

Ø  通过show plugin命令或者查询information_schema.plugin表以确认是否装好了插件。

Ø  插件装好后默认关闭,通过设置合适的系统变量来开启并控制其行为。

ü  master端
mysql> SET GLOBAL rpl_semi_sync_master_enabled = 1;
mysql> SET GLOBAL rpl_semi_sync_master_timeout = N;

ü  slave端
mysql> SET GLOBAL rpl_semi_sync_slave_enabled = 1;

Ø  若是在slave运行过程中开启的半同步复制,则需重启slave的I/O线程以便slave重新连接到master注册为半同步复制slave,否则复制仍为异步复制。

mysql> STOP SLAVE IO_THREAD; START SLAVEIO_THREAD;

Ø  在服务器启动时,也可将这些变量以命令行的方式应用或者写进配置文件。写进配置文件的设置可在每次服务器启动时生效。

ü  master端
[mysqld]
rpl_semi_sync_master_enabled=1
rpl_semi_sync_master_timeout=1000 # 1 second

ü  slave端
[mysqld]
rpl_semi_sync_slave_enabled=1

3.8.3半同步复制的监控

通过检查系统变量了解半同步复制如何配置

mysql> SHOW VARIABLES LIKE 'rpl_semi_sync%';

通过检查状态变量了解半同步复制的操作

mysql> SHOW STATUS LIKE 'Rpl_semi_sync%';

需注意的是当master因阻塞超时或slave又重新追上master而由半同步复制切回异步复制或由异步复制切回半同步复制时会设置Rpl_semi_sync_master_status状态变量的值。半同步复制与异步复制之间这种自动切换并不会改变rpl_semi_sync_master_enabled系统变量的值,所以不能仅通过该值来判断master当前是否在用半同步复制或异步复制,而要同时检查Rpl_semi_sync_master_status状态变量。Rpl_semi_sync_slave_status与Rpl_semi_sync_slave_status亦如此。

3.9延时复制

MySQL5.6开始支持延时复制,该复制方式中slave有意的至少滞后于master指定的秒数。可通过在change master to语句中设置MASTER_DELAY = N来指定延迟的秒数。这样一来从master接收到的事件需在至少滞后于该事件在master上的执行时间N秒后才会在slave上执行。例外情况是,仅影响SQL线程内部状态的格式描述事件或日志文件轮换事件不会延迟。

延时复制可用于如下目的

Ø  避免用户在master上的误操作带来的影响。

Ø  测试有延迟时的系统行为。

Ø  检查数据库在之前一段时间是什么样子。

start slave语句和stop slave语句会忽略延迟而立即生效。reset slave语句会将设定的延迟重置为零。

show slave status语句显示的结果中有三个字段提供了相关信息

Ø  SQL_Delay:非负整数,指定slave必须滞后于master的秒数。

Ø  Slave_SQL_Running_State:字符创,指明SQL线程的当前状态。该值与showprocesslist结果中SQL线程的state字段相同。当slave SQL线程执行事件前在等待规定的延迟,则字段的值为” Waiting until MASTER_DELAY seconds after masterexecuted event”.

Ø  SQL_Remaining_Delay:当Slave_SQL_Running_State为”Waiting until MASTER_DELAY seconds after masterexecuted event”时,该字段为一整数指明了延迟剩余的秒数。在其他时间该字段为NULL。

设置了延时复制后relay-log.info文件会包含设置的延迟值,文件的第一行指出了文件的行数。若从MySQL5.6降级到旧的版本,旧版MySQL不能正确读取该文件。可通过编辑该文件删除包含行数的其实行来解决问题。


参考

http://dev.mysql.com/doc/refman/5.6/en/replication.html

 

你可能感兴趣的:(mysql,Replication)