本教程以MySQL8为主版本。
所有MySQL文章的目录为:总目录 https://blog.csdn.net/zyplanke/article/details/102968014
本文介绍MySQL中主从复制Replication种最基础也是最重要的异步复制的搭建过程。 包括两级复制(一主、一从),三级复制(一主、一从、再从)。
目录
一、复制架构衍生史
二、易混淆概念区分
三、环境介绍
四、主Master节点(IP:43.201)
1、Master节点配置文件
2、创建Slave连接用的用户
3、备份Master节点,作为Slave的初始数据
五、从Slave节点(IP:43.203)
1、Slave节点配置文件
2、Slave从库导入
3、从库同步设置
六、 一主一从复制效果验证
七、三级复制模式
1、修改节点43.203配置文件
2、修改43.205节点配置文件
3、从43.203导出数据
4、数据导入至43.205
5、取消43.203只读设置
6、从库43.205同步设置
八、三层复制效果验证
在谈这个特性之前,我们先来看看MySQL的复制架构衍生史。
在2000年,MySQL 3.23.15版本引入了Replication。Replication作为一种准实时同步方式,得到广泛应用。这个时候的Replicaton的实现涉及到两个线程,一个在Master,一个在Slave。Slave的I/O和SQL功能是作为一个线程,从Master获取到event后直接apply,没有relay log。这种方式使得读取event的速度会被Slave replay速度拖慢,当主备存在较大延迟时候,会导致大量binary log没有备份到Slave端。
在2002年,MySQL 4.0.2版本将Slave端event读取和执行独立成两个线程(IO线程和SQL线程),同时引入了relay log。IO线程读取event后写入relay log,SQL线程从relay log中读取event然后执行。这样即使SQL线程执行慢,Master的binary log也会尽可能的同步到Slave。当Master宕机,切换到Slave,不会出现大量数据丢失。
在2010年MySQL 5.5版本之前,一直采用的是这种异步复制的方式。主库的事务执行不会管备库的同步进度,如果备库落后,主库不幸crash,那么就会导致数据丢失。于是在MySQL在5.5中就顺其自然地引入了半同步复制,主库在应答客户端提交的事务前需要保证至少一个从库接收并写到relay log中。那么半同步复制是否可以做到不丢失数据呢?下面分析。
在2016年,MySQL在5.7.17开始中引入了一个全新的技术,称之为InnoDB Group Replication。全同步技术带来了更多的数据一致性保障。
根据上面提到的这几种复制协议,分别对应MySQL几种复制类型,分别是异步、半同步、全同步。
注意上图只画出了MySQL 5.7版本之前的AFTER_COMMIT,未画出5.7版本后支持的AFTER_SYNC
注:在InnoDB双一设置(sync_binlog=1和innodb_flush_log_at_trx_commit=1)时,在commit过程中,redo log是先于binlog写入磁盘的。
涉及到以下几个参数或概念
bin_log: 数据库服务器(Master和Slave)自身的数据变化,而记录的event二进制数据。 既bin_log是自身节点上的数据操作(变更)而记录的自己的二进制文件。
reley_log: 自己作为从节点,从主节点(别的节点)上拷贝过来的event二进制日志,该日志文件,可以理解为是主节点数据的拷贝,目的是为了使应用该日志,使自己保持与主节点数据一致。
log_slave_updates:自己作为从节点,在应用上面的relay_log的同时,把这些relay_log上的event视为自身节点上的数据操作(变更),记录仅自己的二进制文件。 目的是实现二进制日志文件的多级传递,比如B是A的从节点,C是B的从节点。则B需要开启本参数,以实现从A收到的relay_log在应用后,还同时记录入B的binlog,然后C从B获得的二进制文件(C的relay_log)里面才可以包含A节点的bin_log内容
IP | 操作系统 | 数据库版本 | 用途 |
192.168.43.201 | CentOS 7.7 x64bit | MySQL 8.0.18,已有数据 | 作为Master主节点 |
192.168.43.203 | CentOS 7.7 x64bit | MySQL 8.0.18,全新库 | 先作为201的Slave从节点,然后再作为205的主节点 |
192.168.43.205 | CentOS 7.7 x64bit | MySQL 8.0.18,全新库 | 作为203的Slave从节点 |
主从复制环境,应保证操作系统和数据库版本一致,减少出问题的概率。
确保Master 和 Slave 的 server_id 不能相同,并且Master开启binlog二进制日志(设置文件名前缀为binlog),并在每次commit的同时sync写到binlog文件中。因此编辑/etc/my.cnf文件,增加配置。
[mysqld]
server_id=201
log_bin=binlog
sync_binlog=1
binlog_format=ROW
binlog_rows_query_log_events=ON
#另外还有其他参数,酌情选择设置
#log_bin_trust_function_creators=ON # 是否同步函数(默认OFF)
#binlog_expire_logs_seconds=2592000 #binlog日志文件留存多少秒(默认为30天,之前的expire_logs_day参数已经作废。)
#binlog_cache_size=1M #默认32K,根据业务繁忙程度设置,通常1~4MB
注1:配置文件中参数中的“中划线”和“下划线”都可以。无论是哪种划线(甚至混合也可以),最后显示的变量都是下划线。
注2:对于老版本MySQL5.x,关闭binlog使用“log_bin=OFF”或者“log_bin=0”即可关闭。在MySQL8中,这种方式已无法关闭binlog,需要使用:“disable_log_bin”配置关闭。
注3:binlog_format为ROW或MIX时,对于涉及Temporary临时表的操作,主库不会记录binlog,所以涉及临时表的操作主库不会复制给从库。 若format为STATEMENT,主库记录binlog,会复制给从库。 从复制数据的可靠性考虑,通常format都会选择ROW。
然后重启主节点的mysqld服务。
在Master节点上,创建一个用户,用于数据同步Slave从节点连接Master的用户。 (限制该用户只能从本局域网登录)
mysql> CREATE USER 'replica'@'192.168.43.%' IDENTIFIED BY 'Replica_999';
Query OK, 0 rows affected (0.00 sec)
mysql> GRANT REPLICATION SLAVE ON *.* TO 'replica'@'192.168.43.%';
Query OK, 0 rows affected (0.00 sec)
注:该复制用户在主库、从库均需要存在(下文通过备份实现了从库也有此用户,因此在从库无需再手动建立此用户)
【注】因复制只复制关联建立后的数据,对于之前的数据不会复制。所以需要人工通过备份恢复方式,形成主、从机器的初始数据时一致的。
备份的方式可以多样:
下文以mysqldump方式进行说明。
a、为保证备份期间数据一致性,对主库执行以下命令,将主库设置为禁止写操作。
mysql> FLUSH TABLES WITH READ LOCK;
Query OK, 0 rows affected (0.00 sec)
mysql> SET GLOBAL read_only=ON;
Query OK, 0 rows affected (0.00 sec)
注:①set global read_only是全局级别的参数(不是锁),置为ON之后,普通用户只能读不能写,具有super权限的用户仍可以写。 此属性设置在从节点上,不会影响binlog的relay和应用,既在从节点上设置此属性为只读,从节点salve仍然会读取master上的日志,并且在slave库中应用日志,保证主从数据库同步一致。
②FLUSH TABLES WITH READ LOCK(FTWRL)是表级别的锁(tables指所有表,还可以指定具体的表),执行之后,包括具有super权限的所有用户都不能写表。一直锁定直到unlock。 在从节点执行FTWRL后,由于表都被锁住,从节点对binlog的relay应用也无法写到表中,影响从节点的数据同步。 因此FTWRL通常仅在临时彻底只读时使用。
b、查看当前Master主节点binlog日志名和pos偏移量(因上步已设置为只读,在只读期间偏移量不会变化)
mysql> show master status;
+---------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+---------------+----------+--------------+------------------+-------------------+
| binlog.000003 | 155 | | | |
+---------------+----------+--------------+------------------+-------------------+
1 row in set (0.01 sec)
c、全库备份。
[zyplanke@centosb ~]$ mysqldump -h 192.168.43.201 -u root -p --all-databases --single-transaction > dump_of_master.sql
Enter password:
[zyplanke@centosb ~]$ ls -l dump_of_master.sql
-rw-rw-r--. 1 zyplanke zyplanke 15943508 May 6 00:11 dump_of_master.sql
d、备份完毕后,对主库执行以下命令,恢复Master主节点写操作。
mysql> SET GLOBAL read_only=OFF;
Query OK, 0 rows affected (0.00 sec)
mysql> UNLOCK TABLES;
Query OK, 0 rows affected (0.00 sec)
Slave 的 server_id 不能与主节点相同。
[mysqld]
server_id=203
read_only=ON
注:若从节点不用于写入用途,则将从节点设置为只读(如上)。 若从节点将用于写入用途,则不要设置为只读,并且开启写从节点自身的binlog(既log_bin=binlog和sysc_binlog=1两个参数)。从节点自身的binlog记录的是写操作对从节点的自身binlog,而不是从主节点复制过来的relaylog。
注:如果从节点可能接替Master,升为主节点,则Slave节点的应设置为read_only=OFF。
如果已经启动了GTID模式,还需要删除从节点datadir目录下的auto.cnf文件(删除后,重启时会自动新生成UUID),避免与主节点具有相同的UUID。
然后重启从节点的mysqld服务。 (如果重启之前已经配置了同步设置,则在配置文件中增加skip-slave-start参数,这样启动后不会立即从主库上进行同步。)
[zyplanke@centosb ~]$ mysql -h 192.168.43.203 -u root -p < dump_of_master.sql
Enter password:
注:即使将从库设置read_only属性,也能导入成功,因为导入使用的root用户具有super权限。 read_only不限制具有super权限的用户进行写操作。
导入完毕后,检查导入后的数据库。
在43.203从库中,执行以下语句连接到master主库:
mysql> change master to
master_host='192.168.43.201',
master_port=3306,
master_user='replica',
master_password='Replica_999',
get_master_public_key=1,
master_log_file='binlog.000003',
master_log_pos=155
for channel 'channel_201_3306';
mysql> start slave;
Query OK, 0 rows affected (0.01 sec)
启动slave后,立即能看到slave的状态
【说明】MySQL8.0版本开始由于用户默认使用caching_sha2_password认证,该认证除了密码字符串加密方式不同外,还要求客户端与服务端之间必须通过secure connection安全连接访问。上面的replica复制专用用户需要与主库建立连接,也需要使用安全连接,否则会报:Authentication plugin 'caching_sha2_password' reported error: Authentication requires secure connection 错误。对此,通常有三种解决办法:
①将replica用户认证方式由默认的“caching_sha2_password”改为“mysql_native_password”(在主库、从库对该用户均需修改),直接避免此问题。
②如上,在change master指令中启用“get_master_public_key”使在该用户在连接主库时自动获得主库的公钥。
③将主库公钥文件拷贝至从机上,在change master指令中“master_public_key_path”指向拷贝得到公钥文件。
注:若MySQL服务端(这里即主库)重启时,其公钥会自动重新生成,则客户端(这里即从库)也需要重新连接以获得最新公钥才能成功连接。 对于从库会以Connect_Retry的秒数自动重连。
mysql> show slave status \G;
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.43.201
Master_User: replica
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: binlog.000003
Read_Master_Log_Pos: 155
Relay_Log_File: centosb-relay-bin.000002
Relay_Log_Pos: 319
Relay_Master_Log_File: binlog.000003
Slave_IO_Running: Yes
Slave_SQL_Running: 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: 155
Relay_Log_Space: 529
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: 201
Master_UUID: aa951098-207f-11ea-94ec-000c29939c22
Master_Info_File: mysql.slave_master_info
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:
Master_public_key_path:
Get_master_public_key: 1
Network_Namespace:
1 row in set (0.00 sec)
分别查看主库和从库上面的process。
# 以下为43.201的信息:
mysql> show processlist;
+----+-----------------+----------------------+------+-------------+------+---------------------------------------------------------------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-----------------+----------------------+------+-------------+------+---------------------------------------------------------------+------------------+
| 4 | event_scheduler | localhost | NULL | Daemon | 525 | Waiting on empty queue | NULL |
| 8 | root | localhost | NULL | Query | 0 | starting | show processlist |
| 11 | replica | 192.168.43.203:54456 | NULL | Binlog Dump | 109 | Master has sent all binlog to slave; waiting for more updates | NULL |
+----+-----------------+----------------------+------+-------------+------+---------------------------------------------------------------+------------------+
3 rows in set (0.00 sec)
# 以下为43.203的信息:
mysql> show processlist;
+----+-----------------+-----------------+------+---------+------+--------------------------------------------------------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-----------------+-----------------+------+---------+------+--------------------------------------------------------+------------------+
| 4 | event_scheduler | localhost | NULL | Daemon | 1686 | Waiting on empty queue | NULL |
| 20 | system user | connecting host | NULL | Connect | 176 | Waiting for master to send event | NULL |
| 21 | system user | | NULL | Query | 116 | Slave has read all relay log; waiting for more updates | NULL |
| 22 | root | centosb:51818 | NULL | Query | 0 | starting | show processlist |
+----+-----------------+-----------------+------+---------+------+--------------------------------------------------------+------------------+
4 rows in set (0.00 sec)
在主库上更新一条数据后,在从库上几乎立即就能看到更新后的结果,而且从库的show slave status中,log_pos的值也发生了变化。 说明主从数据同步已经能正常运行。
------- 至此,一主一从复制搭建完毕 --------
-------------------------------------------------------------------------
------- 下面搭建三级复制 --------
在上面一主一从的基础上,我们搭建三级复制模式。既:
43.201作为主节点 →→复制→→ 43.203作为43.201从节点 →→复制→→ 43.205作为43.203从节点
[mysqld]
server_id=205
read_only=ON
log_slave_updates=ON
log_bin=binlog
sync_binlog=1
binlog_format=ROW
binlog_rows_query_log_events=ON
#其他参数参考Master酌情设置
由于43.203是43.201的从节点的同时,还作为43.205的主节点,所以须在43.203配置中增加参数:
log_slave_updates=ON
该ON表示43.203从43.201复制binlog后,在应用43.201写入43.203本地库时,同步在43.203上生成binlog(如果参数值为OFF则表示不写入binlog)。这样43.203的binlog的包括全部的数据(包括43.201的数据),下游节点只需从43.203读取binlog即可。
[mysqld]
server_id=205
read_only=ON
注:若从节点不用于写入用途,则将从节点设置为只读(如上)。 若从节点将用于写入用途,则不要设置为只读,并且开启写从节点自身的binlog(既log_bin=binlog和sysc_binlog=1两个参数)。从节点自身的binlog记录的是写操作对从节点的自身binlog,而不是从主节点复制过来的relaylog。
如果已经启动了GTID模式,还需要删除从节点datadir目录下的auto.cnf文件(删除后,重启时会自动新生成UUID),避免与主节点具有相同的UUID。
然后重启从节点的mysqld服务。 (如果重启之前已经配置了同步设置,则在配置文件中增加skip-slave-start参数,这样启动后不会立即从主库上进行同步。)
因43.205的主节点43.203,因此导入的数据来源因为43.203。
首先,再43.203上创建用于43.205连接的用户replica(如有,无需重复创建)
然后,对43.203设置FTWRL和global read_only, 使43.203禁止写入。然后从43.203备份数据(操作步骤见上)
还需,在43.203上执行show master status命令,获得43.203上的当前的binlog日志文件名和pos值。
[zyplanke@centosb ~]$ mysql -h 192.168.43.205 -u root -p < dump_of_master.sql
Enter password:
导入完毕后,检查导入后的数据库。
在43.203中,取消FTWRL和global read_only设置。
在43.205从库中,执行以下语句连接到master主库:
mysql> change master to
master_host='192.168.43.203',
master_port=3306,
master_user='replica',
master_password='Replica_999',
get_master_public_key=1,
master_log_file='binlog.000008',
master_log_pos=653;
mysql> start slave;
Query OK, 0 rows affected (0.01 sec)
启动slave后,(稍等几秒钟后)查看slave的状态
mysql> show slave status \G;
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.43.203
Master_User: replica
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: binlog.000008
Read_Master_Log_Pos: 826
Relay_Log_File: centosc-relay-bin.000003
Relay_Log_Pos: 492
Relay_Master_Log_File: binlog.000008
Slave_IO_Running: Yes
Slave_SQL_Running: 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: 826
Relay_Log_Space: 702
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: 203
Master_UUID: 79e9dd41-8edb-11ea-b317-000c29859ed1
Master_Info_File: mysql.slave_master_info
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:
Master_public_key_path:
Get_master_public_key: 1
Network_Namespace:
1 row in set (0.00 sec)
分别查看43.203和43.205上面的process。
# 以下为43.203的信息:
mysql> show processlist;
+----+-----------------+----------------------+--------+-------------+------+---------------------------------------------------------------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-----------------+----------------------+--------+-------------+------+---------------------------------------------------------------+------------------+
| 4 | event_scheduler | localhost | NULL | Daemon | 1774 | Waiting on empty queue | NULL |
| 24 | root | centosb:44912 | dbtest | Query | 0 | starting | show processlist |
| 36 | replica | 192.168.43.205:56658 | NULL | Binlog Dump | 139 | Master has sent all binlog to slave; waiting for more updates | NULL |
| 37 | system user | connecting host | NULL | Connect | 25 | Waiting for master to send event | NULL |
| 38 | system user | | NULL | Query | 36 | Slave has read all relay log; waiting for more updates | NULL |
+----+-----------------+----------------------+--------+-------------+------+---------------------------------------------------------------+------------------+
5 rows in set (0.00 sec)
# 以下为43.205的信息:
mysql> show processlist;
+----+-----------------+-----------------+--------+---------+------+--------------------------------------------------------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-----------------+-----------------+--------+---------+------+--------------------------------------------------------+------------------+
| 4 | event_scheduler | localhost | NULL | Daemon | 283 | Waiting on empty queue | NULL |
| 11 | system user | connecting host | NULL | Connect | 261 | Waiting for master to send event | NULL |
| 12 | system user | | NULL | Query | 98 | Slave has read all relay log; waiting for more updates | NULL |
| 16 | root | centosc:57348 | dbtest | Query | 0 | starting | show processlist |
+----+-----------------+-----------------+--------+---------+------+--------------------------------------------------------+------------------+
4 rows in set (0.00 sec)
在43.201上新建表并inset一条数据,在43.203和43.205上上几乎立即就能看到同步的最新结果,而且从库的show slave status中,log_pos的值也发生了变化。 说明主从数据同步已经能正常运行。
同时,若在43.203上执行stop slave 暂停43.201至43.203的同步后,这时在43.201上变更数据,无法在43.203和43.205看到最新结果,而一旦在43.203上执行start slave启动后,几乎立即在43.203和43.205上就能看到最新结果。 这说明43.205的数据来源的确是从43.203复制过来的。