mysql主从复制

一:mysql复制默认以什么方式进行、复制粒度

二:mysql复制的优势

三:mysql复制的方法

四:mysql复制的多种类型

五:记录binlog的三种格式

六:mysql基于binlog复制的原理以及搭建过程

七:mysql复制相关的参数

八:延迟复制的原理以及搭建过程

九:半同步复制的原理以及搭建过程

十:基于GTID复制的原理、搭建过程以及限制条件

 

•MySQL复制允许将主实例(master)上的数据同步到一个或多个从实例(slave)上,默认情况下复制是异步进行的,从库也不需要一直连接到主库来同步数据

• MySQL复制的数据粒度可以是主实例上所有的数据库,也可以是指定的一个或多个数据库,也可以是一个数据库里的指定的表

• MySQL复制所带来的优势在于:
• 扩展能力:通过复制功能可以将MySQL的性能压力分担到一个或多个slave上。这要求所有的写操作和修改操作都必须在Master上完成,而读操作可以被分配到一个或多个slave上。
将读写分离到不同服务器执行之后,MySQL的读写性能得到提升
• 数据库备份:由于从实例是同步主实例的数据,所以可以将备份作业部署到从库
• 数据分析和报表:同样,一些数据分析和报表的实现可以在从实例执行,以减少对主库的性能影响
• 容灾能力:可以在物理距离较远的另一个数据中心建立一个slave,保证在主实例所在地区遭遇灾难时,在另一个数据中心能快速恢复

MySQL复制有两种方法
• 传统方式:基于主库的bin-log将日志事件和事件位置复制到从库,从库再加以应用来达到主从同步的目的
• Gtid方式:global transaction identifiers是基于事务来复制数据,因此也就不依赖日志文件,同时又能更好的保证主从库数据一致性

MySQL复制有多种类型:
• 异步复制:一个主库,一个或多个从库,数据异步同步到从库
• 同步复制:在MySQL Cluster中特有的复制方式
• 半同步复制:在异步复制的基础上,确保任何一个主库上的事务在提交之前至少有一个从库已经收到该事务并日志记录下来
• 延迟复制:在异步复制的基础上,人为设定主库和从库的数据同步延迟时间,即保证数据延迟至少是这个参数

• 并行复制:SQL线程实现了多线程,来提升slave的并发度

 

MySQL复制有三种核心格式
• 基于语句的复制(statement based replication):基于主库将SQL语句写入到bin log中完成复制
• 基于行数据的复制(row based replication):基于主库将每一个行数据变化的信息作为事件写入到bin log中完成日志
• 混合复制(mixed based replication):上述两者的结合。默认情况下优先使用基于语句的复制,只有当部分语句如果基于语句复制不安全的情况下才会自动切换为基于行数据的复制

mysql复制的工作原理:

MySQL复制涉及三个线程,其中一个在主库,另两个在从库
• binlog dump thread:在主库创建,用来在从库链接过来时发送bin log的内容
• slave io thread:在备库创建,用来连接主库并请求发送新的bin log内容。该线程读取主库的bin log dump线程发送的更新内容并将此内容复制到本地的relay log中
• Slave sql thread:在备库创建,读取slave io线程在本地relay log中的内容并在本地执行内容中的事件

MySQL基于binlog的复制

基于binary log的复制是指主库将修改操作写入到bin log中,从库负责读取主库的bin log,并在本地复制一份,然后将里面的操作在从库执行一遍,每个从库会保存目前读取主库日志的文件名和日志位置主库和每个从库都必须有一个唯一ID,叫server-id配置在配置文件中

传统主从环境的搭建步骤
主库:
1.安装完成mysql
2.修改主机的配置文件打开binlog
server-id=1 ##取值范围 1~2^32-1
log-bin=/dbdata/data/bin-log
log-bin-index=/dbdata/data/bin-log.index
3.创建同步账号
CREATE USER 'repl'@'192.168.237.%' IDENTIFIED BY 'mysql';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.237.%';
4.在从库用用同步账号登录主库测试账号
5.备份全库并把备份文件copy到从库恢复(备份之前全库加锁;flush tables with read lock;) 生产环境中不适用lock   --single-transaction --master-data=2;
6.记录show master status \G 位置

从库:
1.修改从库的配置文件
server-id=2
relay-log=/dbdata/data/relay-log
relay-log-index=/dbdata/data/relay-log.index
2. CHANGE MASTER TO
MASTER_HOST=‘master_host_name’, ##主库的主机名
MASTER_PORT=port_number ##主库的端口号
MASTER_USER=‘replication_user_name’, ##复制的数据库用户名
MASTER_PASSWORD=‘replication_password’, ##复制的用户密码
MASTER_LOG_FILE=‘recorded_log_file_name’, ##主库的日志文件名
MASTER_LOG_POS=recorded_log_position; ##主库的日志文件位置
例:
CHANGE MASTER TO
MASTER_HOST='192.168.10.241',
MASTER_PORT=3306,
MASTER_USER='repl',
MASTER_PASSWORD='mysql',
MASTER_LOG_FILE='mysql-bin.000013',
MASTER_LOG_POS=154;
3.start slave;

• 查看主备库复制是否正常:

• 在slave上执行show slave status\G命令

seconds_behind_master 指的是什么意思?从库延迟时间?

官网解释:

 

seconds_Behind_Master: The number of seconds that the slave SQL thread is behind processing the master binary log
也就是说,是SQL thread在执行IO thread dump下来的relay log的时间差。大家都知道relay log中event记录的时间戳是主库上的时间戳,而SQL thread的时间戳是从库上的,也就是说,如果主库和从库的时间是一致的,那么这个SBM代表的确实是从库延后主库的一个时间差

 

 


 

复制相关的参数

• log_slave_updates:该参数用来控制是否将收到的主库的更新数据的语句也记录在slave自己的bin log中。正常情况下是不需要记录的,但如果是想创建级联复制关系,比如A -> B -> C,这其中B既要作为A的从库,也要作为C的主库,则需要既开启log-bin参数,也要开启log_slave_updates参数

• replicate-do-db:该参数用来指定需要复制的数据库

• replicate-ignore-db:该参数决定了忽略指定数据库的复制,其行为和replicate-do-db正好相反

• replicate-do-table=db_name.tbl_name:通过该参数告知slave的SQL thread仅复制指定表上的数据。如果有多个表,则该参数要使用多次

• replicate-ignore-table=db_name.tbl_name:通过该参数告知slave的SQL thread将指定表上的数据过滤掉

• replicate-wild-do-table=db_name.tbl_name:通过该参数告知SQL的SQL thread仅复制符合匹配的表,可以使用_和%作为通配符。比如replicate-wild-dotable=foo%.bar%表示复制以foo打头的数据库下所有bar打头的表数据。如果是replicate-wild-do-table=foo%.%,则表示即复制foo打头的所有表的数据,也复制create/drop/alter database foo打头的命令

• replicate-wild-ignore-table=db_name.tbl_name:通过该参数告知SQL的SQL thread过滤掉符合匹配的表

• slave-parallel-workers: 该参数决定了slave上启动多个SQL thread线程来并行应用数据的默认值是0代表不允许并行,取值范围可以是0~1024

• skip-slave-start :该参数决定了在MySQL启动时是否先不启动slave线程,即暂停复制

• slave-parallel-type=type :该参数决定了当启动了并行之后,采用什么粒度的并行方式。默认值database表示按照不同的数据库执行并行LOGICAL_CLOCK则表示按照在binlog中的一组提交的事务作为并行粒度

• slave-skip-errors=[err_code1,err_code2,...|all|ddl_exist_errors]:该参数决定了当slave的SQL thread执行过程中碰到何种错误时可以忽略并继续接下来的数据复制。正常情况下当有错误发生时,复制会停止而需要人工干预修复才能继续进行。除非非常自信可以忽略某些错误,否则不要使用这个参数,不然会导致虽然复制执行正常,但其实内部的数据已经完全不一致

• sql_slave_skip_counter代表在非GTID复制环境下,通过设置此参数来跳过多少个复制事件。设置完该参数并非立即生效,而是要等待下次start slave命令的执行生效,并将该参数再次设置为0

• binlog-do-db=db_name: 该参数决定了哪些库下的修改会被记录到bin log中。其行为与replicate-do-db类型,在基于SQL语句复制的环境下,只记录在当前数据库下的修改

• binlog-ignore-db=db_name:该参数决定了在bin log中忽略的数据库,其行为与replicate-ignore-db类型

• binlog_format:该参数决定了bin log中记录的格式

MySQL延迟复制

延迟复制是指定从库对主库的延迟至少是指定的这个间隔时间,默认是0秒。可以通过change master to命令来指定CHANGE MASTER TO MASTER_DELAY = N;
其原理是从库收到主库的bin log之后,不是立即执行,而是等待指定的秒数之后再执行

延迟复制的使用场景比如:
确保在主库上被错误修改的数据能及时找回
测试在从库IO集中在恢复bin log过程中对应用程序的访问影响,保留一份若干天前的数据库状态,和当前状态可以做对比
show slave status中SQL_Delay值表明了设置的延迟时长

MySQL半同步复制

默认创建的MySQL复制是异步的,意味着主库将数据库修改事件写入到自己的bin log,而并不知道从库是否获取了这些事件并应用在自己身上。所以当主库崩溃导致要主从切换时,有可能从库上的数据不是最新的

从5.7版本开始MySQL通过扩展的方式支持了半同步复制当主库执行一个更新操作事务时,提交操作会被阻止直到至少有一个半同步的复制slave确认已经接收到本次更新操作,主库的提交操作才会继续,半同步复制的slave发送确认消息只会在本次更新操作记录已经记录到本地的relay log之后,如果没有任何slave发送确认消息而导致超时时,半同步复制会转换成异步复制

半同步复制会对MySQL性能产生影响,因为主库的提交动作只有在收到至少一个从库的确认消息之后才能执行。但这个功能是性能和数据可靠性方面的权衡

rpl_semi_sync_master_wait_point参数用来控制半同步复制的行为:

AFTER_SYNC
AFTER_COMMIT 

需要配置的系统参数包括:
rpl_semi_sync_master_enabled:在主库配置,确保主库的半同步复制功能开启
rpl_semi_sync_master_timeout:配置主库等待多少毫秒时间来保证接收备库的确认消息,当超过这个时间时,半同步变成异步方式
rpl_semi_sync_slave_enabled:在从库配置,确保从库的半同步复制功能开启

主库【】
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
SET GLOBAL rpl_semi_sync_master_enabled = 1;
SET GLOBAL rpl_semi_sync_master_timeout = N; ##N是毫秒,默认是10000,代表10秒
从库【】
INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
SET GLOBAL rpl_semi_sync_slave_enabled =1;
需在从库重启slave线程

半同步复制监控参数:

Rpl_semi_sync_master_clients:检查半同步的slave个数
Rpl_semi_sync_master_status:1表示主库的半同步功能开启并且运行正常,0表示主库的半同步功能关闭或者半同步复制已经变成了异步复制
Rpl_semi_sync_master_no_tx:表示有多少提交没有收到slave的确认消息
Rpl_semi_sync_master_yes_tx:表示有多少个提交收到了slave的确认消息
Rpl_semi_sync_slave_status:1表示备库上slave功能开启并且运行正常,0表示功能为开启或者运行异常

通过mysql> SHOW STATUS LIKE 'Rpl_semi_sync%';查看各个参数的状态

 

mysql并行复制

 

在MySQL 5.6版本之前,Slave服务器上有两个线程I/O线程和SQL线程。I/O线程负责接收二进制日志(更准确的说是二进制日志的event),SQL线程进行回放二进制日志。如果在MySQL 5.6版本开启并行复制功能,那么SQL线程就变为了coordinator线程,coordinator线程主要负责以前两部分的内容:

 

  • 若判断可以并行执行,那么选择worker线程执行事务的二进制日志
  • 若判断不可以并行执行,如该操作是DDL,亦或者是事务跨schema操作,则等待所有的worker线程执行完成之后,再执行当前的日志

 

这意味着 coordinator线程并不是仅将日志发送给worker线程,自己也可以回放日志,但是所有可以并行的操作交付由worker线程完成

(疑问:这里是在判断不可以并行执行时,在等待所有的worker线程执行完成后,是由coordinator执行还是由worker进程非并行进行。从上面两句话中看不出coordinator进程回放日志。)

上述机制实现了基于schema的并行复制存在两个问题,首先是crash safe功能不好做,因为可能之后执行的事务由于并行复制的关系先完成执行,那么当发生crash的时候,这部分的处理逻辑是比较复杂的。从代码上看,5.6这里引入了Low-Water-Mark标记来解决该问题,其是希望借助于日志的幂等性来解决该问题,不过5.6的二进制日志回放还不能实现幂等性。另一个最为关键的问题是这样设计的并行复制效果并不高,如果用户实例仅有一个库,那么就无法实现并行回放,甚至性能会比原来的单线程更差。而 单库多表是比多库多表更为常见的一种情形 。

MySQL 5.7基于组提交的并行复制

MySQL 5.7才可称为真正的并行复制,这其中最为主要的原因就是slave服务器的回放与主机是一致的即master服务器上是怎么并行执行的slave上就怎样进行并行回放。不再有库的并行复制限制,对于二进制日志格式也无特殊的要求(基于库的并行复制也没有要求)

从MySQL官方来看,其并行复制的原本计划是支持表级的并行复制和行级的并行复制,行级的并行复制通过解析ROW格式的二进制日志的方式来完成, WL#4648 。但是最终出现给小伙伴的确是在开发计划中称为:MTS: Prepared transactions slave parallel applier,可见: WL#6314 

MySQL 5.7并行复制的思想简单易懂,一言以蔽之: 一个组提交的事务都是可以并行回放 ,因为这些事务都已进入到事务的prepare阶段,则说明事务之间没有任何冲突(否则就不可能提交)。

为了兼容MySQL 5.6基于库的并行复制,5.7引入了新的变量slave-parallel-type,其可以配置的值有:

  • DATABASE:默认值,基于库的并行复制方式
  • LOGICAL_CLOCK:基于组提交的并行复制方式

支持并行复制的GTID

如何知道事务是否在一组中,又是一个问题,因为原版的MySQL并没有提供这样的信息。在MySQL 5.7版本中,其设计方式是将组提交的信息存放在GTID中。那么如果用户没有开启GTID功能,即将参数gtid_mode设置为OFF呢?故MySQL 5.7又引入了称之为Anonymous_Gtid的二进制日志event类型,如:

mysql> SHOW BINLOG EVENTS in 'mysql-bin.000006';
+------------------+-----+----------------+-----------+-------------+-----------------------------------------------+
| Log_name | Pos | Event_type | Server_id | End_log_pos | Info |
+------------------+-----+----------------+-----------+-------------+-----------------------------------------------+
| mysql-bin.000006 | 4 | Format_desc | 88 | 123 | Server ver: 5.7.7-rc-debug-log, Binlog ver: 4 |
| mysql-bin.000006 | 123 | Previous_gtids | 88 | 194 | f11232f7-ff07-11e4-8fbb-00ff55e152c6:1-2 |
| mysql-bin.000006 | 194 | Anonymous_Gtid | 88 | 259 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| mysql-bin.000006 | 259 | Query | 88 | 330 | BEGIN |
| mysql-bin.000006 | 330 | Table_map | 88 | 373 | table_id: 108 (aaa.t) |
| mysql-bin.000006 | 373 | Write_rows | 88 | 413 | table_id: 108 flags: STMT_END_F |
......

  

这意味着在 MySQL 5.7版本中即使不开启GTID,每个事务开始前也是会存在一个Anonymous_Gtid ,而这GTID中就存在着组提交的信息。

LOGICAL_CLOCK

然而,通过上述的SHOW BINLOG EVENTS,我们并没有发现有关组提交的任何信息。但是通过mysqlbinlog工具,用户就能发现组提交的内部信息

root@localhost:~# mysqlbinlog mysql-bin.0000006 | grep last_committed
#150520 14:23:11 server id 88 end_log_pos 259 CRC32 0x4ead9ad6 GTID last_committed=0 sequence_number=1
#150520 14:23:11 server id 88 end_log_pos 1483 CRC32 0xdf94bc85 GTID last_committed=0 sequence_number=2
#150520 14:23:11 server id 88 end_log_pos 2708 CRC32 0x0914697b GTID last_committed=0 sequence_number=3
#150520 14:23:11 server id 88 end_log_pos 3934 CRC32 0xd9cb4a43 GTID last_committed=0 sequence_number=4
#150520 14:23:11 server id 88 end_log_pos 5159 CRC32 0x06a6f531 GTID last_committed=0 sequence_number=5
#150520 14:23:11 server id 88 end_log_pos 6386 CRC32 0xd6cae930 GTID last_committed=0 sequence_number=6
#150520 14:23:11 server id 88 end_log_pos 7610 CRC32 0xa1ea531c GTID last_committed=6 sequence_number=7
#150520 14:23:11 server id 88 end_log_pos 8834 CRC32 0x96864e6b GTID last_committed=6 sequence_number=8
#150520 14:23:11 server id 88 end_log_pos 10057 CRC32 0x2de1ae55 GTID last_committed=6 sequence_number=9
#150520 14:23:11 server id 88 end_log_pos 11280 CRC32 0x5eb13091 GTID last_committed=6 sequence_number=10
#150520 14:23:11 server id 88 end_log_pos 12504 CRC32 0x16721011 GTID last_committed=6 sequence_number=11
#150520 14:23:11 server id 88 end_log_pos 13727 CRC32 0xe2210ab6 GTID last_committed=6 sequence_number=12
#150520 14:23:11 server id 88 end_log_pos 14952 CRC32 0xf41181d3 GTID last_committed=12 sequence_number=13
...

  

可以发现较之原来的二进制日志内容多了last_committed和sequence_number,last_committed表示事务提交的时候,上次事务提交的编号,如果事务具有相同的last_committed,表示这些事务都在一组内,可以进行并行的回放。例如上述last_committed为0的事务有6个,表示组提交时提交了6个事务,而这6个事务在从机是可以进行并行回放的。

上述的last_committed和sequence_number代表的就是所谓的LOGICAL_CLOCK

并行复制配置与调优

master_info_repository

务必将参数master_info_repostitory设置为TABLE,这样性能可以有50%~80%的提升。这是因为并行复制开启后对于元master.info这个文件的更新将会大幅提升,资源的竞争也会变大。在之前 InnoSQL 的版本中,添加了参数来控制刷新master.info这个文件的频率,甚至可以不刷新这个文件。因为刷新这个文件是没有必要的,即根据master-info.log这个文件恢复本身就是不可靠的。在MySQL 5.7中,Inside君推荐将master_info_repository设置为TABLE,来减小这部分的开销。

slave_parallel_workers

若将slave_parallel_workers设置为0,则MySQL 5.7退化为原单线程复制,但将slave_parallel_workers设置为1,则SQL线程功能转化为coordinator线程,但是只有1个worker线程进行回放,也是单线程复制。然而,这两种性能却又有一些的区别,因为多了一次coordinator线程的转发,因此slave_parallel_workers=1的性能反而比0还要差

配置:

【从库】

master_info_repository = TABLE
relay_log_info_repository = TABLE
relay_log_recovery = 1
slave-parallel-type = LOGICAL_CLOCK
slave-parallel-workers = 16
slave_preserve_commit_order=1
slave_transaction_retries=128

 

MySQL基于GTID的复制

GTID(global transaction identifiers)复制是完全基于事务的复制,即每个在主库上执行的事务都会被分配一个唯一的全局ID并记录和应用在从库上这种复制方式简化了建立slave和master/slave之间切换的工作,因为其完全不需要找当前执行的bin log和log中的位置完成切换,一个GTID是master上执行的任何commit事务所分配的全局唯一ID标示,其由两部分组成:GTID = source_id:transaction_id
Source_id代表主库的server_uuid,transaction_id代表事务按顺序提交的ID,比如第一个提交则是1,第十个提交的事务就是10,GTID集合代表一组GTID

GTID复制原理

① 当一个事务在主库提交时,该事务就被赋予了一个GTID,并记录在主库的binary log
② 主库的binary log会被传输到从库的relay log中,从库读取此GTID并生成gtid_next系统参数
③ 从库验证此GTID并没有在自己的binary log中使用,则应用此事务在从库上

mysql5.7.4开始;gtid_executed系统表记录同步复制的信息(UUID:事务号),这样就可以不用开启log_slave_updates参数,减少了从库的压力

GTID复制【】
主从一样都需要添加;同步记录会存放在mysql系统库的gtid_executed表中
gtid-mode=on
enforce-gtid-consistency=on
从库【】
stop slave ;
reset slave all;
CHANGE MASTER TO
MASTER_HOST=‘master_host_name’, ##主库的主机名
MASTER_PORT=port_number ##主库的端口号
MASTER_USER=‘replication_user_name’, ##复制的数据库用户名
MASTER_PASSWORD=‘replication_password’, ##复制的用户密码
MASTER_AUTO_POSITION = 1;
主从复制过程中参数详解:
replicate-do-db :在从库定义的
binlog-do-db:在主库定义的,那些库中的修改可以记录到二进制日志中

使用GTID复制的限制条件:
由于GTID复制是依赖于事务的,所以MySQL的一些属性不支持,当一个事务中既包含对InnoDB表的操作,也包含对非事务型存储引擎表(MyISAM)的操作时,就会导致一个事务中可能会产生多个GTID的情况;或者是当master和slave的表使用的存储引擎不一样时,都会导致GTID复制功能不正常
create table…select语句在基于语句复制的环境中是不安全的,在基于行复制的环境中,此语句会被拆分成两个事件,一是创建表,二是insert数据,在某些情况下这两个事件会被分配相同的GTID,而导致insert数据的操作被忽略,所以GTID复制不支持create table … select语句create/drop temporary table语句在GTID复制环境中不能放在事务中执行,只能单独执行,并且autocommit要开启sql_slave_skip_counter语句是不支持的,如果想要跳过事务,可以使用gtid_executed变量

基于GTID复制跳过错误实际应用:

1.stop slave;
2.set GTID_NEXT='f0e1cd81-46cc-11e8-87fb-00163e321fb5:5600010'; 注:GTID_NEXT=Executed_Gtid_Set 当前执行的事物+1
3.begin; commit; 执行一个空事物
4.SET @@SESSION.GTID_NEXT= 'AUTOMATIC';
5.start slave

 

参考:

https://www.cnblogs.com/xiaotengyi/p/5532191.html

posted @ 2019-03-19 10:34  h_s  阅读(248)  评论(0编辑  收藏  举报