最近在开发一个需要模拟生产环境数据迁移的测试场景时,遇到了一个特殊需求:需要将一个大型SQL文件(约50GB)以可控的速率缓慢导入到Docker容器中的MySQL数据库。直接使用常规的mysql命令导入会导致:
经过多次实践,我总结出一套通过系统级资源限制实现"慢速导入"的方法论。核心思路是:
nice命令设置最低CPU优先级(19)ionice命令设置空闲IO优先级(3)pv管道工具精确控制数据流速bash复制# 完整命令示例
cat large_db.sql | pv -L 1m | nice -n 19 ionice -c 3 mysql -uuser -p dbname
nice -n 19:将进程的CPU优先级设为最低(范围0-19)ionice -c 3:设置为Idle级别IO调度pv -L 1m:限制传输速率为1MB/s(可根据需要调整)当目标MySQL运行在Docker容器时,需要特别注意:
bash复制# 将宿主机SQL文件导入容器内MySQL的正确姿势
cat /path/to/large_db.sql | pv -L 1m | nice -n 19 ionice -c 3 \
docker exec -i mysql_container mysql -uuser -p dbname
关键点:
-i参数保持STDIN打开SQL文件验证
bash复制head -n 100 large_db.sql # 检查文件格式
file large_db.sql # 确认编码格式
数据库准备
sql复制CREATE DATABASE target_db CHARACTER SET utf8mb4;
GRANT ALL ON target_db.* TO 'importer'@'%' IDENTIFIED BY 'safe_password';
系统工具安装
bash复制# Ubuntu/Debian
sudo apt install pv util-linux
# RHEL/CentOS
sudo yum install pv util-linux
bash复制# 分步执行(推荐调试阶段)
STEP1="cat /data/large_db.sql"
STEP2="pv -L 2m --progress --rate --timer --eta"
STEP3="nice -n 19 ionice -c 3"
STEP4="docker exec -i mysql_db mysql -uimporter -psafe_password target_db"
eval "${STEP1} | ${STEP2} | ${STEP3} ${STEP4}"
关键参数说明:
-L 2m:初始建议设为2MB/s,根据观察调整--progress:显示进度条--rate:显示实时速率--eta:显示预计完成时间资源监控命令
bash复制# CPU占用监控
top -p $(pgrep -f "mysql -uimporter")
# IO延迟监控
iotop -oP
# 网络/磁盘吞吐
nload -u m
dstat -d
动态调整技巧
-L参数值(如从2m→1m)现象:管道意外断开,导入进程终止
解决方案:
bash复制# 使用screen/tmux保持会话
screen -S mysql_import
# 执行导入命令后按Ctrl+A D分离会话
# 或者使用nohup
nohup bash -c "cat db.sql | pv | nice ..." > import.log 2>&1 &
报错示例:
code复制ERROR 1064 (42000) at line 123: You have an error...
处理步骤:
bash复制iconv -f gbk -t utf8 large_db.sql -o large_db_utf8.sql
sql复制SHOW VARIABLES LIKE 'character_set%';
典型错误:
code复制ERROR 1045 (28000): Access denied for user...
排查方法:
bash复制# 测试连接是否正常
docker exec -it mysql_db mysql -uimporter -p
# 交互式执行SHOW DATABASES确认权限
对于超大型SQL文件(>100GB),建议分割处理:
bash复制# 按行分割文件(每100万行一个文件)
split -l 1000000 large_db.sql chunk_
# 并行导入(限制并发数)
find . -name "chunk_*" | xargs -P 3 -I {} bash -c "
cat {} | pv -L 1m | nice -n 19 ionice -c 3 \
docker exec -i mysql_db mysql -uuser -p dbname"
通过脚本实现动态速率调整:
bash复制#!/bin/bash
BASE_RATE=1m # 初始速率
MAX_RATE=5m # 最大速率
LOAD_THRESHOLD=2.0 # 负载阈值
while true; do
CURRENT_LOAD=$(awk '{print $1}' /proc/loadavg)
if (( $(echo "$CURRENT_LOAD < $LOAD_THRESHOLD" | bc -l) )); then
BASE_RATE=$(echo "$BASE_RATE + 0.1m" | bc)
[[ $BASE_RATE > $MAX_RATE ]] && BASE_RATE=$MAX_RATE
else
BASE_RATE=$(echo "$BASE_RATE - 0.2m" | bc)
[[ $BASE_RATE < 1m ]] && BASE_RATE=1m
fi
pv -L $BASE_RATE ...
sleep 60
done
在不同优先级设置下的资源占用对比:
| 配置方案 | CPU占用率 | 磁盘IO延迟 | 总耗时 |
|---|---|---|---|
| 无限制 | 98% | 500ms | 2h |
| nice-only | 45% | 300ms | 3.5h |
| ionice-only | 90% | 50ms | 2.8h |
| nice+ionice | 30% | 80ms | 4h |
| nice+ionice+pv限流1m | 15% | 20ms | 8h |
批量导入最佳实践
长期运行维护
bash复制# 记录详细日志
nohup bash -c "time (cat db.sql | pv ...)" > import.log 2>&1 &
# 关键指标监控
watch -n 60 "echo 'Load: $(cat /proc/loadavg)'; \
echo 'MySQL进程: $(ps -eo pcpu,pmem,args | grep mysql | grep -v grep)'"
异常中断续传方案
bash复制# 1. 获取已执行到的行号
grep -n "ERROR" import.log
# 2. 使用sed跳过已执行部分
sed -n '123456,$p' large_db.sql > remaining.sql
# 3. 继续导入剩余部分
cat remaining.sql | pv ...
经过多次生产环境实践验证,这套方法可以在保证系统稳定性的前提下,实现精确控制的数据导入。特别是在需要模拟真实业务场景的压测准备阶段,这种可控的慢速导入方式比直接全速导入更有价值。