mysql主从原理(异步复制)

Posted lonuve

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mysql主从原理(异步复制)相关的知识,希望对你有一定的参考价值。

主从复制作用:数据分布(异机或异地)---负载均衡----备份----高可用和故障转移----升级测试.

主从原理:

  线程介绍:

主服务器的一个工作线程:

DUMP线程,作用:接收到从库发来的请求后,负责给slave服务器发送二进制日志

从服务器的两个工作线程:

I/O线程:

作用:负责读取主服务器的二进制日志,并将其保存到自己的中继日志文件中。

SQL线程:

作用:来复制执行中继日志。

注意从库的IO线程和SQL线程是分开的,互不影响.


1: slave发送请求:(从库IO线程负责)

slave节点start slave后,会向master进行连接,并且请求从binlog的指定位置开始同步。

(从库的IO线程负责请求,请求中包括主库的账号/密码/端口/binlog-file+pos点等)

2:Master接受请求并发送数据:(主库DUMP线程负责)

master收到slaveIO线程的同步请求后,masterDUMP线程开始根据请求信息读取指定日志位置的日志信息,返回给slave端的io线程,这个信息包括了日志内容,binlog的文件名称和位置。

3:slave写入relay log:(由从库IO线程负责)

slaveIO线程接收到master的消息后,开始将这个消息写入到relay_log的末尾,同时把master的位置信息写入到master.info中,下次同步会读取master.info的位置信息。

4:Slave执行解析后的sql(由从库SQL线程负责)

slavesql线程检测到relay

log中新增的内容后,会进行相关的日志解析,生成对应的语句,并且在slave执行这些语句。同时把回放位置写入到Relay_log_info.

最终的实质是,在slave端执行了和master同样的sql语句

IO线程作用:连接主库,并且接收主库发送的数据,并且写入到从库relay_log.

SQL线程作用:  负责回放relay_log

主从参数介绍:

  主库参数(修改binlog需要重新启动数据库):

binlog-do-db=#  需要复制的库

binlog-ignore-db=#  需要被忽略的库

max_binlog_size=2048M

默认为1024M

binlog_format=ROW  #  必须为ROW

transaction-isolation=READ-COMMITTED     ---事务隔离级别为RC

expire_logs_days=7 #

binlog保留多少天,看公司计划安排

server-id=111

必须和所有从机不一样,且从机之间也不一样

binlog_cache_size=#

binlog  缓存的大小,设置时要当心

sync_binlog=1

必须设置为1,默认为0

innodb_flush_log_at_trx_commit=1 #  提交事物的时候刷新日志

innodb_support_xa=1

从库参数:

log_slave_updates

SQL线程回放的数据写入到从机的binlog中去(用于级联复制)

replicate-do-db=#  需要复制的库

replicate-ignore-db=#  需要忽略的库

replicate-do-table=#  需要复制的表

replicate-ignore-table=  需要忽略的表

server-id=112

必须在一个复制集群环境中全局唯一,可通过看auto.cnf查看.

relay-log-recover=1 # I/O

thread crash safe  –  IO线程安全

relay_log_info_repository=TABLE # SQL thread crash safe  –  SQL线程安全

Skip-slave-start=1  slave数据库启动的时候,不会自动开启复制.

master

info  主要记录IO线程同步主库的数据的位置,以便于在重启主从的时候,可以从上次同步的位置开始同步.通过参数查看:

  show

variables like ‘%master_info_re%‘;

Relay_log_info主要记录SQL线程回放relay_log位置的信息.

show variables like

‘%relay_log_info%‘;

查看主库拥有的从库的个数在主库执行  mysql>

show slave hosts;

可以有多个从库,但从库之间的uuid不能一致,若一致,可以修改配置文件中的service_id,删除数据文件中的auto.cnf,再重新开启数据库.

read_only=1  限制,就是read_only=1对有super不起作用,

super_read_only  对所有用户起作用

搭建主从后,开启read_only,保证从库其他用户不写入数据

主从全新搭建过程:

  若都是空库,则直接配置主从.若主库在运行,则先将主库mysqldump导出,再导入到从库,最后配置主从复制.

  1:主库创建用户并授权.

  reset

master;

  grant

REPLICATION CLIENT,REPLICATION SLAVE on *.* to [email protected]‘192.168.%‘

identified by ‘rep‘;

  flush

privileges;

  然后查看主库binlog位置和pos:   show master

status;

执行完此步骤后不要再操作主服务器MYSQL,防止主服务器状态值变化.

  2:从库配置slave.

change master to

master_host=‘192.168.88.2‘,master_user=‘rep‘,master_password=‘rep‘,

master_log_file=‘mysql-bin.000002‘,master_log_pos=154,MASTER_PORT=3307;

其中master_log_filemaster_log_pos是主库show master status的结果.

  3:开启和查看slave.

开启主从复制:   start slave;

查看主从状态:   show slave

status\G;

  4:主库关闭读锁:

unlock table;

主从状态(show slave

status\G;)字段含义:

 

mysql> show slave status\G;

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

***************************

Slave_IO_State: Waiting for master to send event

Master_Host: 192.168.60.22

Master_User: rep

Master_Port: 3306

Connect_Retry: 60

                      ----尝试重连的时间间隔(单位s)

Master_Log_File:

mysql-bin.000006

          ----当前主服务器的binlog日志

Read_Master_Log_Pos: 1173

              -----当前主服务器的事务pos.

Relay_Log_File: hadoop02-relay-bin.000002

    ---当前中继日志

Relay_Log_Pos: 1336

                    ------中继日志的写入位置.

Relay_Master_Log_File: mysql-bin.000006

  ---当前同步主库的binlog日志

Slave_IO_Running:

Yes-----------------此状态必须YES

Slave_SQL_Running:

Yes-----------------此状态必须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:

1173----当前中继日志的回放位置.

Relay_Log_Space: 1512

Seconds_Behind_Master: 0   -----是否延迟.(Read_Master_Log_Pos-Exec_Master_Log_Pos)

主从参数说明:

Slave_IO_state:显示当前IO线程的状态,一般情况下就是显示等待主服务器发送二进制日志。

Master_log_file:显示当前同步的主服务器的binlog日志。

Read_master_log_pos:显示主服务器上binlogpos点。

上面两个参数决定了目前主库的binlog的位置.

Relay_master_log_file:当前中继日志同步的主库的哪个binlog日志。

Relay_log_file:显示当前写入的中继日志。

Relay_log_pos:显示当前写入到中继日志的pos点。

上面两个参数决定了中继日志的写入位置.

Slave_IO_running:从服务器中IO线程的运行状态,yes代表正常

Slave_SQL_running:从服务器中sql线程的运行状态,YES代表正常

上面两参数决定了主从是否正常

Exec_Master_log_pos:表示从库SQL线程应用中继日志的pos点。可Read_master_log_pos相比较,若相等,则表示无延迟.

  Seconds_Behind_Master:0说明主从无延迟,null则可能有问题,在增大则说明延迟在增加.


在线搭建主从:

  可能主库的binlog已丢掉,故需先在线导出主库,导入到从库,再根据导出的binlogpos点开始主从.

  在从库执行下面语句:

    Stop

slave ;    ---先停止slave:

   

    reset

slave all;     ---Reset

slave  其实是把master.info  relay-log.info文件删除掉,但是同步信息还存在,因此有人如果再次开启start slave,又会重新开启同步了。故用reset slave

all.

    reset

master; insert into ....

            ------之前的binlog文件丢了.

  1:sql执行导出主库  mysqldump

-uroot -p -S /tmp/mysql3307.sock --master-data=2

--single-transaction   -A >

/tmp/all.sql

  2:在从库导入:   sql里执行  source /tmp/all.sql.

导完后再对主库做一些插入修改操作,改变binlogpos,但导出文件时及之后的binlog文件不能丢.

  Vim

/tmp/all.sql  在第22行左右有:

-- CHANGE MASTER TO

MASTER_LOG_FILE=‘mysql-bin.000001‘, MASTER_LOG_POS=422;

说明binlog文件是bin.000001,pos点是422.

而因主库一直在做变动,故此时查看主库会发现pos点并不是422.(show master status;)

根据第4步结果修改:master_log_filemaster_log_pos参数.

change master to

master_host=‘192.168.88.2‘,master_user=‘rep‘,master_password=‘rep‘,

master_log_file=‘mysql-bin.000001‘,master_log_pos=422,MASTER_PORT=3307;

参数修改完后在数据库里执行上面语句.

  开启主从:Start slave;

                查看主从状态:

      show slave

status\G;

  查看主库拥有的从库的个数在主库执行  mysql>

show slave hosts;

  stop slave;

set global

sql_slave_skip_counter=1;

start slave;

  主库加锁:主库加读锁,防止主库有新数据写入,主要是针对MySIAM表,但是这个锁加入后,业务无法正常操作,这个还是要权衡数据库备份数据要求和业务的要求的。

flush tables with read

lock;

主库数据备份:必用参数

--single-transaction  启动单一事务备份,这样保证备份是某个时间点的数据,而不会造成数据的不一致

master-data  这个参数会在备份文件中,记录备份时的binlogpos位置,用于从库恢复数据后的主从搭建

/usr/local/mysql/bin/mysqldump

-uroot -pLR9fjxm3g -S /tmp/mysql3307.sock -A --single-transaction

--master-data=2

  >/tmp/20160304alls.sql

传输到从库将备份文件scp或者ftp到从库使用

scp /tmp/20160303all2.sql

  [email protected]:/tmp/

从库恢复:在从库恢复主库备份过得数据文件

mysql -uroot -p -S

/tmp/mysql3307.sock

或者进入数据库后使用source命令(建议使用source命令,因为source命令会打印结果)

查找主库pos:

使用head命令查找前35行备份文件,然后找到描述master  binlog的位置

head -n 35

/tmp/20160303all2.sql

CHANGE MASTER TO

MASTER_LOG_FILE=‘mysql-bin.000007‘,

MASTER_LOG_POS=88940;

从库进行指定:

change master to

master_host=‘192.168.95.100‘,master_user=‘rep‘,master_password=‘rep‘,

master_log_file=‘mysql-bin.000007‘,master_log_pos=88940,MASTER_PORT=3307;

开启:start slave

进行start slave后,从库会有一个追逐主库的过程,在追完主库后,可以进行业务的迁移或者作为从库使用.

  1.主从延迟发展:

5.5版本

单线程复制

5.6版本

并行复制,只实现库级别的并行复制,业务,不会跨库访问

但大多数操作都是单库多表,而不是多库多表,意义不大.

5.7  推出表级别并行复制,可以实现表级别的并行复制

8.0  推出了基于writeset(写集)的复制模式(表里面必须有主键或者唯一索引)

Binlog内容增加了,增加了是否可并行复制的记录内容:

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

组提交时提交了6个事务,而这6个事务在从机是可以进行并行回放的。

多线程的思路就是把sql_thread  变成分发线程,然后由一组worker_thread来负责执行

Sql_thread的任务是分发线程,worker_thread线程的作用是回放中继日志.

5.7的表级别并行复制基于last_committed,相同的last_committed越多,则并行复制效率越高,故从库的并发复制能力受制于主库的并发能力.主库的并发越高,从库的并发复制能力一般越高,性能越好.

MySQL 5.7基于表的并行复制

slave-parallel-type=LOGICAL_CLOCK

slave-parallel-workers=16

master_info_repository=TABLE

relay_log_info_repository=TABLE

slave_preserve_commit_order=1

Slavecommit的顺序保持一致,必须为1,否则可能会有GAP锁产生

参数介绍:

relay_log_info_repositorymaster_info_repository5.7设置为保存到TABLE比较合适

slave-parallel-type:

5.7为了兼容5.6版本的库复制,增加了slave-parallel-type=LOGICAL_CLOCK参数

5.6库级复制参数值为DATABASE

              5.7表级别复制参数值为LOGICAL_CLOCK

slave-parallel-workers  :从库并发数目,设置大于4比较合适

在线调整:

mysql> set global

slave_parallel_workers=8;

mysql> stop slave;

--  一定要重启一下slave才能有效

mysql> start

slave;

故主库的并发越高,从库的并发复制能力一般越高,性能越好.

1.show slave status显示参数Seconds_Behind_Master不为0,这个数值可能会很大,

2.Read_Master_Log_PosExec_Master_Log_Pos显示bin-log的编号相差很大,说明bin-log在从库上没有及时同步,所以近期执行的bin-log和当前IO线程所读的bin-log相差很大

3.MySQL的从库数据目录下存在大量mysql-relay-log日志,该日志同步完成之后就会被系统自动删除,存在大量日志,说明主从同步延迟很厉害.

 

1.低版本的串行复制导致:

      当主库的TPS并发较高时,产生的DDL数量超过slave一个sql线程所能承受的范围,那么延时就产生了,当然还有就是可能与slave的大型query语句产生了锁等待。故需尽量避免在主库频繁的执行大事务(可以将大事务拆成小事务执行)

2.主库压力太大.  

3.主从走公网,带宽流量不够,造成了延迟.

首要原因:数据库在业务上读写压力太大,CPU计算负荷大,网卡负荷大,硬盘随机IO太高

次要原因:读写binlog带来的性能影响,网络传输延迟。

 

硬件方面考虑:

1.更换从库更快的硬盘

2.网络,网卡,更换带宽更大的网卡

mysql考虑  :从库作为备份数据库来说

3.增加从库的innodb_buffer_pool_size,可以缓存更多数据防止由于转换造成的IO压力.

4.增加innodb_log_file_sizeinnodb_log_files_in_group,减少buffer落盘.

5.修改参数innodb_flush_method,提高写入性能(SSD强烈推荐使用)

6.从库binlog关闭(如果可以),

log_slave_updates关闭.

7.修改innodb_flush_log_at_trx_commitsync_binlog  0或者2

8.修改master_info_repositoryrelay_log_info_repositoryTABLE,防止直接落盘压力

9.升级5.7.因为:mysql5.5只支持一个线程复制.

mysql5.6  支持库之间的同步复制.

mysql5.7支持表之间的同步复制.

从库宕机后,因为主从报错,导致从库无法启动,

解决方法:启动的加一个参数--skip-slave-start

mysqld_safe

--skip-slave-start --user=mysql &

mysql主库异常宕机情况下,如果未设置sync_binlog=1或者innodb_flush_log_at_trx_commit=1很有可能出现binlog或者relaylog文件出现损坏,导致主从不一致。

1. redotrx_prepare未写入,但binlog写入,造成从库数据量比主库多。

2. redotrx_preparecommit都写入了,但是binlog未写入,造成从库数据量比主库少。

很显然,如果我们弱化配置的持久性( innodb_flush_log_at_trx_commit !=1  或者sync_binlog !=1 )

宕机可能导致两种丢数据的场景:

引擎层提交了,但binlog没写入,备库丢事务;

引擎层没有prepare,但binlog写入了,主库丢事务。

即使我们将参数设置成innodb_flush_log_at_trx_commit=1  和  sync_binlog=1,也还会面临这样一种情况:主库crash时还有binlog没传递到备库,如果我们直接提升备库为主库,同样会导致主备不一致,老主库必须根据新主库重做,才能恢复到一致的状态。

IO线程出问题:

1.主库的binlog问题

比如找不到主库的binlog

2.主库的权限不对,IO

connectiong...

3.relay log写入可能出问题,具体出问题的位置,看报错

SQL线程出问题:

1.主键冲突

2.从库少数据

3.主从的数据的格式不对(int char

vvarchar)

ERROR 1062 :从库插入数据,发生唯一性冲突

ERROR 1032:从库找不到要删除的数据

ERROR 1452:无法在外键的表中插入或者更新参考主键没有的数据

  如何保证主从数据一致性:保证IO线程高可靠性和SQL线程高可靠性.

IO线程接收一个个的event  ,将接收到的event,通过设置参数master_info_repository  可以将  master-info  信息(IO线程接收到的位置,Master_log_nameMaster_log_pos

)写入到文件(

FILE )或者数据库( TABLE )中。然后将

接收到的event写入relay log

file

参数  sync_master_info=10000  表示

接收10000event,写一次master-info

存在问题:  master-info.log  和  relay-log  无法保证一致性。

relay-log-recover=1:该参数表示当前接收到的relay-log全部删除,然后从SQL线程回放到的位置重新拉取(SQL线程通过配置后是可靠的).即不再从master_info_repository里查找,而是从SQL线程回放的位置重新开始执行.

relay_log_info_repository=TABLE

event的回放和relay-info的更新放在同一个事物里面,变成原操作,从而保证一致性(要么都写入,要么都不写)

每一次事物提交,都会写入mysql.slave_relay_log_info中,sync_relay_log_info=N将被忽略(原本sync_relay_log_info是设置回放多少次才落盘relay_log_info相应文件中.),回放的具体pos点必定就是relay_log_info中的值了.相当于sync_relay_log_info=1的情况.

三个参数可以保证复制的安全性:

relay-log-recover=1

----保证IO安全性

relay_log_info_repository=TABLE ----保证SQL线程安装性

master_info_repository=TABLE

      ----提高性能

master_info_repository设置为TABLE或者FILE对复制的可靠性是没有帮助的,因为设置relay-log-recover=1后,会重新通过SQL线程回放到的位置进行拉取。但是  master_info_repository  也一定要设置为  TABLE  ,性能上比设置为  FILE  有很高的提升(官方BUG)

设置为  TABLE  后,  master-info将信息保存到  mysql.slave_master_info  .

总结:通过relay_log_info_repository=table保证了SQL线程回放点的准确性,再让IO线程依赖SQL线程的准确回放点开始写入中继日志.保证了中继日志的准确性.

  1.  主库的MySQL版本应该低于从库的MySQL版本

2. mariadbmysqlbinlog格式不一样,不能进行主从的搭建


以上是关于mysql主从原理(异步复制)的主要内容,如果未能解决你的问题,请参考以下文章

mysql数据库主从同步复制原理

MySQL主从复制原理

深度探索MySQL主从复制原理

MySQL主从复制原理

Mysql 主从复制原理

MySQL的主从复制原理以及流程