基本原理
mysql的主从复制默认基于异步复制,主要有三个进程参与,分别是主mysql上mysqldump进程,从mysql的i/o进程和sql进程。
执行过程:
- 从mysql的I/O进程向主mysql上mysqldump进程发起请求,读取主服务的本地binlog,主服务将数据通过3306端口发送给从mysql。
- 从mysql的I/O进程将数据存放到中继日志中,同时在master-info文件中记录所获取的binlog数据对应的日志文件名和日志读取点位置,便于告知主mysql下次发送二进制日志数据的起始点。
- 从mysql的sql进程检测到中继日志有数据后,在从mysql本地执行新加的语句,并将执行的语句的结果,存入数据库。
优点
MySQL的主服务器上的I/O线程,将变更数据按照事务提交的顺序写到binlong中后,就直接返回给客户端数据更新成功,主服务不会考虑从服务是否发起同步请求。所以不管从同步还是不同步,都不会影响到主库的数据写入。
缺点
如果主挂了,主的二进制日志还没来得及发给从,又没有其它措施保存二进制日志,那数据就丢了。
搭建步骤
master
- 创建监控主从状态的用户
grant replication slave,replication client on *.* to myreplic@'<slave_ip>' identified by '123456';
flush privileges;
💥做主从服务器的原则是,MYSQL版本要相同,如果不能满足,最起码从服务器的MYSQL的版本必须高于主服务器的MYSQL版本
- 主关键配置
[mysqld]
server-id=100
log-bin=mysql-bin
binlog-ignore-db=mysql
binlog_format=row
binlog_row_image=FULL
max_allowed_packet=128M
💥最好是只设置binlog-ignore-db。在5.5的版本中,同时设置了binlog-do-db和binlog-ignore-db会导致日志不再记录。
✨binlog-do-db
可以只记录某个库的二进制日志。
- 锁表、备份、二进制日志信息
导出包括锁表语句以及master的二进制日志语句,均可以用下列一条命令处理
mysqldump --all-databases --lock-all-tables --master-data=2 -p<密码> > slave.sql
slave
- 从关键配置
server-id=200
replicate-ignore-db=mysql
relay-log=relay-bin
relay_log_index=relay-bin.index
max_allowed_packet=128M
- 导入全量数据
mysql –u root –p<密码> < slave.sql
- 链接主,并告知主从哪开始发日志数据
语句可以从 slave.sql 里获取
mysql> change master to MASTER_HOST='<master_ip>', MASTER_USER='myreplic', MASTER_PASSWORD='123456', MASTER_LOG_FILE='master-bin.xxxxx', MASTER_LOG_POS=xxx;
查看主从服务器的状态
mysql> show slave status\G;
Master_Host: 10.200.10.10 -- master服务器IP
Master_User: myreplic -- 主从同步用户
Master_Port: 3306 -- 主库端口
Connect_Retry: 60 -- 建立连接尝试次数
Master_Log_File: mysql-bin.000010 -- master的binlog日志,在master上执行show master status查看
Read_Master_Log_Pos: 33101249 -- 中继器读到的master binlog的位置
Relay_Log_File: relay-bin.000010 -- 从库中继器的日志文件
Relay_Log_Pos: 367 -- 中继日志读到的位置
Relay_Master_Log_File: mysql-bin.000010 -- 当前中继读到的master的binlog文件
Slave_IO_Running: Yes -- I/O线程状态
Slave_SQL_Running: Yes -- SQL线程状态
Replicate_Do_DB:
Replicate_Ignore_DB: mysql,information_schema,performance_schema,sys -- 主从同步忽略的库
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: 33101249 -- SQL线程执行到的Relay_Master_Log_File文件的位置
Relay_Log_Space: 741
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: 2 -- master库的serverId,在my.cnf中配置的,主从不能设置一样
Master_UUID:
Master_Info_File: mysql.slave_master_info -- master的信息,这里根据master_info_repository配置决定
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
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
Replicate_Rewrite_DB:
Channel_Name:
Master_TLS_Version:
半同步模式
原理
半同步模式的出现是为了解决异步复制的丢数据问题。半同步模式分两种AFTER_COMMIT
和AFTER_SYNC
。
AFTER_COMMIT
模式下:
主服务器提交事务之后,并不会返回OK给客户端,而是等待从服务器发送中继日志接收完毕的ACK确认。
因事务已提交,所以其它客户端可以看到事务信息。
如果从服务器接收中继日志期间,主服务器挂了,则故障转移后,客户端在老主和新主上看到的信息就不一致了。
5.7.2版本之后的【AFTER_SYNC】大概原理:可以保证的是不丢数据,不过会多数据
就是在【主】将事务写入二进制日志之后,就会等待【从】接收二进制日志,在【从】将日志写入到中继日志后并返回【ACK】给【主】,主才会将事务提交给存储引擎执行并返回ok给客户端。
那么,当【主】挂的时候,且客户端没有收到【主】响应,则有两种情况:
- 若【从】还未接收二进制日志到中继日志,那么故障转移后,【旧主】还未提交事务 + 【新主(旧从)】中继日志里没有事务,所以客户端在【新主(旧从)】上重新发起提交事务,此时【新主】数据既没有少,也没有多。
- 若【从】已经接收二进制日志中继日志,那么故障转移后,【旧主】还未提交事务 + 【新主(旧从)】中继日志里有事务,所以客户端在【新主(旧从)】上重新发起提交事务,此时【新主(旧从)】数据会出现重复。
配置方法
- 命令方式(临时)
# 主服务器
安装插件:mysql> INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
启动模块:mysql> SET GLOBAL rpl_semi_sync_master_enabled = 1;
# 从服务器
安装插件:msyql> INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
启动模块:mysql> SET GLOBAL rpl_semi_sync_slave_enabled = 1;
重启从的IO线程: mysql> STOP SLAVE IO_THREAD; START SLAVE IO_THREAD;
-
配置方式(永久)
在Master和Slave的my.cnf中编辑
# On Master
[mysqld]
plugin-load="rpl_semi_sync_master=semisync_master.so"
rpl_semi_sync_master_enabled=1
rpl_semi_sync_master_timeout=10000 # 此单位是毫秒,主10秒内没有收到从的ACK,就会降级为异步复制
rpl_semi_sync_master_wait_for_slave_count=1 # 主在rpl_semi_sync_master_timeout内只需要收到一个从的ACK,就可以返回响应给客户端。若没达成条件,就会转为异步。
rpl_semi_sync_master_wait_no_slave=ON # ON表示仅当事务期间,Rpl_semi_sync_master_clients < rpl_semi_sync_master_wait_for_slave_count ,才会转为异步。OFF表示,哪怕空闲的时候,也会转为异步。
rpl_semi_sync_master_wait_point=AFTER_SYNC # 主等待从同步完事务后才提交事务到存储引擎
# On Slave
[mysqld]
plugin-load="rpl_semi_sync_slave=semisync_slave.so"
rpl_semi_sync_slave_enabled=1
✨rpl_semi_sync_master_wait_point 参数若设置为 AFTER_COMMIT,则表示主提交事务到存储引擎后才会同步事务到从。
配置参数
mysql> show variables like '%Rpl%';
状态参数
半同步模式开启后,通过主可以看到半同步的一些状态
msyql> show status like '%Rpl_semi%';
+--------------------------------------------+-------+
| Variable_name | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients | 1 | # 当前处于半同步状态的从服务个数
| Rpl_semi_sync_master_net_avg_wait_time | 0 |
| Rpl_semi_sync_master_net_wait_time | 0 |
| Rpl_semi_sync_master_net_waits | 0 |
| Rpl_semi_sync_master_no_times | 0 |
| Rpl_semi_sync_master_no_tx | 0 |
| Rpl_semi_sync_master_status | ON | # OFF表示出现异常转为异步模式
| Rpl_semi_sync_master_timefunc_failures | 0 |
| Rpl_semi_sync_master_tx_avg_wait_time | 0 |
| Rpl_semi_sync_master_tx_wait_time | 0 |
| Rpl_semi_sync_master_tx_waits | 0 |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0 |
| Rpl_semi_sync_master_wait_sessions | 0 |
| Rpl_semi_sync_master_yes_tx | 0 |
+--------------------------------------------+-------+
14 rows in set (0.00 sec)
同步错误
当同步出错的时候,你可以通过show slave status\G
看到错误信息,例如:
1032:记录不存在,当更新或删除一个条不存在的记录时,我们可以忽略此异常
1050:数据表已经存在
上述几个错误,根据情况,一般都可以直接通过slave-skip-errors=<错误码>
跳过错误,从而恢复同步。
1236: Got fatal error 1236 from master when reading data from binary log: 'Client requested master to startreplicationfromposition > filesize'
主库异常断电,导致buffer中的事务来不及写入二进制日志,使从库I/O线程读取到比二进制日志中end pos还大的pos。
通过mysqlbinlog查看Relay_Master_Log_File文件,找到对应Exec_Master_Log_Pos相近的pos.
这里有两种情况:
一种是有相近的pos值,然后从库通过执行select master_pos_wait方式,查看已经同步的pos值,找到未同步的,然后从库执行change master to master_log_pos=xxx;
另外一种情况就是Relay_Master_Log_File没有相近的pos值,这时候一般是本binlog已经完成同步,将master_log_file指向下一个文件,pos值改成下个文件的最小值即可。
1236: Got fatal error 1236 from master when reading data from binary log
需要重建slave。常见于主库二进制日志被提前删除。加大expire_logs_days
的时间。