MySQL 不停机加从库(重复入口)
01 前置理论与核心机制¶
1. InnoDB 的 MVCC 快照读特性¶
-
无锁导出:在 MySQL 运行期间直接全量导出数据,最大的风险是锁表导致业务写入停滞。只要业务表全部采用 InnoDB 引擎,利用其多版本并发控制(MVCC)特性,即可在导出时开启一个独立事务。通过一致性快照读(Consistent Nonlocking Read)获取数据,整个导出过程不会阻塞线上的 INSERT / UPDATE 等写入操作。
-
参数依托:在执行 mysqldump 时,该机制通过 --single-transaction 参数触发生效。
2. 增量接力坐标 (Binlog Position)¶
-
精准定位:主库的数据处于持续变更中,从库仅获取存量快照数据是不够的,还必须明确这份快照对应主库日志的具体时间点。
-
参数依托:利用 --master-data=2 参数,mysqldump 会在建立一致性快照的瞬间,获取当前主库的 Binlog 文件名和偏移量(Position),并将其以注释形式写入导出的 SQL 文件头部。从库导入全量数据后,直接基于该坐标继续向后订阅增量日志即可实现无缝衔接。
02 故障排查与恢复闭环 SOP¶
当线上业务持续写入新数据,且不允许停机维护时,按以下四步标准链路完成从库在线接入:
步骤一:主库创建同步专用账号¶
在主库创建专门用于主从同步的账号,并授予 REPLICATION SLAVE 权限。该操作不会影响主库正常处理业务。
步骤二:导出一致性数据快照¶
在主库宿主机使用 mysqldump 导出全量数据,必须同时指定事务快照与坐标记录参数:
# 在主库宿主机执行全量备份 (-A 代表所有数据库)
mysqldump -uroot -pqaz123 --single-transaction --master-data=2 -A > all_data.sql
步骤三:提取同步起点坐标¶
数据导出完成后,解析 SQL 文件头部,提取被注释的 CHANGE MASTER TO 语句中的文件名与偏移量:
- 观测点:终端会输出类似 -- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000002', MASTER_LOG_POS=1543; 的记录,保存这两个核心参数值。
步骤四:从库数据导入与开启同步¶
将快照文件传输至从库服务器,先导入存量数据,再配置增量同步链路:
# 1. 将备份文件传输至从库 (192.168.0.89)
scp all_data.sql [email protected]:/root/
# 2. 登录从库宿主机,导入存量数据快照
mysql -uroot -pqaz123 进入从库的 MySQL 命令行,填入同步账号、密码以及步骤三提取的坐标,启动复制链路并验证:
```sql
CHANGE MASTER
MASTER_HOST
MASTER_USER
MASTER_PASSWORD
MASTER_LOG_FILE
MASTER_LOG_POS
SLAVE
SLAVE \G
- 验证点:确认 Slave_IO_Running 和 Slave_SQL_Running 均为 Yes。这表明从库已正常连接主库,并开始自动拉取和回放导出期间产生的增量数据,实现线上业务的零停机从库扩容。
03 真实生产故障现象与案例库¶
案例 1:混用 MyISAM 引擎导致线上锁表阻塞¶
-
故障现象:DBA 使用 --single-transaction 参数执行导出时,线上业务突然出现大面积写入超时与锁等待。
-
排查与处置:排查发现,该实例中存在数张历史遗留的 MyISAM 引擎表。--single-transaction 仅对支持事务与 MVCC 的 InnoDB 引擎有效;当遇到 MyISAM 表或加上了全局锁参数时,为保障数据一致性,会触发全表读锁,导致线上写请求被阻塞。处置方案为立即终止导出进程,在业务低峰期将遗留表引擎统一转换为 InnoDB 后再执行在线备份。
案例 2:漏加 master-data 导致主键冲突同步中断¶
-
故障现象:全量数据导入从库并执行 START SLAVE 后,SQL 线程立即报错 Duplicate entry ... for key 'PRIMARY' 并停止同步。
-
根因与处置:导出时遗漏了 --master-data=2 参数,操作人员仅通过手动执行 SHOW MASTER STATUS 获取了存在时间差的坐标位置。由于该时间差内的数据已被包含在全量 SQL 中,从库回放 Binlog 时重复插入已存在的记录,从而触发主键冲突。处置方案为重新携带完整参数执行导出与导入。
案例 3:百 GB 级大库使用逻辑导出耗时过长¶
-
故障现象:生产数据库体积达 200GB,使用 mysqldump 导出与从库导入累计耗时超过 4 小时,且长事务导出期间导致主库 Undo Log 持续膨胀。
-
根因与处置:mysqldump 本质上是将底层数据转化为 SQL 语句的逻辑备份,在数据量超过 50G~100G 时效率较低。处置方案为改用 Percona XtraBackup 进行物理热备(直接拷贝物理数据页并配合 Redo Log 恢复),或在 MySQL 8.0.17+ 环境下直接使用原生的 Clone Plugin(克隆插件),大幅缩短扩容耗时。
04 生产排障速查表(不停机加从库)¶
05 面试应答参考话术¶
面试官问:“线上的 MySQL 数据库正在运行,不断有新数据写入,现在需要在线新增一个从库,怎么做到不影响线上的正常读写?”
可以这样回答:只要业务表全部使用 InnoDB 引擎,就可以利用其 MVCC 快照读特性实现零停机的从库搭建。标准流程分为四步:第一步:创建复制账号。
在主库执行 GRANT REPLICATION SLAVE 创建一个专用于主从同步的账号,该操作轻量且不影响业务。第二步:导出一致性快照(核心步骤)。
使用 mysqldump 导出一份全量数据快照。执行时必须加上两个关键参数:一是 --single-transaction,它会开启一个独立事务,利用 InnoDB 的 MVCC 机制通过快照读获取数据,全程不会锁表,不阻塞主库的任何写入操作;二是 --master-data=2,它会在建立一致性快照的瞬间,将当前主库准确的 Binlog 文件名和位置偏移量(Position)以注释形式写入 SQL 文件头部。第三步:提取同步坐标。
全量 SQL 导出完成后,使用 head -n 30 查看文件头部被注释的 CHANGE MASTER TO 语句,提取里面记录的日志文件名和 Position 坐标。第四步:从库导入与挂载。
将 SQL 文件传输至新从库并导入存量数据。随后在从库执行 CHANGE MASTER TO,填入复制账号密码以及提取到的精确坐标,最后执行 START SLAVE 启动同步。通过 SHOW SLAVE STATUS 确认 Slave_IO_Running 和 Slave_SQL_Running 均为 Yes,从库就会从快照时间点开始自动追平增量数据。此外,这套基于 mysqldump 的逻辑备份方案主要适用于百 GB 以内的数据量。如果生产库达到数百 GB 甚至 TB 级别,逻辑导入导出耗时过长,我会改用 Percona XtraBackup 进行物理热备,或者在 MySQL 8.0 环境下直接使用原生的 Clone 插件来完成在线扩容。
06 面试高频追问与应答预案¶
追问 1:如果主库开启了 GTID 模式,还需要去文件头找 File 和 Position 坐标吗?¶
-
回答要点:
-
不需要。开启 GTID(全局事务标识符)后,主从复制基于全局唯一的事务 ID 集合进行对齐,不再依赖物理文件名和偏移量。
-
在 GTID 模式下,mysqldump 导出时会自动将当前已执行的 GTID 集合(SET @@GLOBAL.gtid_purged)写入 SQL 文件中。从库导入数据后,只需执行 CHANGE MASTER TO ... MASTER_AUTO_POSITION = 1; 即可,从库会自动比对缺失的事务集并向主库拉取增量数据。
追问 2:为什么导出时不使用 mysqldump 的 --lock-all-tables 参数来保证数据一致性?¶
-
回答要点:
-
因为生产环境要求不能阻塞线上读写。--lock-all-tables 会在整个导出期间对所有表施加全局读锁,导致主库所有的 INSERT、UPDATE、DELETE 操作全部被阻塞挂起,等同于业务停服。
-
仅当数据库中存在不支持事务的 MyISAM 引擎表,且必须保证跨表强一致性时,才会被迫在停机窗口内使用锁表参数。
07 主从同步健康度自动巡检脚本(check_mysql_repl.sh)¶
该脚本用于在从库节点上自动检测主从复制链路的运行状态,核验 IO/SQL 线程状态及主从复制延迟。
#!/usr/bin/env bash
# ==============================================================================
# 脚本名称: check_mysql_repl.sh
# 功能描述: MySQL 主从同步状态与复制延迟自动化健康诊断 (纯只读)
# 使用方式: 在从库宿主机上执行 (建议通过配置文件管理凭证)
# ==============================================================================
set -o pipefail
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
PLAIN='\033[0m'
# 配置数据库连接信息 (生产环境建议通过 ~/.my.cnf 免密连接)
DB_USER="root"
DB_PASS="qaz123"
MYSQL_CMD="mysql -u${DB_USER} -p${DB_PASS} -e"
echo -e "${BLUE}================================================================${PLAIN}"
echo -e "${BLUE} MySQL 主从复制链路状态自动化巡检 ${PLAIN}"
echo -e "${BLUE}================================================================${PLAIN}"
# 1. 获取 Slave 完整状态
SLAVE_STATUS=$($MYSQL_CMD "SHOW SLAVE STATUS \G" 2>/dev/null)
if [ -z "$SLAVE_STATUS" ]; then
echo -e "${RED}[FATAL] 未获取到同步状态。请确认当前节点已配置主从复制且数据库凭证正确。${PLAIN}"
exit 1
fi
# 2. 提取核心双 Yes 状态
IO_RUNNING=$(echo "$SLAVE_STATUS" | grep -w "Slave_IO_Running:" | awk '{print $2}')
SQL_RUNNING=$(echo "$SLAVE_STATUS" | grep -w "Slave_SQL_Running:" | awk '{print $2}')
LAST_ERROR=$(echo "$SLAVE_STATUS" | grep -w "Last_SQL_Error:" | sed -e 's/^[ \t]*Last_SQL_Error: //')
echo -e "\n${YELLOW}[1/2] 检查同步线程状态 (IO/SQL Thread)...${PLAIN}"
if [ "$IO_RUNNING" == "Yes" ] && [ "$SQL_RUNNING" == "Yes" ]; then
echo -e " -> IO Thread 状态: ${GREEN}${IO_RUNNING}${PLAIN} (负责拉取主库 Binlog)"
echo -e " -> SQL Thread 状态: ${GREEN}${SQL_RUNNING}${PLAIN} (负责回放 Relay Log)"
echo -e " -> ${GREEN}[OK] 核心复制链路双 Yes 状态正常。${PLAIN}"
else
echo -e " -> IO Thread 状态: ${RED}${IO_RUNNING}${PLAIN}"
echo -e " -> SQL Thread 状态: ${RED}${SQL_RUNNING}${PLAIN}"
echo -e " -> ${RED}[DANGER] 主从复制链路异常中断!${PLAIN}"
if [ -n "$LAST_ERROR" ]; then
echo -e "\n -> ${YELLOW}最近的 SQL 回放错误详情:${PLAIN}"
echo -e " ${RED}${LAST_ERROR}${PLAIN}"
echo -e " (提示: 若为主键冲突或记录不存在,需核实数据一致性或重新构建同步坐标)"
fi
exit 1
fi
# 3. 检查复制延迟进度
echo -e "\n${YELLOW}[2/2] 检查主从同步延迟指标 (Seconds_Behind_Master)...${PLAIN}"
DELAY_SECONDS=$(echo "$SLAVE_STATUS" | grep -w "Seconds_Behind_Master:" | awk '{print $2}')
if [ "$DELAY_SECONDS" == "NULL" ]; then
echo -e " -> 延迟状态: ${RED}NULL${PLAIN}"
echo -e " -> ${RED}[WARN] 无法计算延迟,通常因同步线程未运行所致。${PLAIN}"
elif [ "$DELAY_SECONDS" -eq 0 ]; then
echo -e " -> 当前延迟: ${GREEN}0 秒${PLAIN}"
echo -e " -> ${GREEN}[OK] 存量与增量数据已追平主库,处于实时同步状态。${PLAIN}"
elif [ "$DELAY_SECONDS" -lt 60 ]; then
echo -e " -> 当前延迟: ${YELLOW}${DELAY_SECONDS} 秒${PLAIN}"
echo -e " -> ${YELLOW}[INFO] 存在轻微延迟。若刚完成全量导入,属于增量日志正常追赶过程。${PLAIN}"
else
echo -e " -> 当前延迟: ${RED}${DELAY_SECONDS} 秒${PLAIN}"
echo -e " -> ${RED}[ALERT] 主从延迟较大!请排查从库磁盘 I/O 负载、主库大事务,或评估开启多线程并行复制 (slave_parallel_workers)。${PLAIN}"
fi
echo -e "\n${BLUE}================================================================${PLAIN}"