基于docker搭建的mysql主从数据库模拟模拟宕机后数据恢复
生产环境中不可避免会出现MySQL服务宕机的情况,常见的场景如下:
事先启动搭建好的mysql主从服务
INSERT INTO `rbac`.`think_user`(`u_id`, `u_name`, `u_password`, `register_time`, `is_delete`, `status`, `update_time`, `last_login_time`, `role_id`, `u_phone`, `u_email`) VALUES (26, 'aaaaaa', '123456', '2019-05-31 18:03:58', 1, 1, 1561384746, '2019-05-31 18:04:10', '1', NULL, NULL);
INSERT INTO `rbac`.`think_user`(`u_id`, `u_name`, `u_password`, `register_time`, `is_delete`, `status`, `update_time`, `last_login_time`, `role_id`, `u_phone`, `u_email`) VALUES (27, 'aaaaaa', '123456', '2019-05-31 18:03:58', 0, 1, 1561384674, '2019-05-31 18:04:10', '1', NULL, NULL);
INSERT INTO `rbac`.`think_user`(`u_id`, `u_name`, `u_password`, `register_time`, `is_delete`, `status`, `update_time`, `last_login_time`, `role_id`, `u_phone`, `u_email`) VALUES (28, 'aaaaaa', '123456', '2019-05-31 18:03:58', 0, 0, 1561384674, '2019-05-31 18:04:10', '1', NULL, NULL);
docker stop my-app-mysql8-slave1
# 开启slave container容器
docker start my-app-mysql8-slave1
# 进入容器并启动mysql服务
docker exec -it my-app-mysql8-slave1 /bin/bash
/usr/local/mysql/bin/mysqld_safe --user=mysql &
# 使用mysql客户端工具连接mysql-server
/usr/local/mysql/bin/mysql -uroot -p
# 查看slave状态
show slave status\G;
show slave 报错信息如下:
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 172.18.0.4
Master_User: slave1
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000015
Read_Master_Log_Pos: 156
Relay_Log_File: 29677a6fee42-relay-bin.000023
Relay_Log_Pos: 324
Relay_Master_Log_File: mysql-bin.000013
Slave_IO_Running: Yes
Slave_SQL_Running: No
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 1062
Last_Error: Error 'Duplicate entry '26' for key 'think_user.PRIMARY'' on query. Default database: 'rbac'. Query: 'INSERT INTO `rbac`.`think_user`(`u_id`, `u_name`, `u_password`, `register_time`, `is_delete`, `status`, `update_time`, `last_login_time`, `role_id`, `u_phone`, `u_email`) VALUES (NULL, 'ccc', '123456', '2019-05-31 18:03:58', 0, 0, 1561384674, '2019-05-31 18:04:10', '1', NULL, NULL)'
Skip_Counter: 0
Exec_Master_Log_Pos: 156
Relay_Log_Space: 2385
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: NULL
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 1062
Last_SQL_Error: Error 'Duplicate entry '26' for key 'think_user.PRIMARY'' on query. Default database: 'rbac'. Query: 'INSERT INTO `rbac`.`think_user`(`u_id`, `u_name`, `u_password`, `register_time`, `is_delete`, `status`, `update_time`, `last_login_time`, `role_id`, `u_phone`, `u_email`) VALUES (NULL, 'ccc', '123456', '2019-05-31 18:03:58', 0, 0, 1561384674, '2019-05-31 18:04:10', '1', NULL, NULL)'
Replicate_Ignore_Server_Ids:
Master_Server_Id: 1
Master_UUID: 7f06a326-b845-11ea-85b9-0242ac110002
Master_Info_File: mysql.slave_master_info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State:
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp: 200629 02:22:37
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set:
Auto_Position: 0
Replicate_Rewrite_DB:
Channel_Name:
Master_TLS_Version:
Master_public_key_path:
Get_master_public_key: 0
Network_Namespace:
1 row in set (0.00 sec)
ERROR:
No query specified
此时查看slave数据库think_user表中主键数据为26,27,28的数据已经存在,说名在slave宕机之前数据已经同步到了表中。但是为什么宕机重新启动后有重新插入相同的数据呢? 这里就要说一下mysql主从的过程中做了哪些操作了,关于mysql中复制原理可参考我之前的一篇文章:地址。在slave 的进程将master中的数据写入relay log再写入表中,涉及到relay log的更新和relay log信息的更新。影响salve 中继日志信息同步的选项与master-bin.log同步信息选项如下:
默认值为 10000。设置此变量将立即对所有复制通道(包括正在运行的通道)生效。
此变量对副本的影响取决于服务器的 relay_log_info_repository 设置(FILE【relay-log.info】或 TABLE【`mysql`.`slave_relay_log_info` 】)。如果设置为 TABLE,则变量的效果还取决于副本的应用程序元数据存储库表使用的存储引擎是事务性的(例如 InnoDB)还是不是事务性的(MyISAM)。对于sync_relay_log_info零和大于零的值,这些因素对服务器行为的影响 如下:
sync_relay_log_info = 0
如果 relay_log_info_repository 设置为FILE,则MySQL服务器不执行relay-log.info文件与磁盘的同步 ;而是,服务器依赖操作系统来定期刷新其内容,就像处理其他任何文件一样。
如果 relay_log_info_repository 将设置为TABLE,并且该表的存储引擎是事务性的,则在每次事务处理后都会更新该表。(sync_relay_log_info在这种情况下,该 设置将被有效忽略。)
如果 relay_log_info_repository 设置为TABLE,并且该表的存储引擎不是事务性的,则该表永远不会更新。
sync_relay_log_info = N > 0
如果 relay_log_info_repository 将设置为FILE,则副本在每次交易后将其relay-log.info文件同步 到磁盘(使用fdatasync()) N。
如果 relay_log_info_repository 将设置为TABLE,并且该表的存储引擎是事务性的,则在每次事务处理后都会更新该表。(sync_relay_log_info在这种情况下,该设置将被有效忽略。)
如果 relay_log_info_repository 将设置为TABLE,并且该表的存储引擎不是事务性的,那么在每个N事件之后都会更新该表 。
如果此变量的值大于0,则fdatasync()在将每个sync_relay_log事件写入中继日志后,MySQL服务器都会 使用将该服务器的中继日志同步到磁盘 。设置此变量将立即对所有复制通道(包括正在运行的通道)生效。
设置sync_relay_log为0将导致不对磁盘进行任何同步;在这种情况下,服务器依赖操作系统来不时刷新中继日志的内容,就像处理其他任何文件一样。
值1是最安全的选择,因为在发生崩溃的情况下,您从中继日志中最多丢失一个事件。但是,它也是最慢的选择(除非磁盘具有电池备份的高速缓存,这使得同步非常快)。
此变量对slave的影响取决于slave的 master_info_repository设置为FILE或TABLE,具体如下:
master_info_repository = FILE。
master_info_repository = TABLE。
综上所述:
通过合理的设置sync_relay_log_info、sync_relay_log、sync_master_info 可最大程度保证slave服务器的数据完整性,但同时slave同步性能也会有所下降。
以下为我的slave配置
# 设置relay-log
relay_log_info_repository=TABLE
sync_relay_log_info=1
sync_relay_log=1
# 设置 master_info 同步
master_info_repository=TABLE
sync_master_info=1
通过循环执行以下语句,直到slave可以正常工作
mysql> STOP SLAVE;
mysql> SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1; # 跳过冲突的步数
mysql> START SLAVE;
mysql> SHOW SLAVE STATUS;