MySQL 主从架构中的使用技巧及优化

文档说明
本文档基于实际操作经验,对延迟复制和慢查询日志进行了系统性优化和扩展,补充了更多实用技巧和生产级最佳实践。
第一部分:延迟复制
1.1 什么是延迟复制
延迟复制是指设置从库故意落后于主库一定时间(如10秒、1小时等),主要用于:
- 误操作防护:主库误删除数据,可在延迟窗口内恢复
- 故障回溯:提供时间点恢复能力
- 读负载分离:不影响实时查询需求
1.2 配置延迟复制
MySQL 8.0+ 语法
sql
-- 停止复制
STOP REPLICA;
-- 设置延迟时间(单位:秒)
CHANGE REPLICATION SOURCE TO SOURCE_DELAY = 10;
-- 启动复制
START REPLICA;
-- 验证配置
SHOW REPLICA STATUS\G
MySQL 5.7 及以下语法
sql
STOP SLAVE;
CHANGE MASTER TO MASTER_DELAY = 10;
START SLAVE;
SHOW SLAVE STATUS\G
1.3 验证延迟配置
sql
mysql> SHOW SLAVE STATUS\G
| 字段 | 说明 |
|---|---|
SQL_Delay |
设置的延迟秒数 |
SQL_Remaining_Delay |
剩余延迟秒数(正在等待时显示) |
1.4 延迟复制测试
步骤1:查看当前数据
sql
mysql> SELECT * FROM baibai.userlist;
+-------+--------+
| name | passwd |
+-------+--------+
| swp | 123 |
| user2 | 123 |
+-------+--------+
2 rows in set (0.00 sec)
步骤2:在主库执行删除操作
sql
mysql> DELETE FROM baibai.userlist WHERE name='swp';
Query OK, 1 row affected (0.00 sec)
mysql> SELECT * FROM baibai.userlist;
+-------+--------+
| name | passwd |
+-------+--------+
| user2 | 123 |
+-------+--------+
1 row in set (0.00 sec)
步骤3:在无延迟从库查看(已同步)
bash
[root@mysql3 ~]# mysql -uroot -proot -e 'SELECT * FROM baibai.userlist;'
+-------+--------+
| name | passwd |
+-------+--------+
| user2 | 123 |
+-------+--------+
步骤4:在延迟从库查看(延迟窗口内数据未变)
sql
mysql> SELECT * FROM baibai.userlist;
+-------+--------+
| name | passwd |
+-------+--------+
| swp | 123 |
| user2 | 123 |
+-------+--------+
2 rows in set (0.00 sec)
步骤5:等待延迟时间过后再次查看
sql
mysql> SELECT * FROM baibai.userlist;
+-------+--------+
| name | passwd |
+-------+--------+
| user2 | 123 |
+-------+--------+
1 row in set (0.00 sec)
1.5 延迟复制的常见应用场景
| 场景 | 延迟时间建议 | 说明 |
|---|---|---|
| 防误删除 | 10-30分钟 | 给DBA留出反应时间 |
| 报表统计 | 1-6小时 | 使用延迟从库跑批,不影响主库 |
| 历史归档 | 24小时 | 保留昨日数据快照 |
| 审计合规 | 0(实时) | 按需配置 |
1.6 延迟复制管理命令
sql
-- 修改延迟时间
STOP REPLICA;
CHANGE REPLICATION SOURCE TO SOURCE_DELAY = 300; -- 改为5分钟
START REPLICA;
-- 取消延迟(恢复实时同步)
STOP REPLICA;
CHANGE REPLICATION SOURCE TO SOURCE_DELAY = 0;
START REPLICA;
-- 查看当前延迟状态
SHOW REPLICA STATUS\G | grep -E "SQL_Delay|SQL_Remaining_Delay"
-- 临时停止延迟从库的SQL线程(保留IO线程)
STOP REPLICA SQL_THREAD;
-- 恢复
START REPLICA SQL_THREAD;
1.7 延迟复制注意事项
⚠️ 重要提醒
- 延迟从库的
Seconds_Behind_Master会显示延迟值,不是故障- 延迟期间主库的 binlog 不会被清理(从库未读取)
- 长时间延迟可能导致 binlog 占用大量磁盘空间
- 建议配合
expire_logs_days设置合理的 binlog 保留时间
第二部分:慢查询日志
2.1 慢查询日志基础
慢查询日志记录执行时间超过阈值的 SQL 语句,是性能优化的核心工具。
2.2 查看慢查询配置
sql
-- 查看慢查询相关变量
mysql> SHOW VARIABLES LIKE 'slow%';
+---------------------+----------------------------------+
| Variable_name | Value |
+---------------------+----------------------------------+
| slow_launch_time | 2 |
| slow_query_log | OFF |
| slow_query_log_file | /data/mysql/mysql-node1-slow.log |
+---------------------+----------------------------------+
-- 查看慢查询阈值
mysql> SHOW VARIABLES LIKE 'long%';
+-----------------+-----------+
| Variable_name | Value |
+-----------------+-----------+
| long_query_time | 10.000000 |
+-----------------+-----------+
-- 查看是否记录未使用索引的查询
mysql> SHOW VARIABLES LIKE 'log_queries_not_using_indexes';
+-------------------------------+-------+
| Variable_name | Value |
+-------------------------------+-------+
| log_queries_not_using_indexes | OFF |
+-------------------------------+-------+
2.3 开启慢查询日志
sql
-- 开启慢查询日志
mysql> SET GLOBAL slow_query_log = ON;
-- 设置阈值(单位:秒)
mysql> SET GLOBAL long_query_time = 4;
-- 记录未使用索引的查询(建议开启)
mysql> SET GLOBAL log_queries_not_using_indexes = ON;
-- 记录全表扫描的查询
mysql> SET GLOBAL log_slow_admin_statements = ON;
-- 验证配置
mysql> SHOW VARIABLES LIKE 'slow%';
mysql> SHOW VARIABLES LIKE 'long%';
2.4 测试慢查询日志
sql
-- 执行短查询(低于阈值,不会记录)
mysql> SELECT SLEEP(3);
+----------+
| SLEEP(3) |
+----------+
| 0 |
+----------+
1 row in set (3.00 sec)
-- 执行慢查询(超过阈值,会被记录)
mysql> SELECT SLEEP(4);
+----------+
| SLEEP(4) |
+----------+
| 0 |
+----------+
1 row in set (4.00 sec)
2.5 查看慢查询日志
bash
# 查看慢查询日志内容
[root@mysql-node1 ~]# cat /data/mysql/mysql-node1-slow.log
日志输出示例:
text
/usr/local/mysql/bin/mysqld, Version: 8.3.0 (Source distribution). started with:
Tcp port: 3306 Unix socket: /data/mysql/mysql.sock
Time Id Command Argument
# Time: 2026-02-27T02:04:39.297189Z
# User@Host: root[root] @ localhost [] Id: 10
# Query_time: 4.000424 Lock_time: 0.000000 Rows_sent: 1 Rows_examined: 1
SET timestamp=1772157875;
SELECT SLEEP(4);
2.6 慢查询日志分析工具
mysqldumpslow(MySQL 自带)
bash
# 按查询时间排序,查看最慢的10条
mysqldumpslow -s t -t 10 /data/mysql/mysql-node1-slow.log
# 按平均查询时间排序
mysqldumpslow -s at -t 10 /data/mysql/mysql-node1-slow.log
# 按查询次数排序
mysqldumpslow -s c -t 10 /data/mysql/mysql-node1-slow.log
# 显示详细的查询语句
mysqldumpslow -a /data/mysql/mysql-node1-slow.log
pt-query-digest(Percona Toolkit)
bash
# 安装
yum install percona-toolkit -y
# 分析慢查询日志
pt-query-digest /data/mysql/mysql-node1-slow.log
# 输出到文件
pt-query-digest /data/mysql/mysql-node1-slow.log > slow_query_report.txt
# 分析多个日志文件
pt-query-digest /data/mysql/mysql-node1-slow.log /data/mysql/mysql-node2-slow.log
2.7 生产环境配置建议
sql
-- 永久生效配置(写入 my.cnf)
[mysqld]
# 慢查询日志
slow_query_log = ON
slow_query_log_file = /var/log/mysql/slow-query.log
long_query_time = 2 # 建议2秒
log_queries_not_using_indexes = ON # 记录未使用索引的查询
log_slow_admin_statements = ON # 记录慢管理语句
log_slow_slave_statements = ON # 记录从库慢查询
# 慢查询日志轮转
slow_query_log_rotate_size = 100M # 日志文件达到100M时轮转
2.8 慢查询日志轮转配置
bash
#!/bin/bash
# /etc/logrotate.d/mysql-slow
/data/mysql/mysql-slow.log {
daily
rotate 30
missingok
notifempty
compress
delaycompress
sharedscripts
postrotate
# 重新打开日志文件
mysql -e "SET GLOBAL slow_query_log = OFF; SET GLOBAL slow_query_log = ON;"
endscript
}
第三部分:主从架构高级技巧
3.1 跳过特定数据库的复制
sql
-- 在从库配置(my.cnf)
[mysqld]
replicate_ignore_db = mysql # 忽略 mysql 库
replicate_ignore_db = performance_schema
replicate_do_db = baibai # 只复制 baibai 库
-- 动态修改
CHANGE REPLICATION FILTER
REPLICATE_DO_DB = (baibai),
REPLICATE_IGNORE_DB = (mysql, performance_schema);
3.2 并行复制优化
sql
-- 查看当前并行复制配置
SHOW VARIABLES LIKE 'slave_parallel%';
-- 设置并行复制(MySQL 5.7+)
SET GLOBAL slave_parallel_workers = 4;
SET GLOBAL slave_parallel_type = LOGICAL_CLOCK;
-- 永久配置(my.cnf)
[mysqld]
slave_parallel_workers = 4
slave_parallel_type = LOGICAL_CLOCK
3.3 半同步复制
sql
-- Master 配置
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
SET GLOBAL rpl_semi_sync_master_enabled = 1;
-- Slave 配置
INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
SET GLOBAL rpl_semi_sync_slave_enabled = 1;
-- 查看状态
SHOW STATUS LIKE 'Rpl_semi_sync%';
3.4 GTID 复制模式
sql
-- 启用 GTID(my.cnf)
[mysqld]
gtid_mode = ON
enforce_gtid_consistency = ON
-- 使用 GTID 配置复制
CHANGE MASTER TO
MASTER_HOST='172.25.254.10',
MASTER_USER='swp',
MASTER_PASSWORD='swp',
MASTER_AUTO_POSITION = 1;
第四部分:监控与告警
4.1 复制延迟监控
sql
-- 实时监控延迟
SELECT
TIMEDIFF(NOW(), last_timestamp) AS delay
FROM performance_schema.replication_applier_status_by_worker;
-- 监控复制状态
SELECT
SERVICE_STATE AS io_thread,
LAST_ERROR_NUMBER,
LAST_ERROR_MESSAGE
FROM performance_schema.replication_connection_status;
4.2 告警脚本
bash
#!/bin/bash
# check_replication_delay.sh
THRESHOLD=60 # 延迟告警阈值(秒)
DELAY=$(mysql -uroot -proot -e "SHOW SLAVE STATUS\G" | grep "Seconds_Behind_Master:" | awk '{print $2}')
if [ "$DELAY" = "NULL" ] || [ "$DELAY" -gt "$THRESHOLD" ]; then
echo "WARNING: Replication delay is ${DELAY}s"
# 发送告警(邮件、钉钉、微信等)
# curl -X POST "https://your-webhook-url" -d "{\"msg\":\"MySQL复制延迟: ${DELAY}s\"}"
else
echo "OK: Replication delay is ${DELAY}s"
fi
第五部分:故障处理速查表
| 问题 | 现象 | 解决方案 |
|---|---|---|
| 复制中断 | Slave_SQL_Running: No |
STOP SLAVE; SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; START SLAVE; |
| IO线程停止 | Slave_IO_Running: No |
检查网络、权限、binlog是否存在 |
| 延迟过大 | Seconds_Behind_Master 持续增长 |
检查从库性能,考虑并行复制 |
| 主键冲突 | 1062错误 | 跳过或删除重复数据 |
| 数据不一致 | 主从数据不同 | 使用 pt-table-checksum 检查和修复 |
附录:快速命令参考
sql
-- 延迟复制
STOP SLAVE; CHANGE MASTER TO MASTER_DELAY=10; START SLAVE; SHOW SLAVE STATUS\G
-- 慢查询日志
SET GLOBAL slow_query_log=ON; SET GLOBAL long_query_time=2;
-- 查看复制状态
SHOW SLAVE STATUS\G | grep -E "Running|Delay|Behind"
-- 跳过复制错误
STOP SLAVE; SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; START SLAVE;
-- 查看 binlog 文件
SHOW BINARY LOGS;
SHOW MASTER STATUS;