MySQL 1主2从集群搭建(推荐GTID)

📋 环境规划

主机名

IP地址

角色

server-id

db-master

192.168.7.10

主库(写)

1

db-slave1

192.168.7.20

从库1(读)

2

db-slave2

192.168.7.30

从库2(读)

3

主库配置 (db-master - 192.168.7.10)

修改配置文件

 # 编辑文件
 vim /etc/my.cnf
 ​
 # 在 [mysqld] 标签下添加:
 [mysqld]
 # 主库配置
 server-id=1
 log-bin=mysql-bin
 binlog_format=ROW
 # 不需要同步的数据库
 binlog-ignore-db=sys
 binlog-ignore-db=mysql
 binlog-ignore-db=information_schema
 binlog-ignore-db=performance_schema
 # 可选:指定需要同步的数据库
 # binlog-do-db=school_db
 ​
 # 重启Mysql服务
 systemctl restart mysqld

创建复制用户

 -- 登录MySQL
 mysql -uroot -p
 ​
 -- 创建复制专用用户
 CREATE USER 'repl'@'%' IDENTIFIED WITH mysql_native_password BY 'Repl123!';
 ​
 -- 授予复制权限
 GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
 ​
 -- 刷新权限
 FLUSH PRIVILEGES;

记录日志位点

 # 日志位点
 SHOW MASTER STATUS;
 ​
 **输出示例**:
 +------------------+----------+--------------+------------------+-------------------+
 | File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
 +------------------+----------+--------------+------------------+-------------------+
 | mysql-bin.000001 |      828 |              | sys,mysql,...    |                   |
 +------------------+----------+--------------+------------------+-------------------+

⚠️ 重要:记录下 File 和 Position 的值,配置从库时需要用到!

开放防火墙

 # 开放3306端口
 firewall-cmd --permanent --add-port=3306/tcp
 firewall-cmd --reload
 ​
 # 或直接关闭防火墙(测试环境)
 systemctl stop firewalld
 systemctl disable firewalld

初始化测试数据

 # 创建库
 CREATE DATABASE IF NOT EXISTS school_db 
     DEFAULT CHARACTER SET utf8mb4 
     COLLATE utf8mb4_unicode_ci;
     
 # 进入
 USE school_db;
 ​
 # 创建表
 CREATE TABLE students (
     id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
     name VARCHAR(50) NOT NULL,
     age TINYINT UNSIGNED,
     class_name VARCHAR(50),
     created_at DATETIME DEFAULT CURRENT_TIMESTAMP
 );
 ​
 # 插入数据
 INSERT INTO students(name, age, class_name) VALUES
 ('张三', 18, 'Q20班'),
 ('李四', 19, 'Q20班'),
 ('王五', 18, 'Q20班'),
 ('赵六', 20, 'Q20班'),
 ('孙七', 19, 'Q20班');

从库配置 (db-slave1 - 192.168.7.20)

修改配置文件

 # 编辑文件
 vim /etc/my.cnf
 ​
 # 在 [mysqld] 标签下添加:
 [mysqld]
 # 从库1配置
 server-id=2
 relay-log=mysql-relay
 read_only=1
 ​
 # 重启Mysql服务
 systemctl restart mysqld

测试主库连接

 # 测试能否连接主库
 mysql -urepl -pRepl123! -h 192.168.7.10 -P3306 -e "SELECT 1"

如果能正常返回 1,说明连接正常。

配置主从连接

 -- 登录MySQL
 mysql -uroot -p
 ​
 -- 停止已有的同步(如果有)
 STOP SLAVE;
 ​
 -- 配置主库信息
 CHANGE MASTER TO
 MASTER_HOST='192.168.7.10',
 MASTER_PORT=3306,
 MASTER_USER='repl',
 MASTER_PASSWORD='Repl123!',
 MASTER_LOG_FILE='mysql-bin.000001',  -- 替换为主库实际的File
 MASTER_LOG_POS=826;                  -- 替换为主库实际的Position
 ​
 -- 启动同步
 START SLAVE;

验证同步状态

 # 查询链接状态
 SHOW SLAVE STATUS\G;
 ​
 # 重点检查以下两项:
 Slave_IO_Running: Yes
 Slave_SQL_Running: Yes

从库配置 (db-slave2 - 192.168.7.30)

修改配置文件

 # 编辑文件
 vim /etc/my.cnf
 ​
 # 在 [mysqld] 标签下添加:
 [mysqld]
 # 从库1配置
 server-id=3
 relay-log=mysql-relay
 read_only=1
 ​
 # 重启Mysql服务
 systemctl restart mysqld

测试主库连接

 # 测试能否连接主库
 mysql -urepl -pRepl123! -h 192.168.7.10 -P3306 -e "SELECT 1"

配置主从连接

 -- 登录MySQL
 mysql -uroot -p
 ​
 -- 配置主库信息(与从库1完全相同)
 CHANGE MASTER TO
 MASTER_HOST='192.168.7.10',
 MASTER_PORT=3306,
 MASTER_USER='repl',
 MASTER_PASSWORD='Repl123!',
 MASTER_LOG_FILE='mysql-bin.000001',  -- 替换为主库实际的File
 MASTER_LOG_POS=826;                  -- 替换为主库实际的Position
 ​
 -- 启动同步
 START SLAVE;

验证同步状态

 # 查询链接状态
 SHOW SLAVE STATUS\G;
 ​
 # 重点检查以下两项:
 Slave_IO_Running: Yes
 Slave_SQL_Running: Yes

验证主从同步

在主库插入新数据

 -- 在主库执行 (192.168.7.10)
 USE school_db;
 INSERT INTO students(name, age, class_name) VALUES ('刘八', 21, 'Q20班');
 INSERT INTO students(name, age, class_name) VALUES ('陈九', 22, 'Q20班');

在从库查询数据

 -- 在从库1执行 (192.168.7.20)
 USE school_db;
 SELECT * FROM students;
 ​
 -- 在从库2执行 (192.168.7.30)
 USE school_db;
 SELECT * FROM students;

预期结果:两个从库都能看到新插入的数据。

📊 常用管理命令

查看同步状态

 -- 查看从库状态
 SHOW SLAVE STATUS\G;
 ​
 -- 查看主库状态
 SHOW MASTER STATUS;
 ​
 -- 查看进程列表
 SHOW PROCESSLIST;

暂停/恢复同步

 -- 暂停同步
 STOP SLAVE;
 ​
 -- 恢复同步
 START SLAVE;
 ​
 -- 重置同步(慎用)
 STOP SLAVE;
 RESET SLAVE;
 START SLAVE;

跳过错误(测试环境)

 -- 跳过1个错误
 STOP SLAVE;
 SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1;
 START SLAVE;
 ​
 -- 或配置文件跳过所有错误(不推荐生产)
 slave_skip_errors = ALL

🔍 故障排查

问题1:Slave_IO_Running: No

可能原因:

  • 网络不通

  • 复制用户密码错误

  • 防火墙阻止3306端口

解决方法:

 # 检查网络
 ping 192.168.7.10
 ​
 # 检查端口
 telnet 192.168.7.10 3306
 ​
 # 检查防火墙(主库和从库)
 systemctl status firewalld
 # 开放端口
 firewall-cmd --permanent --add-port=3306/tcp
 firewall-cmd --reload
 ​
 # 重新配置主从
 STOP SLAVE;
 CHANGE MASTER TO ...;
 START SLAVE;

问题2:Slave_SQL_Running: No

可能原因:

  • 数据冲突(主键重复)

  • 表不存在

解决方法:

 -- 查看具体错误
 SHOW SLAVE STATUS\G;
 -- 查看 Last_SQL_Error 字段
 ​
 -- 跳过错误
 STOP SLAVE;
 SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1;
 START SLAVE;

问题3:UUID冲突

原因:从库是克隆主库的虚拟机,auto.cnf 中的UUID相同。

解决方法:

 # 删除auto.cnf
 rm -f /var/lib/mysql/auto.cnf
 ​
 # 重启MySQL
 systemctl restart mysqld
 ​
 # MySQL会自动生成新的UUID

问题4:从库无法连接主库

 # 检查主库是否允许远程连接
 mysql -h 192.168.7.10 -uroot -p -e "SELECT host, user FROM mysql.user;"
 ​
 # 检查复制用户是否存在
 mysql -h 192.168.7.10 -uroot -p -e "SELECT host, user FROM mysql.user WHERE user='repl';"
 ​
 # 重新创建复制用户
 mysql -h 192.168.7.10 -uroot -p
 DROP USER 'repl'@'%';
 CREATE USER 'repl'@'%' IDENTIFIED WITH mysql_native_password BY 'Repl123!';
 GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
 FLUSH PRIVILEGES;