1. 为什么Shell脚本能提升MySQL运维效率
作为数据库管理员,每天都要面对大量重复性的MySQL操作:备份恢复、用户权限管理、性能监控、数据校验...这些工作如果每次都手动敲命令,不仅效率低下还容易出错。我在某次凌晨3点处理生产环境故障时,就因为手误打错了一个DROP语句差点酿成大祸。
Shell脚本的价值在于把零散操作封装成可复用的自动化流程。比如我们团队现在用脚本实现的例行工作:
- 每天凌晨自动全量备份+binlog增量备份
- 每小时检查一次慢查询并邮件报警
- 新项目上线时批量创建100+用户权限
- 跨机房数据一致性校验
这些原本需要人工值守的操作,现在全部通过crontab定时触发脚本完成。根据我们的统计,日常运维效率提升了2-3倍,人为失误率下降90%。更重要的是——终于不用再半夜爬起来处理备份了!
2. 基础环境准备
2.1 MySQL客户端工具选择
推荐使用官方的mysql命令行客户端,相比图形化工具更适合脚本化操作。通过-e参数可以直接执行SQL语句,配合-N -B参数可以获取纯净的制表符分隔数据:
# 安装MySQL客户端(以Ubuntu为例) sudo apt-get install mysql-client # 基础连接测试 mysql -h127.0.0.1 -uroot -p'your_password' -e "SHOW DATABASES;"安全提示:生产环境不要在命令行直接暴露密码,推荐使用
~/.my.cnf配置文件:[client] user=root password=your_secure_password host=127.0.0.1
2.2 Shell脚本编写规范
遵循这些规范可以避免90%的常见错误:
- 脚本开头必须加解释器和字符集声明:
#!/bin/bash set -euo pipefail export LANG=C.UTF-8 - 所有MySQL操作函数前添加连接检查:
check_mysql_connection() { if ! mysql -e "SELECT 1" &>/dev/null; then echo "MySQL连接失败" >&2 return 1 fi } - 使用变量存储敏感信息:
readonly MYSQL_USER="admin" readonly MYSQL_HOST="db-cluster.rds.aliyuncs.com"
3. 核心功能脚本实现
3.1 自动化备份方案
这是我们的生产级备份脚本框架:
#!/bin/bash # 全量备份+binlog增量备份脚本 readonly BACKUP_DIR="/data/backups/mysql" readonly BACKUP_PREFIX="fullbackup_$(date +%Y%m%d)" readonly LOG_FILE="/var/log/mysql_backup.log" # 创建当日备份目录 mkdir -p "${BACKUP_DIR}/${BACKUP_PREFIX}" # 全量备份(使用mysqldump) mysqldump --single-transaction --master-data=2 \ --all-databases > "${BACKUP_DIR}/${BACKUP_PREFIX}/full.sql" # 刷新binlog确保完整性 mysql -e "FLUSH BINARY LOGS" # 打包压缩备份文件 tar czf "${BACKUP_DIR}/${BACKUP_PREFIX}.tar.gz" -C "${BACKUP_DIR}" "${BACKUP_PREFIX}" # 清理7天前的旧备份 find "${BACKUP_DIR}" -name "fullbackup_*" -mtime +7 -exec rm -f {} \;关键参数说明:
--single-transaction:保证备份期间数据一致性--master-data=2:记录binlog位置点FLUSH BINARY LOGS:创建新的binlog文件
3.2 用户权限批量管理
新项目上线时经常需要批量创建用户:
#!/bin/bash # 批量创建MySQL账号(从CSV文件读取) # 格式:username,password,dbname,host,privileges readonly USER_FILE="new_users.csv" while IFS=, read -r user pass db host priv; do # 创建用户并授权 mysql <<EOF CREATE USER '${user}'@'${host}' IDENTIFIED BY '${pass}'; GRANT ${priv} ON ${db}.* TO '${user}'@'${host}'; FLUSH PRIVILEGES; EOF done < "${USER_FILE}"4. 高级运维技巧
4.1 实时监控慢查询
这个脚本会每5分钟检查一次慢查询并发送报警:
#!/bin/bash # 慢查询监控脚本 readonly THRESHOLD=2 # 超过2秒视为慢查询 readonly ALERT_EMAIL="dba-team@company.com" # 获取当前慢查询数量 slow_count=$(mysql -NBe "SELECT COUNT(*) FROM performance_schema.events_statements_summary_by_digest WHERE AVG_TIMER_WAIT > ${THRESHOLD}*1000000000") if (( slow_count > 0 )); then # 获取慢查询详情 mysql -e "SELECT DIGEST_TEXT, COUNT_STAR, AVG_TIMER_WAIT/1000000000 AS avg_sec FROM performance_schema.events_statements_summary_by_digest WHERE AVG_TIMER_WAIT > ${THRESHOLD}*1000000000 ORDER BY AVG_TIMER_WAIT DESC" | mail -s "MySQL慢查询报警" "${ALERT_EMAIL}" fi4.2 主从同步状态监控
这个脚本检查主从复制延迟和错误:
#!/bin/bash # 主从复制监控脚本 slave_status=$(mysql -e "SHOW SLAVE STATUS\G") # 检查IO线程和SQL线程状态 if ! grep -q "Slave_IO_Running: Yes" <<< "${slave_status}" || ! grep -q "Slave_SQL_Running: Yes" <<< "${slave_status}"; then echo "主从复制线程异常!" >&2 mysql -e "SHOW SLAVE STATUS\G" >&2 exit 1 fi # 检查复制延迟(超过60秒报警) delay=$(grep "Seconds_Behind_Master" <<< "${slave_status}" | awk '{print $2}') if (( delay > 60 )); then echo "主从复制延迟 ${delay} 秒" >&2 fi5. 生产环境避坑指南
5.1 连接池管理
在脚本中频繁创建MySQL连接会导致"Too many connections"错误。推荐两种解决方案:
- 使用连接池工具:
# 安装mysqlsh(MySQL Shell) sudo apt-get install mysql-shell # 使用连接池执行查询 mysqlsh --uri root@localhost --js -e "session.runSql('SELECT 1')"- 脚本内复用连接:
# 使用命名管道复用连接 exec 3<> <(mysql -NBe "SELECT 1") while read -u 3 line; do echo "结果: $line" done exec 3>&-5.2 密码安全最佳实践
绝对不要在脚本中硬编码密码!推荐三种安全方案:
- 使用MySQL配置文件的
[client]段 - 从环境变量读取:
export MYSQL_PWD=$(aws secretsmanager get-secret-value --secret-id prod/mysql) mysql -uroot -e "SELECT 1" - 使用交互式输入:
read -s -p "MySQL密码: " password mysql -uroot -p"${password}" -e "SELECT 1"
6. 性能优化脚本示例
6.1 索引碎片整理
这个脚本会找出碎片率超过30%的表并优化:
#!/bin/bash # 自动优化表碎片 readonly FRAG_THRESHOLD=30 # 碎片率阈值 mysql -NBe "SELECT CONCAT(table_schema,'.',table_name) FROM information_schema.tables WHERE data_free > 0" | while read table; do # 计算碎片率 frag_ratio=$(mysql -NBe "SELECT ROUND(data_free/(data_length+index_length)*100,2) FROM information_schema.tables WHERE CONCAT(table_schema,'.',table_name)='${table}'") if (( $(echo "${frag_ratio} > ${FRAG_THRESHOLD}" | bc -l) )); then echo "优化表 ${table} (碎片率 ${frag_ratio}%)" mysql -e "OPTIMIZE TABLE ${table}" fi done6.2 查询缓存预热
在业务高峰期前预热缓存:
#!/bin/bash # 缓存预热脚本 readonly WARMUP_FILE="/tmp/top_queries.sql" # 从慢查询日志提取高频SQL pt-query-digest --filter '$event->{arg} =~ m/^select/i' \ /var/log/mysql/mysql-slow.log | awk '/^# Query/ {getline; print}' > "${WARMUP_FILE}" # 并行执行预热 parallel -j 8 < "${WARMUP_FILE}"7. 错误处理与日志记录
7.1 完善的错误处理机制
这个模板包含了完整的错误处理和日志记录:
#!/bin/bash # 带错误处理的脚本模板 set -euo pipefail exec > >(tee -a "/var/log/mysql_script.log") 2>&1 trap 'handle_error $LINENO' ERR handle_error() { local line=$1 echo "[$(date)] 错误发生在第 ${line} 行" >&2 # 发送报警通知 mail -s "MySQL脚本执行失败" admin@example.com <<< "查看日志:/var/log/mysql_script.log" exit 1 } # 主逻辑 mysql -e "START TRANSACTION" # 业务操作... mysql -e "COMMIT" echo "[$(date)] 脚本执行成功"7.2 审计日志记录
记录所有敏感操作的审计日志:
#!/bin/bash # 带审计的MySQL操作脚本 readonly AUDIT_LOG="/var/log/mysql_audit.log" log_audit() { echo "[$(date)] [$(whoami)] $@" | tee -a "${AUDIT_LOG}" } # 示例:用户删除操作 delete_user() { local user=$1 log_audit "删除用户 ${user}" mysql -e "DROP USER '${user}'@'%'" }8. 脚本性能优化技巧
8.1 批量操作替代循环
错误示范(性能差):
for id in {1..1000}; do mysql -e "INSERT INTO test VALUES (${id})" done正确做法(性能提升100倍):
# 生成批量SQL values=$(printf "(%d)," {1..999}; echo "(1000)") mysql -e "INSERT INTO test VALUES ${values}"8.2 并行处理技巧
使用GNU parallel加速大批量操作:
# 并行导出所有表数据 mysql -NBe "SHOW TABLES" | parallel -j 8 "mysqldump db {} > {}.sql" # 并行执行SQL文件 find /sql_scripts -name "*.sql" | parallel -j 4 "mysql db < {}"9. 扩展应用场景
9.1 自动化Schema变更
使用脚本管理DDL变更:
#!/bin/bash # 自动化Schema迁移 readonly SCHEMA_VERSION="20230815" readonly LOCK_FILE="/tmp/schema_migrate.lock" # 获取锁防止并发执行 exec 9>"${LOCK_FILE}" flock -n 9 || exit 1 # 检查当前版本 current_ver=$(mysql -NBe "SELECT version FROM schema_version") if [[ "${current_ver}" != "${SCHEMA_VERSION}" ]]; then echo "开始执行Schema变更..." mysql < "/opt/schema/${SCHEMA_VERSION}.sql" mysql -e "UPDATE schema_version SET version='${SCHEMA_VERSION}'" fi9.2 数据校验脚本
跨实例数据一致性校验:
#!/bin/bash # 数据一致性校验 readonly SOURCE_DSN="user:pass@source-host:3306" readonly TARGET_DSN="user:pass@target-host:3306" # 使用pt-table-checksum进行校验 pt-table-checksum --replicate=test.checksums \ --no-check-binlog-format \ --databases=production \ h=${SOURCE_DSN} # 获取不一致记录 pt-table-sync --replicate=test.checksums \ h=${SOURCE_DSN} h=${TARGET_DSN} \ --print | while read sql; do mysql -h${TARGET_DSN} -e "${sql}" done10. 持续集成实践
10.1 自动化测试框架
集成到CI/CD管道的测试脚本:
#!/bin/bash # MySQL测试套件 set -e # 启动测试容器 docker run -d --name mysql_test \ -e MYSQL_ROOT_PASSWORD=test \ -p 3306:3306 mysql:5.7 # 等待MySQL启动 while ! mysqladmin ping -h127.0.0.1 -uroot -ptest --silent; do sleep 1 done # 执行测试 mysql -h127.0.0.1 -uroot -ptest < setup_test_db.sql bats mysql_tests.bats # 清理 docker stop mysql_test docker rm mysql_test10.2 配置漂移检测
定期检查生产环境配置是否符合规范:
#!/bin/bash # 配置合规性检查 readonly STANDARD_FILE="/opt/mysql_standard.cnf" # 导出当前配置 mysql --print-defaults > current.cnf # 使用diff比较差异 if ! diff -u "${STANDARD_FILE}" current.cnf; then echo "发现配置漂移!" >&2 exit 1 fi11. 实际案例分享
去年我们通过脚本自动化解决了两个典型问题:
案例一:批量处理用户权限
- 背景:公司并购需要为200+新员工配置数据库权限
- 传统方式:DBA手动操作,预计8小时,错误率约5%
- 脚本方案:编写用户批量导入脚本,30分钟完成,零错误
- 关键脚本:
# 从HR系统API获取用户列表 curl -s https://hr-api/users | jq -r '.[] | [.email, .dept] | @csv' > users.csv # 批量创建权限 while IFS=, read -r email dept; do mysql <<EOF CREATE USER '${email}'@'%' IDENTIFIED BY '${RANDOM}'; GRANT SELECT ON ${dept}_db.* TO '${email}'@'%'; EOF done < users.csv
案例二:跨机房数据同步
- 背景:需要验证主从机房的数据一致性
- 传统方式:导出比对,耗时2小时,只能抽样检查
- 脚本方案:编写checksum校验脚本,15分钟完成全量比对
- 关键代码:
# 主库生成校验值 pt-table-checksum --replicate=percona.checksums \ --no-check-binlog-format \ --databases=production \ h=master-host,u=admin,p=xxx # 从库检查差异 pt-table-sync --replicate=percona.checksums \ h=master-host,u=admin,p=xxx h=slave-host,u=admin,p=xxx \ --print | tee /tmp/repair.sql # 自动修复差异 mysql -hslave-host -uadmin -pxxx < /tmp/repair.sql
12. 脚本维护建议
版本控制:所有脚本必须纳入Git仓库,建议结构:
/mysql-scripts ├── backups │ ├── full_backup.sh │ └── binlog_backup.sh ├── user_mgmt │ ├── create_user.sh │ └── audit_users.sh └── lib ├── mysql_utils.sh └── logging.sh文档规范:每个脚本头部包含标准注释:
#!/bin/bash # 名称:mysql_backup.sh # 功能:MySQL全量+增量备份 # 作者:DBA Team # 参数:无 # 输出:/data/backups/mysql/ # 依赖:mysqldump, mysql-client # 日志:/var/log/mysql_backup.log定期评审:每季度检查脚本:
- 是否还有在使用
- 密码策略是否需要更新
- 是否有性能优化空间
- 错误处理是否完善
13. 性能对比测试
我们对常见操作做了脚本化和手动操作的效率对比(测试环境:AWS RDS MySQL 5.7,10万行数据):
| 操作类型 | 手动操作耗时 | 脚本执行耗时 | 效率提升 |
|---|---|---|---|
| 创建100个用户权限 | 45分钟 | 28秒 | 96倍 |
| 全库备份 | 32分钟 | 25分钟 | 1.3倍 |
| 跨库数据迁移 | 6小时 | 47分钟 | 7.6倍 |
| 慢查询分析 | 手动无法实现 | 3分钟 | ∞ |
关键发现:
- 越是重复性操作,脚本优势越明显
- 某些复杂分析手动根本无法完成
- 备份类操作受限于I/O,提升有限但更可靠
14. 安全加固方案
14.1 最小权限原则
每个脚本使用专用MySQL账号:
# 监控脚本用只读账号 readonly MONITOR_USER="monitor_ro" readonly MONITOR_PASS=$(vault read -field=password mysql/creds/monitor) mysql -u"${MONITOR_USER}" -p"${MONITOR_PASS}" -e "SHOW STATUS"14.2 SQL注入防护
使用参数化查询避免注入:
# 危险!可能被注入 mysql -e "SELECT * FROM users WHERE id=${input}" # 安全做法 mysql -e "SET @id=${input}; SELECT * FROM users WHERE id=@id"14.3 敏感信息过滤
在日志中自动脱敏:
log_safe() { local msg=$1 # 过滤密码等敏感信息 echo "${msg}" | sed -E 's/(password|pwd)=[^&]*/\1=***/g' }15. 未来改进方向
- Ansible集成:将常用脚本改造成Ansible role,实现更规范的自动化管理
- Prometheus监控:输出脚本执行的metrics指标
- Web化管理:开发简单界面供非运维人员安全使用常用功能
- AI辅助优化:利用GPT-4自动生成复杂查询的优化建议
我们团队正在开发一个开源工具mysql-script-kit,集合了文中提到的各种实用脚本,预计下个季度发布第一个版本。