集群
单节点数据库
优点:适合数据量小的网站,如企业网站。
缺点:单个数据库无法满足日益增长的读写请求;
为什么要使用集群
高可用性:站点高可用,冗余站点;服务高可用,冗余服务;数据高可用,冗余数据。
负载均衡:切换某服务访问某节点,分摊单个节点的数据库压力
可伸缩性:新增数据库节点便利,方便扩容。
MySQL集群缺点
- 网络分裂:群集还可能由于网络故障而拆分为多个部分,每部分内的节点相互连接,但各部分之间的节点失去连接。
- 脑裂:导致数据库节点彼此独立运行的集群故障称为“脑裂”。这种情况可能导致数据不一致,并且无法修复,例如当两个数据库节点独立更新同一表上的同一行时
MySQL集群中各实例的数据同步,均基于mysql的复制机制。
原理
第一步:主库开启bin-log日志: ** 主库需要设置开启记录二进制日志,提交数据更新的事务之前将更新的事件记录都二进制日志中,后才进行事务提交。
第二步:备库建立IO线程连接: ** 备库创建IO线程与主库创建连接,主库上启动一个binlog-dump线程。该线程会读取主库二进制事件,该线程不会对事件进行轮询。如果从库数据与主库已经保持一致后,该线程进入休眠状态,一旦主库有新事件会以信号量唤醒binlog-dump线程。备库IO线程会将事件写入到中继日志(relay-log)中。
第三步:SQL线程重放数据:**SQL线程执行最后一步,从中继日志中获取事件并在备库中执行,从而实现数据库的更新。
类型
M-S:一主一从
M-S-S:一主多从
M-M:双主
M-M-S-S:双主多从
实验
M-S实践(手动指定POS位置)
环境
master1——192.168.28.141——mysql8.0.34——数据库已有数据
[root@master1 ~]# mysql -uroot -p'Admin.123'
mysql> create database class;
Query OK, 1 row affected (0.00 sec)
mysql> create table class.stu (id int,name varchar(50));
Query OK, 0 rows affected (0.02 sec)
mysql> insert into class.stu values(1,'a');
Query OK, 1 row affected (0.05 sec)mysql> select * from class.stu;
+------+------+
| id | name |
+------+------+
| 1 | a |
+------+------+
1 row in set (0.00 sec)
slave1——192.168.28.143——mysql8.0.34——数据库没有数据
master1
开启二进制日志,指定master的server-id标识为1
[root@master1 ~]# echo -e "log_bin \nserver-id=1" >> /etc/my.cnf
[root@master1 ~]# systemctl restart mysqld
创建复制用户并授权
[root@master1 ~]# mysql -uroot -p'Admin.123'
mysql> create user 'rep'@'192.168.28.%' identified by 'Admin.123';
Query OK, 0 rows affected (0.05 sec)mysql> grant replication slave on *.* to 'rep'@'192.168.28.%';
Query OK, 0 rows affected (0.00 sec)mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)mysql> show grants for 'rep'@'192.168.28.%';
+--------------------------------------------------------+
| Grants for rep@192.168.28.% |
+--------------------------------------------------------+
| GRANT REPLICATION SLAVE ON *.* TO `rep`@`192.168.28.%` |
+--------------------------------------------------------+
1 row in set (0.01 sec)
备份
[root@master1 ~]# mysqldump -uroot -p'Admin.123' -A --single-transaction --source-data=2 --flush-logs > `date +'%F-%T'-all.sql`
[root@master1 ~]# ls
2023-09-10-21:20:02-all.sql anaconda-ks.cfg[root@master1 ~]# cat 2023-09-10-21\:20\:02-all.sql
...
-- CHANGE MASTER TO MASTER_LOG_FILE='master1-bin.000003', MASTER_LOG_POS=157;
将备份文件传输至slave1上
[root@master1 ~]# scp ./2023-09-10-21\:20\:02-all.sql root@slave1:/root
slave1
[root@slave1 ~]# ls
2023-09-10-21:20:02-all.sql anaconda-ks.cfg#测试能否以复制用户登录master1的数据库
[root@slave1 ~]# mysql -h master1 -urep -p'Admin.123'
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
...#设置slave1的server-id
[root@slave1 ~]# echo "server-id=2" >> /etc/my.cnf
[root@slave1 ~]# systemctl restart mysqld
恢复数据
[root@slave1 ~]# mysql -uroot -p'Admin.123'
mysql> set sql_log_bin=0; #关闭二进制日志,避免产生因恢复操作出现的日志
mysql> source /root/2023-09-10-21:20:02-all.sql #导入数据
mysql> change master to master_host='master1',master_user='rep',master_password='Admin.123',master_log_file='/var/lib/mysql/master1-bin.000003',master_log_pos=157;
mysql> start slave; #开启同步#查看从数据库的状态
mysql> show slave status\G;
*************************** 1. row ***************************Slave_IO_State: Waiting for source to send eventMaster_Host: master1Master_User: repMaster_Port: 3306Connect_Retry: 60Master_Log_File: master1-bin.000003Read_Master_Log_Pos: 157Relay_Log_File: slave1-relay-bin.000002Relay_Log_Pos: 328Relay_Master_Log_File: master1-bin.000003Slave_IO_Running: YesSlave_SQL_Running: Yes
...
- Slave_IO_State: 显示从服务器I/O线程的当前状态。
- Slave_IO_Running: 表示从服务器的I/O线程是否正在运行,如果值为"YES",则说明I/O线程正常运行。
- Slave_SQL_Running: 表示从服务器的SQL线程是否正在运行,如果值为"YES",则说明SQL线程正常运行。
- Master_Log_File: 显示主服务器当前二进制日志的位置。
- Read_Master_Log_Pos: 显示从服务器当前读取主服务器二进制日志的位置。
- Relay_Log_File: 显示从服务器当前中继日志的位置。
- Relay_Log_Pos: 显示从服务器当前中继日志的位置。
- Connect_Retry: 显示从服务器尝试连接到主服务器的重试间隔时间(单位为秒)。
此时数据已保持一致
mysql> select * from class.stu;
+------+------+
| id | name |
+------+------+
| 1 | a |
+------+------+
1 row in set (0.01 sec)
在master1中新增数据
mysql> insert into class.stu values(2,'b');
Query OK, 1 row affected (0.00 sec)mysql> select * from class.stu;
+------+------+
| id | name |
+------+------+
| 1 | a |
| 2 | b |
+------+------+
2 rows in set (0.00 sec)
查看slave1是否同步,已同步
mysql> select * from class.stu;
+------+------+
| id | name |
+------+------+
| 1 | a |
| 2 | b |
+------+------+
2 rows in set (0.00 sec)
双主双从(自动记录POS位置)
双主避免单点故障。
gtid_mode=ON 是一种MySQL的特性,它为每个事务分配一个唯一的全局事务ID。这个选项用于启用GTID。在大多数情况下,你应该开启这个选项,因为它可以提供更好的复制和故障恢复功能。
enforce_gtid_consistency=1 这个选项确保了在MySQL服务器上所有线程和复制都严格遵守GTID的一致性。如果设置为1,那么MySQL将不允许在同一个会话中执行两次相同的事务。这对于确保数据的一致性非常有用。
数据库配置文件添加如下
log_bin
server-id=1 #每台服务器server-id 不一样
gtid_mode=ON
enforce_gtid_consistency=1
一、配置 master1 为 master2 的主服务器
master1——192.168.28.141
[root@master1 ~]# mysql -uroot -p'Admin.123'
mysql> create database class;
Query OK, 1 row affected (0.00 sec)mysql> create table class.stu (id int,name varchar(50));
Query OK, 0 rows affected (0.01 sec)mysql> insert into class.stu values(1,'wei');
Query OK, 1 row affected (0.09 sec)mysql> select * from class.stu;
+------+------+
| id | name |
+------+------+
| 1 | wei |
+------+------+
1 row in set (0.00 sec)[root@master1 ~]# vim /etc/my.cnf
...
log_bin
server-id=1
gtid_mode=ON
enforce_gtid_consistency=1
[root@master1 ~]# systemctl restart mysqld
[root@master1 ~]# mysql -uroot -p'Admin.123'
mysql> create user 'rep'@'192.168.28.%' identified by 'Admin.123';
Query OK, 0 rows affected (0.05 sec)mysql> grant replication slave on *.* to 'rep'@'192.168.28.%';
Query OK, 0 rows affected (0.00 sec)mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)mysql> show grants for 'rep'@'192.168.28.%';
+--------------------------------------------------------+
| Grants for rep@192.168.28.% |
+--------------------------------------------------------+
| GRANT REPLICATION SLAVE ON *.* TO `rep`@`192.168.28.%` |
+--------------------------------------------------------+
1 row in set (0.01 sec)[root@master1 ~]# mysqldump -uroot -p'Admin.123' -A --single-transaction --source-data=2 --flush-logs > `date +'%F-%T'-all.sql`
[root@master1 ~]# scp ./2023-09-11-02\:10\:19-all.sql root@192.168.28.143:/root/
master2——192.168.28.143
[root@master2 ~]# vim /etc/my.cnf
...
log_bin
server-id=1
gtid_mode=ON
enforce_gtid_consistency=1
[root@master2 ~]# systemctl restart mysqld
[root@master2 ~]# ls
2023-09-11-02:10:19-all.sql anaconda-ks.cfg[root@master2 ~]# mysql -uroot -p'Admin.123'
mysql> set sql_log_bin=0;
mysql> source /root/2023-09-11-02:10:19-all.sql
mysql> change master to master_host='master1',master_user='rep',master_password='Admin.123',get_master_public_key=1,master_auto_position=1;
mysql> start slave;
mysql> show slave status\G;
*************************** 1. row ***************************Slave_IO_State: Waiting for source to send eventMaster_Host: master1Master_User: repMaster_Port: 3306Connect_Retry: 60Master_Log_File: master1-bin.000005Read_Master_Log_Pos: 197Relay_Log_File: master2-relay-bin.000002Relay_Log_Pos: 377Relay_Master_Log_File: master1-bin.000005Slave_IO_Running: YesSlave_SQL_Running: Yes
...
mysql> select * from class.stu;
+------+------+
| id | name |
+------+------+
| 1 | wei |
+------+------+
1 row in set (0.00 sec)
get_master_public_key=1 在MySQL的主从复制中,主服务器会生成一个公钥(即get_master_public_key),并将其发送给从服务器。从服务器使用该公钥与主服务器建立加密连接,以确保数据传输的安全性。不加此选项会有如下报错
2023-09-10T16:27:42.806971Z 10 [ERROR] [MY-010584] [Repl] Replica I/O for channel '': Error connecting to source 'rep@master1:3306'. This was attempt 2/86400, with a delay of 60 seconds between attempts. Message: Authentication plugin 'caching_sha2_password' reported error: Authentication requires secure connection. Error_code: MY-002061
二、配置 master2 为 master1 的主服务器
master2——192.168.28.143
[root@master2 ~]# mysql -uroot -p'Admin.123'
mysql> create user 'per'@'192.168.28.%' identified by 'Admin.123';
Query OK, 0 rows affected (0.00 sec)mysql> grant replication slave on *.* to 'per'@'192.168.28.%';
Query OK, 0 rows affected (0.00 sec)mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)mysql> show grants for 'per'@'192.168.28.%';
+--------------------------------------------------------+
| Grants for per@192.168.28.% |
+--------------------------------------------------------+
| GRANT REPLICATION SLAVE ON *.* TO `per`@`192.168.28.%` |
+--------------------------------------------------------+
1 row in set (0.00 sec)
master1——192.168.28.141
[root@master1 ~]# mysql -uroot -p'Admin.123'
mysql> change master to master_host='master2',master_user='per',master_password='Admin.123',get_master_public_key=1,master_auto_position=1;
Query OK, 0 rows affected, 8 warnings (0.00 sec)mysql> start slave;
Query OK, 0 rows affected, 1 warning (0.00 sec)mysql> show slave status\G;
*************************** 1. row ***************************Slave_IO_State: Waiting for source to send eventMaster_Host: master2Master_User: perMaster_Port: 3306Connect_Retry: 60Master_Log_File: master2-bin.000002Read_Master_Log_Pos: 663Relay_Log_File: master1-relay-bin.000002Relay_Log_Pos: 747Relay_Master_Log_File: master2-bin.000002Slave_IO_Running: YesSlave_SQL_Running: Yes
...
三、测试双主效果
在master1新增数据,观察master2是否同步
[root@master1 ~]# mysql -uroot -p'Admin.123'
mysql> insert into class.stu values(2,'shu');
Query OK, 1 row affected (0.00 sec)mysql> select * from class.stu;
+------+------+
| id | name |
+------+------+
| 1 | wei |
| 2 | shu |
+------+------+
2 rows in set (0.00 sec)[root@master2 ~]# mysql -uroot -p'Admin.123'
mysql> select * from class.stu;
+------+------+
| id | name |
+------+------+
| 1 | wei |
| 2 | shu |
+------+------+
2 rows in set (0.01 sec)
在master2新增数据,观察master1是否同步
[root@master2 ~]# mysql -uroot -p'Admin.123'
mysql> insert into class.stu values(3,'wu');
Query OK, 1 row affected (0.00 sec)mysql> select * from class.stu;
+------+------+
| id | name |
+------+------+
| 1 | wei |
| 2 | shu |
| 3 | wu |
+------+------+
3 rows in set (0.00 sec)[root@master1 ~]# mysql -uroot -p'Admin.123'
mysql> select * from class.stu;
+------+------+
| id | name |
+------+------+
| 1 | wei |
| 2 | shu |
| 3 | wu |
+------+------+
3 rows in set (0.00 sec)
四、配置双从
master1备份数据,用以两个slave同步数据
[root@master1 ~]# mysqldump -uroot -p'Admin.123' -A --single-transaction --source-data=2 --flush-logs > `date +'%F-%T'-all.sql`
[root@master1 ~]# ls
2023-09-11-03:11:40-all.sql anaconda-ks.cfg[root@master1 ~]# scp ./2023-09-11-02\:10\:19-all.sql root@192.168.28.144:/root/
root@192.168.28.144's password:
2023-09-11-02:10:19-all.sql 100% 1255KB 57.5MB/s 00:00
[root@master1 ~]# scp ./2023-09-11-02\:10\:19-all.sql root@192.168.28.145:/root/
root@192.168.28.145's password:
2023-09-11-02:10:19-all.sql
slave1——192.168.28.144
[root@slave1 ~]# ls
2023-09-11-02:10:19-all.sql anaconda-ks.cfg
[root@slave1 ~]# mysql -uroot -p'Admin.123' < 2023-09-11-02\:10\:19-all.sql[root@slave1 ~]# vim /etc/my.cnf
...
log_bin
server-id=3
gtid_mode=ON
enforce_gtid_consistency=1master-info-repository=TABLE
relay-log-info-repository=TABLE
[root@slave1 ~]# systemctl restart mysqld[root@slave1 ~]# mysql -uroot -p'Admin.123'
mysql> change master to master_host='192.168.28.141',master_user='rep',master_password='Admin.123',get_master_public_key=1,master_auto_position=1 for channel '192.168.28.141';
Query OK, 0 rows affected, 8 warnings (0.00 sec)mysql> change master to master_host='192.168.28.143',master_user='per',master_password='Admin.123',get_master_public_key=1,master_auto_position=1 for channel '192.168.28.143';
Query OK, 0 rows affected, 8 warnings (0.00 sec)mysql> start slave;
Query OK, 0 rows affected, 1 warning (0.01 sec)mysql> show slave status\G;
*************************** 1. row ***************************Slave_IO_State: Waiting for source to send eventMaster_Host: 192.168.28.141Master_User: repMaster_Port: 3306Connect_Retry: 60Master_Log_File: master1-bin.000006Read_Master_Log_Pos: 237Relay_Log_File: slave1-relay-bin-192@002e168@002e28@002e141.000004Relay_Log_Pos: 457Relay_Master_Log_File: master1-bin.000006Slave_IO_Running: YesSlave_SQL_Running: Yes
...
*************************** 2. row ***************************Slave_IO_State: Waiting for source to send eventMaster_Host: 192.168.28.143Master_User: perMaster_Port: 3306Connect_Retry: 60Master_Log_File: master2-bin.000002Read_Master_Log_Pos: 1235Relay_Log_File: slave1-relay-bin-192@002e168@002e28@002e143.000002Relay_Log_Pos: 1455Relay_Master_Log_File: master2-bin.000002Slave_IO_Running: YesSlave_SQL_Running: Yesmysql> select * from class.stu;
+------+------+
| id | name |
+------+------+
| 1 | wei |
| 2 | shu |
| 3 | wu |
+------+------+
3 rows in set (0.00 sec)
slave2——192.168.28.145
[root@slave2 ~]# ls
2023-09-11-02:10:19-all.sql anaconda-ks.cfg
[root@slave2 ~]# mysql -uroot -p'Admin.123' < 2023-09-11-02\:10\:19-all.sql[root@slave2 ~]# vim /etc/my.cnf
...
log_bin
server-id=4
gtid_mode=ON
enforce_gtid_consistency=1master-info-repository=TABLE
relay-log-info-repository=TABLE
[root@slave2 ~]# systemctl restart mysqld[root@slave2 ~]# mysql -uroot -p'Admin.123'
mysql> change master to master_host='192.168.28.141',master_user='rep',master_password='Admin.123',get_master_public_key=1,master_auto_position=1 for channel '192.168.28.141';
Query OK, 0 rows affected, 8 warnings (0.00 sec)mysql> change master to master_host='192.168.28.143',master_user='per',master_password='Admin.123',get_master_public_key=1,master_auto_position=1 for channel '192.168.28.143';
Query OK, 0 rows affected, 8 warnings (0.00 sec)mysql> start slave;
Query OK, 0 rows affected, 1 warning (0.01 sec)mysql> show slave status\G;
*************************** 1. row ***************************Slave_IO_State: Waiting for source to send eventMaster_Host: 192.168.28.141Master_User: repMaster_Port: 3306Connect_Retry: 60Master_Log_File: master1-bin.000006Read_Master_Log_Pos: 237Relay_Log_File: slave2-relay-bin-192@002e168@002e28@002e141.000004Relay_Log_Pos: 457Relay_Master_Log_File: master1-bin.000006Slave_IO_Running: YesSlave_SQL_Running: Yes
...
*************************** 2. row ***************************Slave_IO_State: Waiting for source to send eventMaster_Host: 192.168.28.143Master_User: perMaster_Port: 3306Connect_Retry: 60Master_Log_File: master2-bin.000002Read_Master_Log_Pos: 1235Relay_Log_File: slave2-relay-bin-192@002e168@002e28@002e143.000002Relay_Log_Pos: 1455Relay_Master_Log_File: master2-bin.000002Slave_IO_Running: YesSlave_SQL_Running: Yesmysql> select * from class.stu;
+------+------+
| id | name |
+------+------+
| 1 | wei |
| 2 | shu |
| 3 | wu |
+------+------+
3 rows in set (0.00 sec)
master-info-repository 是MySQL主从复制配置中的选项,用于指定主服务器信息存储的方式。它可以在配置文件或表中存储主服务器的信息。
- 当master-info-repository的值为FILE时,主服务器的信息会被保存在从服务器数据目录下的master.info和relay-log.info两个文件中。
- 当master-info-repository的值为TABLE时,主服务器的信息会被保存在MySQL数据库的master_slave_info表中。
relay-log-info-repository 是MySQL主从复制中的一个参数,用于指定relay log信息存储的方式。
- 当relay-log-info-repository的值为TABLE时,从服务器上的relay log信息会被保存在MySQL数据库的slave_relay_log_info表中,以供从服务器在处理复制过程中使用。
- relay-log-info-repository的默认值取决于存储引擎。在InnoDB存储引擎下,relay-log-info-repository的默认值是TABLE。
经测试,当修改master1或master2数据时,其余三台设均能实现数据同步。