026:复制

Posted 米豆小书童

tags:

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

026:复制


一. 复制

  • 常见数据库复制模式对比

线上必须设置为 binlog_format = row ,如果希望通过 binlog 实现 flashback 的功能(网易的 mysqlbinlog -B ),则必须设置 binlog_row_image=FULL (默认),保证所有的列都出现在binlog中。(FULL对性能影响不大,仅仅对空间占用较多)

1.1 基于binlog刷新和恢复

1.1.1 binlog格式

  • Table Map : 记录了一些元数据,比如列的类型等等
    • 如果没有这个记录,就不知道第一列(@1)是哪个列,是什么类型等等信息
  • Rotate :binlog日志分割
  • Query:查询
  • Update/Write/Delete Rows:对行的操作

命令 flush binary logs; 可以强制刷新binlog到磁盘,并且产生一个新的日志( 重启MySQL 也会产生新的日志),
参数 max_binlog_size 可以设置一个binlog日志的最大的 大小

[root@node1 mysqldata]# mysqlbinlog binlog.000013 -vv
---------------省略部分-------------------------------------------------------------
create database mytest
/*!*/;
# at 359
#180214 15:05:44 server id 8888  end_log_pos 424 CRC32 0xc8484ebb 	GTID	last_committed=1	sequence_number=2
SET @@SESSION.GTID_NEXT= \'9dc847d8-bf72-11e7-9ec4-000c2998e4f1:31\'/*!*/;
# at 424
#180214 15:05:44 server id 8888  end_log_pos 530 CRC32 0xf7f59f56 	Query	thread_id=5	exec_time=1	error_code=0
use `mytest`/*!*/;
SET TIMESTAMP=1518591944/*!*/;
create table t1(a int,b int)
/*!*/;
# at 530
#180214 15:06:25 server id 8888  end_log_pos 595 CRC32 0xc2698315 	GTID	last_committed=2	sequence_number=3
SET @@SESSION.GTID_NEXT= \'9dc847d8-bf72-11e7-9ec4-000c2998e4f1:32\'/*!*/;
# at 595
#180214 15:06:25 server id 8888  end_log_pos 669 CRC32 0x9d13c3d8 	Query	thread_id=5	exec_time=1	error_code=0
SET TIMESTAMP=1518591985/*!*/;
BEGIN
/*!*/;
# at 669
#180214 15:06:25 server id 8888  end_log_pos 717 CRC32 0x05aa5af7 	Table_map: `mytest`.`t1` mapped to number 225
# at 717
-- at后面的数字表示的是文件的 偏移量,也就是常用的 start-position
#180214 15:06:25 server id 8888  end_log_pos 761 CRC32 0x6b56aca9 	Write_rows: table id 225 flags: STMT_END_F

-- 180214 15:06:25 表示该event开始的时间,YYMMDD HH:MM:SS(如果是备机,就是传递到备机上的时间)
-- server id 表示 MySQL服务器的ID
-- end_log_pos 表示下一个event的position
-- Query 表示事件的类型
-- thread_id 表示执行的线程ID
-- exec_time 表示执行的时间
-- error_code 表示执行的code,0表示没有错误

BINLOG \'
8d+DWhO4IgAAMAAAAM0CAAAAAOEAAAAAAAEABm15dGVzdAACdDEAAgMDAAP3WqoF
8d+DWh64IgAALAAAAPkCAAAAAOEAAAAAAAEAAgAC//wBAAAACgAAAKmsVms=
\'/*!*/;
### INSERT INTO `mytest`.`t1`
### SET
###   @1=1 /* INT meta=0 nullable=1 is_null=0 */
###   @2=10 /* INT meta=0 nullable=1 is_null=0 */
# at 761
#180214 15:06:25 server id 8888  end_log_pos 792 CRC32 0x658b9951 	Xid = 27
COMMIT/*!*/;
# at 792
#180214 15:06:29 server id 8888  end_log_pos 857 CRC32 0xcbef0b52 	GTID	last_committed=3	sequence_number=4
SET @@SESSION.GTID_NEXT= \'9dc847d8-bf72-11e7-9ec4-000c2998e4f1:33\'/*!*/;
# at 857
#180214 15:06:29 server id 8888  end_log_pos 931 CRC32 0x33da22fc 	Query	thread_id=5	exec_time=0	error_code=0
SET TIMESTAMP=1518591989/*!*/;
BEGIN
/*!*/;
# at 931
#180214 15:06:29 server id 8888  end_log_pos 979 CRC32 0x2e7be109 	Table_map: `mytest`.`t1` mapped to number 225
# at 979
#180214 15:06:29 server id 8888  end_log_pos 1023 CRC32 0xdeda3369 	Write_rows: table id 225 flags: STMT_END_F

BINLOG \'
9d+DWhO4IgAAMAAAANMDAAAAAOEAAAAAAAEABm15dGVzdAACdDEAAgMDAAMJ4Xsu
9d+DWh64IgAALAAAAP8DAAAAAOEAAAAAAAEAAgAC//wCAAAAFAAAAGkz2t4=
\'/*!*/;
### INSERT INTO `mytest`.`t1`
### SET
###   @1=2 /* INT meta=0 nullable=1 is_null=0 */
###   @2=20 /* INT meta=0 nullable=1 is_null=0 */
# at 1023
#180214 15:06:29 server id 8888  end_log_pos 1054 CRC32 0x8bbeb6e3 	Xid = 28
COMMIT/*!*/;
SET @@SESSION.GTID_NEXT= \'AUTOMATIC\' /* added by mysqlbinlog */ /*!*/;
DELIMITER ;
# End of log file
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;
[root@node1 mysqldata]#

1.1.2 binlog恢复

  • 注意,如果你有 多个binlog文件想要恢复, 不要一个一个顺序恢复
shell> mysqlbinlog binlog.000001 | mysql -u root -p ## DANGER!!

shell> mysqlbinlog binlog.000002 | mysql -u root -p ## DANGER!!

上面这种恢复方式是错误的,如果 binlog.000001创建了一个临时表(CREATE TEMPORARY TABLE),而 binlog.000002 中要使用这个临时表,但是 第一个线程(binlog.000001) 在 释放 的时候会 删除临时表 ,此时 第二个线程(binlog.000002) 就无法使用这个临时表

正确的做法如下:

shell> mysqlbinlog binlog.000001 binlog.000002 | mysql -u root -p

---------------OR----------------

shell> mysqlbinlog binlog.000001 > /tmp/statements.sql shell> mysqlbinlog binlog.000002 >> /tmp/statements.sql

---------------OR----------------

shell> mysqlbinlog binlog.00000[1-2] > /tmp/statements.sql shell> mysql -u root -p -e "source /tmp/statements.sql"

注意:mysqlbinlog的参数 start/stop-position 不能是中间位置 ,必须是在 binlog 文件中 at 后面跟着的一个数字(必须是一个边界值)。 参数 start/stop-datatime 可以通过时间戳来进行恢复

  • 基于position
shell> mysqlbinlog bin.000017 --start-position=1959 --stop-position=2057 -vv > /tmp/a.sql
  • 基于datetime
shell> mysqlbinlog bin.000017 --start-datetime="2016-03-02 21:03:58" --stop-datetime="2016-03-02 23:14:06" -vv > /tmp/a.sql 
shell> mysql -u root -p < a.sql

start和stop的范围是 [start, stop)

-- 在mysql中查看events信息 (from pos limit N,[M])
(root@localhost) 15:06:35 [mytest]>  show binlog events;
+---------------+-----+----------------+-----------+-------------+--------------------------------------------------------------------+
| Log_name      | Pos | Event_type     | Server_id | End_log_pos | Info                                                               |
+---------------+-----+----------------+-----------+-------------+--------------------------------------------------------------------+
| binlog.000011 |   4 | Format_desc    |      8888 |         123 | Server ver: 5.7.18-log, Binlog ver: 4                              |
| binlog.000011 | 123 | Previous_gtids |      8888 |         194 | 9dc847d8-bf72-11e7-9ec4-000c2998e4f1:1-28                          |
| binlog.000011 | 194 | Gtid           |      8888 |         259 | SET @@SESSION.GTID_NEXT= \'9dc847d8-bf72-11e7-9ec4-000c2998e4f1:29\' |
| binlog.000011 | 259 | Query          |      8888 |         384 | use `employees`; DROP TABLE `t1` /* generated by server */         |
| binlog.000011 | 384 | Stop           |      8888 |         407 |                                                                    |
+---------------+-----+----------------+-----------+-------------+--------------------------------------------------------------------+
5 rows in set (0.00 sec)

1.2 MySQL主从复制架构

  • 主服务器

    • MySQL 5.7中, prepare log 部分的日志也是 组提交

    • prepare logcommit logredo file(iblogfile1、iblogfile2)

    • binlogbinlig.00000X 文件

    • MySQL Dump Thread 把 binlog 推送到远程的Slave服务器

      • 每一个Slave,就会对应有一个dump线程
    • 同时,在MySQL主机上还有一个 Master Thread每隔1秒redo log buffer中写入redo file

  • 从服务器

    • IO Thread 负责 接收 Dump线程发送过来的 binlog ,并且记录到本地的 relay log

    • 接受的 单位event

    • SQL Thread/Coordinator Thread 负责将relay log中的日志 回放 到从机

      • 回放的 单位 也是 event
    • 有了多线程以后,coordinator线程负责任务指派work thread负责回放

    • 在 MySQL5.6 中的多线程回放是 基于库 的, 单个库还是单线程

    • 在 MySQL5.7 中的多线程是在 主上如何并行执行的从机上也是如何并行回放的

    • master-info.log 存放了 接收 到的binlog的 位置 ( event的位置 )

    • relay-info.log 存放了 回放 到的relay log的 位置 ( event的位置 )


二. 可传输表空间

简单的说,就是将一个表空间文件(ibd文件),拷贝到远程另外一台数据库进行恢复,进行物理复制。

2.1. innodb 独立表空间导入和导出

  • 操作步骤:

    • 目的服务器:ALTER TABLE t DISCARD TABLESPACE;
    • 源服务器:FLUSH TABLES t FOR EXPORT;
    • 源服务器:拷贝t.ibd,t.cfg文件到目的服务器
    • 源服务器:UNLOCK TABLES;
    • 目的服务器:ALTER TABLE t IMPORT TABLESPACE;

2.2. 演示

hostname 逻辑库
node1.gczheng.com mytest t1
node2.gczheng.com mytest

将node1中mytest库下面的t1表 ,传输node2中mytest库中

  • 1、源服务器查看迁移表状态

  • 源服务器

(root@localhost) 10:44:22 [mysql]> create database tablespace;
Query OK, 1 row affected (0.01 sec)

(root@localhost) 10:44:31 [mysql]> use tablespace;
Database changed

(root@localhost) 10:45:09 [tablespace]> create table qqq(a int);
Query OK, 0 rows affected (0.02 sec)

(root@localhost) 10:45:37 [tablespace]> insert into qqq values(1);
Query OK, 1 row affected (0.01 sec)

(root@localhost) 10:45:50 [tablespace]> select * from qqq;
+------+
| a    |
+------+
|    1 |
+------+
1 row in set (0.00 sec)
  • 2、在目标服务器上创建表空间

  • 目标服务器

(root@localhost) 10:46:59 [(none)]> create database tablespace;
Query OK, 1 row affected (0.00 sec)

(root@localhost) 10:47:18 [(none)]> use tablespace;
Database changed
(root@localhost) 10:47:24 [tablespace]> create table qqq(a int);
Query OK, 0 rows affected (0.01 sec)

创建完成后进行检查

[root@node2 tablespace]# ll
total 112
-rw-r----- 1 mysql mysql    61 Feb 15 10:47 db.opt
-rw-r----- 1 mysql mysql  8554 Feb 15 10:48 qqq.frm   --表结构
-rw-r----- 1 mysql mysql 98304 Feb 15 10:48 qqq.ibd   --表空间,需要通过 DISCARD 将表空间文件删除

ALTER TABLE qqq DISCARD TABLESPACE; 的含义是 保留qqq.frm文件删除qqq.ibd

通过discard 删除ibd文件

(root@localhost) 10:48:07 [tablespace]> ALTER TABLE qqq DISCARD TABLESPACE;
Query OK, 0 rows affected (0.01 sec)
[root@node2 tablespace]# ll
total 16
-rw-r----- 1 mysql mysql   61 Feb 15 10:47 db.opt
-rw-r----- 1 mysql mysql 8554 Feb 15 10:48 qqq.frm

--已经删除表空间qqq.ibd
  • 3、源服务器导出表空间

在源服务器上,通过export 命令导出表空间(同时加读锁)

  • 源服务器
(root@localhost) 10:45:56 [tablespace]> FLUSH TABLES qqq FOR EXPORT;  --其实是对这个表加一个读锁
Query OK, 0 rows affected (0.01 sec)

将导出的cfg文件ibd文件,拷贝到目标服务器的mytest库下

[root@node1 mytest]# ll
total 24
-rw-r----- 1 mysql mysql   61 Feb 14 15:05 db.opt
-rw-r----- 1 mysql mysql  416 Feb 14 18:12 t1.cfg    --export后,多出来的文件,里面保存了一些元数据信息	
-rw-r----- 1 mysql mysql 8578 Feb 14 16:35 t1.frm
-rw-r----- 1 mysql mysql  416 Feb 14 17:09 t1.ibd

[root@node1 mysqldata]# scp tablespace/qqq.cfg tablespace/qqq.ibd  node2:/r2/mysqldata/tablespace/
qqq.cfg                                                                                                                                   100%  373   360.4KB/s   00:00
qqq.ibd                                                                                                                                   100%   96KB  14.0MB/s   00:00

导出表空间后,尽快解锁

(root@localhost) 10:49:00 [tablespace]> unlock tables;
Query OK, 0 rows affected (0.01 sec)

注意:一定要先拷贝cfg和ibd文件,然后才能unlock,因为 unlock** ****的时候,**cfg文件会被删除

源服务器上的日志

2018-02-14T02:38:16.530256Z 4 [Note] Start binlog_dump to master_thread_id(4) slave_server(8899), pos(, 4)
2018-02-14T09:05:44.342882Z 5 [Note] InnoDB: Sync to disk of `mytest`.`t1` started.
2018-02-14T09:05:44.343641Z 5 [Note] InnoDB: Stopping purge                                     --其实stop purge,找个测试的表 for export 即可
2018-02-14T09:05:44.344836Z 5 [Note] InnoDB: Writing table metadata to \'./mytest/t1.cfg\'
2018-02-14T09:05:44.345158Z 5 [Note] InnoDB: Table `mytest`.`t1` flushed to disk
2018-02-14T09:13:23.812898Z 5 [Note] InnoDB: Deleting the meta-data file \'./mytest/t1.cfg\'      --unlock table后,该文件自动被删除
2018-02-14T09:13:23.812950Z 5 [Note] InnoDB: Resuming purge                                     --unlock后,恢复purge线程
  • 4、在目标服务器上修改 cfg文件和ibd文件的 权限

  • 目标服务器

[root@node2 tablespace]# ll
total 116
-rw-r----- 1 mysql mysql    61 Feb 15 10:47 db.opt
-rw-r----- 1 root  root    373 Feb 15 10:49 qqq.cfg
-rw-r----- 1 mysql mysql  8554 Feb 15 10:48 qqq.frm
-rw-r----- 1 root  root  98304 Feb 15 10:49 qqq.ibd

[root@node2 tablespace]# chown -R mysql.mysql ./*

[root@node2 tablespace]# ll
total 116
-rw-r----- 1 mysql mysql    61 Feb 15 10:47 db.opt
-rw-r----- 1 mysql mysql   373 Feb 15 10:49 qqq.cfg
-rw-r----- 1 mysql mysql  8554 Feb 15 10:48 qqq.frm
-rw-r----- 1 mysql mysql 98304 Feb 15 10:49 qqq.ibd

在目标服务器上通过import 命令导入表空间

(root@localhost) 10:48:28 [tablespace]> ALTER TABLE qqq IMPORT TABLESPACE;  --导入表空间
Query OK, 0 rows affected (0.02 sec)

(root@localhost) 10:50:49 [tablespace]> select * from qqq;                  -- 可以读取到从源服务器拷贝过来的数据
+------+
| a    |
+------+
|    1 |
+------+
1 row in set (0.00 sec)

error.log中出现的信息

2018-02-15T02:50:49.725304Z 3 [Note] InnoDB: Importing tablespace for table \'tablespace/qqq\' that was exported from host \'node1.gczheng.com\'
2018-02-15T02:50:49.725433Z 3 [Note] InnoDB: Phase I - Update all pages
2018-02-15T02:50:49.725705Z 3 [Note] InnoDB: Sync to disk
2018-02-15T02:50:49.728143Z 3 [Note] InnoDB: Sync to disk - done!
2018-02-15T02:50:49.729426Z 3 [Note] InnoDB: Phase III - Flush changes to disk
2018-02-15T02:50:49.739010Z 3 [Note] InnoDB: Phase IV - Flush complete
2018-02-15T02:50:49.739496Z 3 [Note] InnoDB: `tablespace`.`qqq` autoinc value set to 0

注意:

表的名称必须相同,经过上述测试,库名可以不同该方法也可以用于分区表的备份和恢复


三 复制环境搭建

配置信息 主库(master) 从库(slave)
主机 node1.gczheng.com node2.gczheng.com
IP 192.168.88.88 192.168.88.99
Port 3306 3306
MySQL版本 MySQL5.7.18 MySQL5.7.18
Server_ID 8888 8899

注意:server-id在主从的配置中必须不同(在一个复制关系中,server-id必须唯一)

3.1. 创建一个复制用户

在Master节点上创建一个用于复制的用户,供Slave节点使用

  • master服务器
mysql root@localhost:(none)> create user \'repl\'@\'192.168.88.99\' identified by \'123456\';
Query OK, 0 rows affected
Time: 0.005s
mysql root@localhost:(none)> grant replication slave on *.* to \'repl\'@\'192.168.88.99\';  --需要replication和slave的权限,线上建议`限制成内网的网段`
Query OK, 0 rows affected
Time: 0.001s
mysql root@localhost:(none)> flush privileges;
Query OK, 0 rows affected
Time: 0.006s
mysql root@localhost:(none)> select User,Host from mysql.user where user=\'repl\';
+------+---------------+
| User | Host          |
+------+---------------+
| repl | 192.168.88.99 |
+------+---------------+
1 row in set
Time: 0.008s
mysql root@localhost:(none)> show grants for \'repl\'@\'192.168.88.99\';
+----------------------------------------------------------+
| Grants for repl@192.168.88.99                            |
+----------------------------------------------------------+
| GRANT REPLICATION SLAVE ON *.* TO \'repl\'@\'192.168.88.99\' |
+----------------------------------------------------------+
1 row in set
Time: 0.007s
mysql root@localhost:(none)>

测试slave节点是否可以通过 rpl@\'%\'** **连接成功

  • slave服务器
[root@node2 tablespace]# mysql -h192.168.88.88 -urepl -p123456
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \\g.
Your MySQL connection id is 17
Server version: 5.7.18-log MySQL Community Server (GPL)

Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type \'help;\' or \'\\h\' for help. Type \'\\c\' to clear the current input statement.

(repl@192.168.88.88) 11:41:06 [(none)]> select current_user;
+--------------------+
| current_user       |
+--------------------+
| repl@192.168.88.99 |
+--------------------+
1 row in set (0.00 sec)

(repl@192.168.88.88) 11:41:09 [(none)]>

说明此时Slave节点可以连接到Master节点了

3.2. 备份数据

3.2.1. 准备测试数据

  • master服务器
mysql root@localhost:(none)> show databases
+--------------------+
| Database           |
+--------------------+
| information_schema |
| employees          |
| mysql              |
| performance_schema |
| sys                |
| tablespace         |
+--------------------+
6 rows in set
Time: 0.007s
mysql root@localhost:(none)>

3.2.2. 导出数据

[root@node1 bakdata]# mydumper  -u root -p root --regex "employees.*|tablespace.*" -o /bakdata/alldb

[root@node1 bakdata]# cd alldb/
[root@node1 alldb]# ls
employees.departments-schema.sql  employees.dept_manager-schema.sql  employees.salaries-schema.sql  employees.titles.sql       tablespace-schema-create.sql
employees.departments.sql         employees.dept_manager.sql         employees.salaries.sql         metadata
employees.dept_emp-schema.sql     employees.employees-schema.sql     employees-schema-create.sql    tablespace.qqq-schema.sql
employees.dept_emp.sql            employees.employees.sql            employees.titles-schema.sql    tablespace.qqq.sql

将备份目录复制到 Slave 节点

[root@node1 alldb]# scp -r /bakdata/alldb  192.168.88.99:/bakdata/
employees-schema-create.sql                                                                                                               100%   68    22.2KB/s   00:00
tablespace-schema-create.sql                                                                                                              100%   69    49.8KB/s   00:00
employees.departments.sql                                                                                                                 100%  351   367.0KB/s   00:00
employees.dept_emp.sql                                                                                                                    100%   14MB  38.7MB/s   00:00
employees.dept_manager.sql                                                                                                                100% 1168   708.4KB/s   00:00
employees.employees.sql                                                                                                                   100%   17MB  48.4MB/s   00:00
employees.salaries.sql                                                                                                                    100%  113MB  56.6MB/s   00:02
employees.titles.sql                                                                                                                      100%   21MB  47.7MB/s   00:00
tablespace.qqq.sql                                                                                                                        100%  132    86.7KB/s   00:00
employees.departments-schema.sql                                                                                                          100%  266   225.5KB/s   00:00
employees.dept_emp-schema.sql                                                                                                             100%  555   127.8KB/s   00:00
employees.dept_manager-schema.sql                                                                                                         100%  567   264.6KB/s   00:00
employees.employees-schema.sql                                                                                                            100%  353   216.5KB/s   00:00
employees.salaries-schema.sql                                                                                                             100%  416   331.3KB/s   00:00
employees.titles-schema.sql                                                                                                               100%  427   468.3KB/s   00:00
tablespace.qqq-schema.sql                                                                                                                 100%  153    54.0KB/s   00:00
metadata                                                                                                                                  100%  175    87.7KB/s   00:00
[root@node1 alldb]#

3.3. 还原数据

[root@node2 bakdata]# myloader -u root -p root -o -d /bakdata/alldb -v -3
** Message: 4 threads created
** Message: Dropping table or view (if exists) `employees`.`departments`
** Message: Creating table `employees`.`departments`
** Message: Dropping table or view (if exists) `employees`.`dept_emp`
** Message: Creating table `employees`.`dept_emp`
** Message: Dropping table or view (if exists) `employees`.`dept_manager`
** Message: Creating table `employees`.`dept_manager`
** Message: Dropping table or view (if exists) `employees`.`employees`
** Message: Creating table `employees`.`employees`
** Message: Dropping table or view (if exists) `employees`.`salaries`
** Message: Creating table `employees`.`salaries`
** Message: Dropping table or view (if exists) `employees`.`titles`
** Message: Creating table `employees`.`titles`
** Message: Dropping table or view (if exists) `tablespace`.`qqq`
** Message: Creating table `tablespace`.`qqq`
** Message: Thread 1 restoring `employees`.`departments` part 0
** Message: Thread 2 restoring `employees`.`dept_emp` part 0
** Message: Thread 3 restoring `employees`.`dept_manager` part 0
** Message: Thread 4 restoring `employees`.`employees` part 0
** Message: Thread 1 restoring `employees`.`salaries` part 0
** Message: Thread 3 restoring `employees`.`titles` part 0
** Message: Thread 4 restoring `tablespace`.`qqq` part 0
** Message: Thread 4 shutting down
** Message: Thread 2 shutting down
** Message: Thread 3 shutting down
** Message: Thread 1 shutting down
[root@node2 bakdata]#

检查数据是否还原到备库

mysql root@localhost:(none)> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| employees          |
| mysql              |
| performance_schema |
| sys                |
| tablespace         |
+--------------------+
6 rows in set
Time: 0.063s

mysql root@localhost:(none)> use employees;
You are now connected to database "employees" as user "root"
Time: 0.045s

mysql root@localhost:employees> show tables;
+---------------------+
| Tables_in_employees |
+---------------------+
| departments         |
| dept_emp            |
| dept_manager        |
| employees           |
| salaries            |
| titles              |
+---------------------+
6 rows in set
Time: 0.009s

mysql root@localhost:employees> select * from titles  limit 1;
+--------+-----------------+------------+------------+
| emp_no | title           | from_date  | to_date    |
+--------+-----------------+------------+------------+
| 10001  | Senior Engineer | 1986-06-26 | 9999-01-01 |
+--------+-----------------+------------+------------+
1 row in set
Time: 0.013s
mysql root@localhost:employees>

-- 已经还原到 Slave 节点上了

可以使用mysqldump加master-data参数,将master信息保存在备份中目前而言,主从数据已经是一致的了

3.4. CHANGE MASTER

由于使用 mydumper备份,没有将 Change Master信息写入SQL,而是写入到metadata中。

3.4.1. 查看master status

[root@node2 bakdata]# cat alldb/metadata
Started dump at: 2018-02-15 11:55:16
SHOW MASTER STATUS:
	Log: binlog.000013
	Pos: 3581
	GTID:9dc847d8-bf72-11e7-9ec4-000c2998e4f1:1-46

Finished dump at: 2018-02-15 11:55:20
[root@node2 bakdata]#

Log: binlog.000013 和 Pos: 3581 表明该备份开始时的 filename 和 postition

3.4.2. change master

  • slave服务器
mysql root@localhost:employees> change master to master_host=\'192.168.88.88\', master_user=\'repl\', master_password=\'123456\', master_port=3306, master_log_file=\'binlog.000013\', master_log_pos=3581;
Query OK, 0 rows affected
Time: 0.068s

mysql root@localhost:employees> show slave status \\G;
***************************[ 1. row ]***************************
Slave_IO_State                |
Master_Host                   | 192.168.88.88
Master_User                   | repl
Master_Port                   | 3306
Connect_Retry                 | 60
Master_Log_File               | binlog.000013            --change master中的filename	
Read_Master_Log_Pos           | 3581                     --metadata中指定的pos
Relay_Log_File                | node2-relay-bin.000001
Relay_Log_Pos                 | 4
Relay_Master_Log_File         | binlog.000013
Slave_IO_Running              | No                      --未启动slave同步,显示No
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                    | 0
Last_Error                    |
Skip_Counter                  | 0
Exec_Master_Log_Pos           | 3581
Relay_Log_Space               | 194
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                 |
1 row in set
Time: 0.027s

mysql root@localhost:employees> start slave;            --开启slave
Query OK, 0 rows affected
Time: 0.053s

mysql root@localhost:employees> show slave status \\G;
***************************[ 1. row ]***************************
Slave_IO_State                | Waiting for master to send event    --IO 线程的状态
Master_Host                   | 192.168.88.88
Master_User                   | repl
Master_Port                   | 3306
Connect_Retry                 | 60
Master_Log_File               | binlog.000013                       --IO线程读取到的文件
Read_Master_Log_Pos           | 3581                                --IO线程读取文件中的位置
Relay_Log_File                | node2-relay-bin.000002    
Relay_Log_Pos                 | 317
Relay_Master_Log_File         | binlog.000013                       --SQL线程执行到的文件	
Slave_IO_Running              | Yes                                 --IO线程启动成功
Slave_SQL_Running             | Yes                                 --SQL线程启动成功	
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           | 3581                                --SQL线程执行到文件的位置
Relay_Log_Space               | 564
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                                   --Slave落后Master执行的秒数,这个值不准确
Master_SSL_Verify_Server_Cert | No
Last_IO_Errno                 | 0                                   --(IO)如果这里有信息的话,就是错误提示信息,可以用来排错		
Last_IO_Error                 |                                     --(SQL)如果这里有信息的话,就是错误提示信息,可以用来排错
1 row in set
Time: 0.020s
mysql root@localhost:employees>

Slave_IO_RunningSlave_SQL_Running 这两个指标都为YES,表示目前的复制的状态是正常的

mysql root@localhost:employees> show processlist\\G;
***************************[ 1. row ]***************************
Id      | 18
User    | root
Host    | localhost
db      | employees
Command | Query
Time    | 0
State   | starting
Info    | show processlist
***************************[ 2. row ]***************************
Id      | 20
User    | system user
Host    |
db      | <null>
Command | Connect
Time    | 1269
State   | Waiting for master to send event                      -- IO线程
Info    | <null>
***************************[ 3. row ]***************************
Id      | 21
User    | system user
Host    |
db      | <null>
Command | Connect
Time    | 1269
State   | Slave has read all relay log; waiting for more updates  -- SQL线程
Info    | <null>
3 rows in set
Time: 0.010s
mysql root@localhost:employees>

3.4.3. 添加并行复制

  • slave服务器

在/etc/my.cnf文件中配置

slave-parallel-type=LOGICAL_CLOCK 
slave-parallel-workers=4

如果开启了并行复制(multi-threaded slave), show processlist 中可以看到 Coordinator 线程

mysql root@localhost:employees> show processlist;
+----+-------------+-----------+-----------+---------+------+--------------------------------------------------------+------------------+
| Id | User        | Host      | db        | Command | Time | State                                                  | Info             |
+----+-------------+-----------+-----------+---------+------+--------------------------------------------------------+------------------+
| 1  | system user |           | <null>    | Connect | 17   | Waiting for master to send event                       | <null>           |
| 2  | system user |           | <null>    | Connect | 17   | Slave has read all relay log; waiting for more updates | <null>           |
| 3  | system user |           | <null>    | Connect | 17   | Waiting for an event from Coordinator                  | <null>           |
| 4  | system user |           | <null>    | Connect | 17   | Waiting for an event from Coordinator                  | <null>           |
| 5  | system user |           | <null>    | Connect | 17   | Waiting for an event from Coordinator                  | <null>           |
| 6  | system user |           | <null>    | Connect | 17   | Waiting for an event from Coordinator                  | <null>           |
| 9  | root        | localhost | employees | Query   | 0    | starting                                               | show processlist |
+----+-------------+-----------+-----------+---------+------+--------------------------------------------------------+------------------+
7 rows in set
Time: 0.009s
mysql root@localhost:employees>
  • 主要的监控参数

Relay_Log_FileRelay_Log_Pos 是中继日志(Relay_Log)信息。

由于 IO线程 拉取数据的速度快于 SQL线程 回放数据的速度,所以 Relay_Log 可在两者之间起到一个缓冲的作用

Relay_Log 的格式和 binlog格式是一样的,但是两者的内容是不一样的(不是和binlog一一对应的

Relay_LogSQL线程回放完成后,(默认)就会被删除,而 binlog 不会(由 expire_logs_days控制

Relay_Log 可以通过设置 relay_log_purge=0 ,使得 Relay_Log 不被删除(MHA中不希望被Purge),需要通过外部的脚本进行删除

  • 验证复制

  • master节点

mysql root@localhost:(none)> insert into tablespace.qqq values(2);
Query OK, 1 row affected
Time: 0.019s
  • slave节点
mysql root@localhost:employees> select * from tablespace.qqq ;
+---+
| a |
+---+
| 1 |
| 2 |
+---+
2 rows in set
Time: 0.026s
mysql root@localhost:employees>

  • 当前演示时的relay-log文件是 binlog.000029
[root@node2 bakdata]# mysqlbinlog /r2/mysqldata/binlog.000029 -vv
------------------省略其他输出-----------------

# at 1134
#180215 12:39:57 server id 8888  end_log_pos 1174 CRC32 0xe52db744 	Write_rows: table id 233 flags: STMT_END_F

BINLOG \'
HQ+FWhO4IgAANAAAAG4EAAAAAOkAAAAAAAEACnRhYmxlc3BhY2UAA3FxcQABAwABY9f0lA==
HQ+FWh64IgAAKAAAAJYEAAAAAOkAAAAAAAEAAgAB//4CAAAARLct5Q==
\'/*!*/;
### INSERT INTO `tablespace`.`qqq`                              --- 这个注释的信息就是传过来的插入数据的信息
### SET
###   @1=2 /* INT meta=0 nullable=1 is_null=0 */
# at 1174
#180215 12:39:57 server id 8888  end_log_pos 1205 CRC32 0x8dabbbc4 	Xid = 556
COMMIT/*!*/;
# at 1205
#180215 12:43:53 server id 8899  end_log_pos 1228 CRC32 0x720c1617 	Stop
SET @@SESSION.GTID_NEXT= \'AUTOMATIC\' /* added by mysqlbinlog */ /*!*/;
DELIMITER ;
# End of log file
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;

3.5. 复制搭建总结

1.MasterSlave 上配置 不同 的 server-id ,且binlog_format 设置为 ROW 格式

2.在Master上创建一个rpl@%的用户( %替换为内网网段 )

3.将Master的备份数据恢复到Slave 上,注意记录master status信息( binlog_file 和position)

4.在Slave上进行‘change master’操作,注意 master_log_filemaster_log_pos 要和备份中的master status一致

5.在Slave上进行start slave 操作

6.在Slave上进行show slave status\\G; 操作,确保Slave_IO_RunningSlave_SQL_Running均为YES

3.6. 搭建真正的高可靠复制环境

3.6.1. 重要的参数

  • Master

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

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

    • max_binlog_size = 2048M #默认为1024M

    • binlog_format = ROW #必须为ROW

    • transaction-isolation = READ-COMMITTED

    • expire_logs_days = 7 # binlog保留多少天,看公司计划安排

    • server-id = 8888 #必须和所有从机不一样,且从机之间也不一样

    • binlog_cache_size = # binlog缓存的大小,设置时要当心

    • sync_binlog = 1 #必须设置为1,默认为0

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

    • innodb_support_xa = 1 #确保事务日志写入bin-log 的顺序与是事务的time-line是一致的

  • Slave

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

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

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

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

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

    • server-id = 8899 #必须在一个复制集群环境中全局唯一

    • relay-log-recovery = 1 #I/O thread crash safe – IO线程安全

    • relay-log-info-repository = TABLE # SQL thread crash safe – SQL线程安全

    • master_info_repository = TABLE

    • read_only = 1

3.6.2. SQL线程高可靠问题

  • 如果将relay_log_info_repository 设置为FILE ,MySQL会把回放信息记录在一个 relay-info.log 的文件中,其中包含SQL线程 回放到的Relay_log_nameRelay_log_pos ,以及对应的MasterMaster_log_nameMaster_log_pos

  • SQL线程回放event
  • 将回放到的binlog的文件名位置写到relay-info.log文件
  • 参数sync_relay_log_info = 10000 (fsync)代表每回放10000个event,写一次relay-info.log
    • 如果该参数设置为1,则表示每回放一个event就写一次relay-info.log,那写入代价很大,且性能很差
    • 设置为1后,即使性能上可以接受,还是会丢最有一次的操作,恢复起来后还是有1062的错误(重复执行event)

SQL线程的数据回放是写数据库操作relay-info写文件操作,这两个操作很难保证一致性

当一个Slave节点在复制数据时,可能发生如下情况,数据2和数据3写入成功(且已经落盘),但是relay-info.log 中的记录还是数据1的位置(因为sync_relay_log_info的关系,此时还没有fsync),如下图所示:

此时Slave宕机,然后重启,便会产生如下的状况:

  1. Slave的库中 存在数据2和数据3
  2. Slave读取relay-info.log中的 Relay_log_name和Relay_log_pos ,此时记录的是 回放到数据1的位置
  3. Slave 从数据1开始回放 ,继续 插入数据2和数据3
  4. 但是,此时的数据库中 存在数据2和数据3 ,于是发生了 1062 的错误(重复记录)
mysql root@localhost:(none)> select * from mysql.slave_relay_log_info \\G;
***************************[ 1. row ]***************************
Number_of_lines   | 7
Relay_log_name    | ./node2-relay-bin.000013    -- relay日志的文件名
Relay_log_pos     | 317                         -- relay日志的位置
Master_log_name   | binlog.000014               -- 对应回放到的 binlog 文件名(Master节点)
Master_log_pos    | 706                         -- 对应回放到的位置
Sql_delay         | 0
Number_of_workers | 4
Id                | 1
Channel_name      |
1 row in set
Time: 0.006s
mysql root@localhost:(none)>

设置为 TABLE 的原理为:将 event的回放relay-info的更新 放在同一个事物 里面,变成原子操作,从而保证一致性(要么都写入,要么都不写)。
每一次事物提交,都会写入 mysql.slave_relay_log_info,sync_relay_log_info=N 将被忽略。官方参数解释:

BEGIN;

apply log event; apply log event;

UPDATEmysql.slave_relay_log_info

SETMaster_log_pos = Exec_Master_Log_Pos,

Master_log_name = Relay_Master_Log_File,

Relay_log_name = Relay_Log_File,

Relay_log_pos = Relay_Log_Pos;

COMMIT;

3.6.3. I/O线程高可用

IO线程也是接收一个个的 event ,将接收到的event,通过设置参数 master_info_repository 可以将master-info 信息(IO线程接收到的位置,Master_log_name 和 Master_log_pos )写入到文件(FILE )或者数据库( TABLE )中。然后将接收到的event 写入relay log file

参数 sync_master_info=10000 表示每接收10000个event,写一次master-info

这里同样存在这个问题, master-info.log 和 relay-log 无法保证一致性。

假设存在下面这个情况,event2和event3已经写入到relay-log,但是master-info还没有同步到master-info.log

此时如果服务宕机后,MySQL重启,I/O线程会读取master-info.log的内容,读取到的位置为event1的位置 ,然后I/O线程会继续将event2event3拉取过来,然后继续写入到relay-log 中。

如上图所示,event2 和event3 被重复写入到了relay-log文件中,当SQL线程回放时,就会产生 1062 的错误(重复记录)

看到的现象还是 IO线程正常SQL线程报错

  • 解决问题的方法:

    • 设置参数 relay-log-recover = 1 ,该参数表示 当前接收到的relay-log全部删除 ,然后从SQL线程回放到的位置 重新拉取(SQL线程通过配置后是可靠的)
  • 所以说,真正的MySQL复制的高可靠是从 5.6 版本开始的,通过设置

    • relay-log-recover = 1
    • relay_log_info_repository = TABLE
    • master_info_repository = TABLE

这三个参数,可以确保整体复制的高可靠(换言之,之前的版本复制不可靠是正常的)。

注意:如果 Slave落后Master 的时间很多,超过了Master上binlog的保存时间,那Master上对应的binlog就会被删除,Slave的I/OThread就拉不到数据了,注意监控主从落后的时间

在已启用主从同步的实例中,设置set GLOBAL relay_log_info_repository=\'TABLE\'; 需要先stop slave,再start slave。

mysql root@localhost:(none)> stop slave;
Query OK, 0 rows affected
Time: 0.002s
mysql root@localhost:(none)> set GLOBAL  relay_log_info_repository=\'TABLE\';
Query OK, 0 rows affected
Time: 0.005s
mysql root@localhost:(none)> start slave;
Query OK, 0 rows affected
Time: 0.008s
mysql root@localhost:(none)> show variables like \'%relay%\';
+---------------------------+-------------------------------------+
| Variable_name             | Value                               |
+---------------------------+-------------------------------------+
| max_relay_log_size        | 0                                   |
| relay_log                 |                                     |
| relay_log_basename        | /r2/mysqldata/node2-relay-bin       |
| relay_log_index           | /r2/mysqldata/node2-relay-bin.index |
| relay_log_info_file       | relay-log.info                      |
| relay_log_info_repository | TABLE                               |
| relay_log_purge           | ON                                  |
| relay_log_recovery        | ON                                  |
| relay_log_space_limit     | 0                                   |
| sync_relay_log            | 10000                               |
| sync_relay_log_info       | 10000                               |
+---------------------------+-------------------------------------+
11 rows in set
Time: 0.014s
mysql root@localhost:(none)>  select * from mysql.slave_relay_log_info \\G;
***************************[ 1. row ]***************************
Number_of_lines   | 7
Relay_log_name    | ./node2-relay-bin.000013
Relay_log_pos     | 317
Master_log_name   | binlog.000014
Master_log_pos    | 706
Sql_delay         | 0
Number_of_workers | 4
Id                | 1
Channel_name      |
1 row in set
Time: 0.006s
mysql root@localhost:(none)>

3.6.4. master_info_repository设置

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

mysql root@localhost:(none)> select * from mysql.slave_master_info\\G;
***************************[ 1. row ]***************************
Number_of_lines        | 25
Master_log_name        | binlog.000014
Master_log_pos         | 706
Host                   | 192.168.88.88
User_name              | repl
User_password          | 123456
Port                   | 3306
Connect_retry          | 60
Enabled_ssl            | 0
Ssl_ca                 |
Ssl_capath             |
Ssl_cert               |
Ssl_cipher             |
Ssl_key                |
Ssl_verify_server_cert | 0
Heartbeat              | 30.0
Bind                   |
Ignored_server_ids     | 0
Uuid                   | 9dc847d8-bf72-11e7-9ec4-000c2998e4f1
Retry_count            | 86400
Ssl_crl                |
Ssl_crlpath            |
Enabled_auto_position  | 0
Channel_name           |
Tls_version            |
1 row in set
Time: 0.010s
mysql root@localhost:(none)>

3.6.5. read_only与super_read_only

如果在Slave机器上对数据库进行修改或者删除,会导致主从的不一致,需要对Slave机器设置为 read_only = 1 ,让Slave提供 只读 操作。
注意: read_only 仅仅对 没有SUPER权限 的用户 有效 (即 mysql.user表的Super_priv字段为Y),一般给 App 的权限是 不需要SUPER权限 的。
参数 super_read_only 可以将有 SUPER权限 的用户也设置为 只读 ,且该参数设置为 ON 后, read_only 也跟着 自动 设置为 ON

mysql root@localhost:(none)> show variables like "read_only";  
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| read_only     | OFF   |   
+---------------+-------+
1 row in set
Time: 0.009s
mysql root@localhost:(none)> set global super_read_only=1;   -- 开启super用户的read_only
Query OK, 0 rows affected
Time: 0.003s
mysql root@localhost:(none)> show variables like "read_only";
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| read_only     | ON    |      --配置自动启用
+---------------+-------+
1 row in set
Time: 0.007s
mysql root@localhost:(none)>

3.7. mysqlreplicate 搭建主从复制

MySQL Utilities官方文档

使用 mysqlreplicate 需要安装 mysql-utilities 包

[root@node1 software]# rpm -ivh https://cdn.mysql.com//Downloads/Connector-Python/mysql-connector-python-2.1.7-1.el7.x86_64.rpm
[root@node1 software]# rpm -ivh https://cdn.mysql.com//Downloads/MySQLGUITools/mysql-utilities-1.6.5-1.el7.noarch.rpm

3.7.1. 测试

  1. 搭建一个备机 node3,并初始化实例;
  2. node3 上新建一个用户 \'gcdb\'@\'%\' (%可以换成内网网段);
  3. 然后在 node3 或 node1 (或者其他任何可以连接到Master/Slave的机器上)执行如下命令
mysql root@localhost:performance_schema> create user \'repl\'@\'192.168.88.100\' identified by \'123456\';

mysql root@localhost:performance_schema> select user,host from mysql.user;
+-----------+----------------+
| user      | host           |
+-----------+----------------+
| gcdb      | %              |
| monitor   | %              |
| repl      | 192.168.88.100 |
| repl      | 192.168.88.99  |
| dbbackup  | localhost      |
| mysql.sys | localhost      |
| operator  | localhost      |
| root      | localhost      |
+-----------+----------------+
8 rows in set
Time: 0.005s

mysql root@localhost:performance_schema> grant replication slave on *.* to \'repl\'@\'192.168.88.100\';
Query OK, 0 rows affected
Time: 0.010s

mysql root@localhost:performance_schema> flush privileges;
Query OK, 0 rows affected
Time: 0.004s
[root@node1 software]#  mysqlreplicate --master=gcdb:iforgot@192.168.88.88:3306 --slave=gcdb:iforgot@192.168.88.100:3306 --rpl-user=repl:123456 -b
WARNING: Using a password on the command line interface can be insecure.
# master on 192.168.88.88: ... connected.
# slave on 192.168.88.100: ... connected.
# Checking for binary logging on master...
# Setting up replication...
# ...done.
[root@node1 software]#

然后在 node3 上执行 show slave status 操作, 复制正常

以上是关于026:复制的主要内容,如果未能解决你的问题,请参考以下文章

这两个代码片段之间有区别吗?如果有,那又如何? [复制]

什么是在 C++ 中获取总内核数量的跨平台代码片段? [复制]

Android:使用支持片段管理器时复制片段

Qt&Vtk-026-QScalarsToColors

AtCoder Grand Contest 026 (AGC026) E - Synchronized Subsequence 贪心 动态规划

VsCode 代码片段-提升研发效率