简介:本资源是国家开放大学MySQL基础课程配套的数据库系统维护实验训练文档,面向初学数据库管理的学生及自学入门者,聚焦数据库日常运维核心技能训练。内容覆盖用户与权限管理(含Teacher/Student双角色实操)、mysqldump备份恢复、二进制日志启用、多方式数据导入导出(SELECT INTO、LOAD DATA、MySQL Workbench等),并针对性解决中文乱码问题(如CHARACTER SET gbk配置)。全文以汽车用品网上商城(Shopping数据库)为统一实验场景,8个递进式实验任务结构清晰,兼顾命令行与图形化工具操作对比。资源为单文件Word文档(.docx),共1个文件,大小3.59MB,格式规范、排版完整,可直接用于实验报告撰写与课堂复盘。目前已有1980人学习下载,适合课程作业提交、考前实训强化及DBA基础能力筑基。
1. 为什么“数据库系统维护”不是运维 checklist,而是 DBA 的日常黑匣子?
你打开一份叫《mysql实验训练4-数据库系统维护.docx》的文档,第一反应可能是:又是一堆SHOW PROCESSLIST、mysqldump命令和备份脚本的罗列?错。真正让线上 MySQL 不翻车的,从来不是“会不会执行命令”,而是在服务还在跑、用户还在下单、慢查询还没报警时,就预判出哪条日志在冒烟、哪个表正在 silently 膨胀、哪次自动清理可能把 binlog 删成空壳。这份实验训练的本质,是把 DBA 日常里靠经验、靠直觉、靠凌晨三点翻日志练出来的“系统性维护思维”,拆解成可观察、可度量、可回滚的 5 类动作:状态巡检(不是只看Uptime)、空间治理(不是只清ibdata1)、日志生命周期管理(不是只PURGE BINARY LOGS)、权限与配置收敛(不是只改my.cnf)、以及最关键的——故障前的黄金 15 分钟响应预案。它面向的是刚从 SQL 基础课毕业、正要接手真实业务库的准 DBA 或后端工程师:你需要的不是“怎么装 MySQL”,而是“当InnoDB_buffer_pool_pages_free掉到 300 以下时,该先查什么、再动什么、最后留什么证据”。接下来,我们就用一台干净的 CentOS 7 + MySQL 8.0.33 环境,把这份文档里藏得最深、但实战中踩坑最多的 5 个维护动作,一层层剥开。
2. 用mysqladmin+INFORMATION_SCHEMA搭建最小化状态巡检流水线
数据库没挂 ≠ 数据库健康。很多线上事故始于“一切正常”的监控面板——因为默认监控项漏掉了关键指标。本节不依赖任何第三方工具,只用 MySQL 自带能力,构建一个 3 分钟可跑通、5 分钟可集成进 crontab 的轻量巡检脚本。
2.1 为什么SHOW STATUS不够用?必须补上这 4 类动态视图
SHOW STATUS只返回累计值(如Threads_connected),无法反映瞬时压力;而INFORMATION_SCHEMA中的PROCESSLIST、TABLES、FILES、INNODB_METRICS才是实时脉搏。尤其注意:
INFORMATION_SCHEMA.PROCESSLIST:过滤Command='Sleep' AND Time > 60的长连接,它们是连接池泄漏的早期信号;INFORMATION_SCHEMA.TABLES:计算DATA_LENGTH + INDEX_LENGTH占总磁盘配额比例,避免单表撑爆分区;INFORMATION_SCHEMA.FILES:检查INNODB_DATA_FILE_PATH对应的 ibdata 文件是否被写满(FILE_SIZEvsMAX_FILE_SIZE);INFORMATION_SCHEMA.INNODB_METRICS:启用buffer_pool_hit_ratio后,命中率持续低于 95% 就需调 buffer pool size。
提示:MySQL 8.0 默认禁用
INNODB_METRICS,需先执行SET GLOBAL innodb_monitor_enable = 'buffer_pool_hit_ratio';,否则查不到数据。
2.2 用一条 bash + mysql 命令生成可读性巡检报告
#!/bin/bash # save as: mysql_health_check.sh MYSQL_CMD="mysql -u root -p'your_password' -Nse" echo "=== MySQL Health Check Report $(date) ===" echo # 1. 连接数水位 CONN_COUNT=$($MYSQL_CMD "SELECT COUNT(*) FROM INFORMATION_SCHEMA.PROCESSLIST;") MAX_CONN=$($MYSQL_CMD "SELECT @@max_connections;") echo "✅ 连接数: ${CONN_COUNT}/${MAX_CONN} (${CONN_COUNT*100/MAX_CONN}%)" # 2. 缓冲池命中率(需提前启用) HIT_RATIO=$($MYSQL_CMD "SELECT CAST(AVG(COUNTER_VALUE) AS DECIMAL(5,2)) FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME='buffer_pool_hit_ratio' AND STATUS='enabled';") echo "✅ 缓冲池命中率: ${HIT_RATIO}% (阈值 >95%)" # 3. 最大表大小(TOP 3) echo -e "\n⚠️ TOP 3 大表:" $MYSQL_CMD "SELECT CONCAT(TABLE_SCHEMA,'.',TABLE_NAME) AS table_name, ROUND((DATA_LENGTH+INDEX_LENGTH)/1024/1024,2) AS size_mb FROM INFORMATION_SCHEMA.TABLES ORDER BY size_mb DESC LIMIT 3;" # 4. 长 Sleep 连接 SLEEP_COUNT=$($MYSQL_CMD "SELECT COUNT(*) FROM INFORMATION_SCHEMA.PROCESSLIST WHERE Command='Sleep' AND Time > 60;") echo -e "\n🚨 长 Sleep 连接数: ${SLEEP_COUNT} (建议 <5)"逻辑说明:
-Nse参数去掉列名、表格边框、转义字符,确保输出纯文本可被 shell 解析;CAST(... AS DECIMAL(5,2))避免浮点精度丢失导致命中率显示为0.00;CONCAT(TABLE_SCHEMA,'.',TABLE_NAME)强制拼接库名+表名,防止同名表混淆;- 所有数值类结果都附带单位或百分比,避免人工换算错误。
参数说明:
your_password必须替换为实际 root 密码,生产环境建议改用.my.cnf配置文件存储凭证;Time > 60是经验值:应用层连接池 idle timeout 通常设为 30~60 秒,超过即异常;size_mb计算中/1024/1024是为转换字节为 MB,避免SELECT ... / 1048576这种易错写法。
3. 空间治理:从ibdata1膨胀到innodb_file_per_table=ON的迁移实操
ibdata1是 MySQL 里最让人又爱又恨的文件——它存着系统表空间、undo log、doublewrite buffer,但一旦开启就无法收缩。很多团队直到磁盘告警才想起这事,结果发现ALTER TABLE ... ENGINE=InnoDB重建表后ibdata1仍岿然不动。本节教你用零停机、可验证、可回退的方式完成空间治理。
3.1 先确认当前模式:innodb_file_per_table是否已生效?
-- 查看全局设置 SELECT @@innodb_file_per_table; -- 查看已有表是否独立表空间 SELECT TABLE_SCHEMA, TABLE_NAME, ENGINE, CREATE_OPTIONS FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA NOT IN ('mysql','information_schema','performance_schema','sys') AND CREATE_OPTIONS LIKE '%partitioned%';若@@innodb_file_per_table = 0,说明所有 InnoDB 表数据都挤在ibdata1里;若为1,则新表会单独生成.ibd文件,但旧表仍留在ibdata1—— 这正是需要迁移的场景。
3.2 安全迁移:对在线业务表执行ALTER TABLE ... ROW_FORMAT=COMPACT
-- 步骤 1:确认表无外键依赖(否则 ALTER 会失败) SELECT CONSTRAINT_SCHEMA, TABLE_NAME, CONSTRAINT_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE REFERENCED_TABLE_NAME = 'your_target_table'; -- 步骤 2:执行在线 DDL(MySQL 5.6+ 支持 ALGORITHM=INPLACE) ALTER TABLE your_db.your_table ENGINE=InnoDB, ROW_FORMAT=COMPACT, ALGORITHM=INPLACE, LOCK=NONE;逻辑说明:
ROW_FORMAT=COMPACT是 MySQL 5.6+ 默认格式,兼容性最好;DYNAMIC虽支持大字段,但某些旧客户端解析异常;ALGORITHM=INPLACE强制走原地修改,避免全表拷贝;LOCK=NONE表示不阻塞读写(前提是无全文索引、无虚拟列等限制);- 执行后,原
ibdata1中该表数据被标记为“可回收”,新数据写入独立.ibd文件,ibdata1不再增长。
参数说明:
your_db.your_table必须替换成真实库名和表名;- 若报错
ERROR 1846 (0A000): ALGORITHM=INPLACE is not supported...,说明表含不支持在线 DDL 的特性(如 FULLTEXT 索引),需降级为ALGORITHM=COPY, LOCK=SHARED(短暂只读锁); - 迁移后务必验证:
ls -lh /var/lib/mysql/your_db/your_table.ibd应存在且大小合理,SELECT COUNT(*) FROM your_table结果不变。
4. 日志生命周期管理:binlog 自动清理的 3 层安全阀设计
PURGE BINARY LOGS TO 'mysql-bin.000123'看似简单,但一次误操作就能让主从同步永久断裂。真正的日志治理不是“删旧日志”,而是建立“谁删、删多少、删之前留证”的闭环。我们用 MySQL 原生命令 + OS 层定时任务 + binlog 校验三重保险。
4.1 第一阀:MySQL 内置expire_logs_days的致命缺陷与绕过方案
MySQL 5.7+ 支持SET GLOBAL expire_logs_days = 7,但该参数存在两个硬伤:
- 不精确:实际清理时间是每天凌晨
mysqld启动时触发,若服务未重启,日志永不清理; - 无审计:删除动作不记录到 error log,无法追溯谁、何时、删了哪些文件。
因此,必须弃用expire_logs_days,改用PURGE BINARY LOGS BEFORE+ 时间戳控制:
-- 查看当前 binlog 列表及时间 SHOW BINARY LOGS; -- 计算 7 天前的时间戳(精确到秒) SELECT DATE_SUB(NOW(), INTERVAL 7 DAY); -- 执行精准清理(注意:BEFORE 后是 datetime,不是文件名!) PURGE BINARY LOGS BEFORE '2024-06-10 00:00:00';注意:
PURGE BINARY LOGS BEFORE删除的是早于该时间的所有 binlog,不是“保留最近 N 个文件”。务必先SHOW BINARY LOGS确认时间范围,再执行。
4.2 第二阀:OS 层 crontab + binlog 备份校验脚本
# /etc/cron.daily/purge-binlog-safe #!/bin/bash BINLOG_DIR="/var/lib/mysql" RETENTION_DAYS=7 DATE_CUTOFF=$(date -d "$RETENTION_DAYS days ago" +"%Y-%m-%d %H:%M:%S") # 步骤 1:备份即将被删的 binlog(仅文件名,不 cp 全量) cd $BINLOG_DIR ls -t mysql-bin.* | awk -v cutoff="$DATE_CUTOFF" ' BEGIN{ cmd="mysql -Nse \"SELECT UNIX_TIMESTAMP(\047"cutoff"\047)"; cmd | getline ts_cutoff; close(cmd) } { # 提取文件名中的时间戳(mysql-bin.000123 → 123) match($0, /[0-9]+$/); num = substr($0, RSTART, RLENGTH) # 估算该文件生成时间(假设每 1GB 生成 1 个文件,实际按业务调整) if (num < 100) ts_file = ts_cutoff - 86400 * 30 else ts_file = ts_cutoff - 86400 * 7 if (ts_file < ts_cutoff) print $0 }' > /tmp/binlog_to_purge_$(date +%F).log # 步骤 2:执行 PURGE(调用 mysql 命令) mysql -u root -p'your_pass' -e "PURGE BINARY LOGS BEFORE '$DATE_CUTOFF';" # 步骤 3:记录操作日志 echo "$(date): PURGED binlogs before $DATE_CUTOFF, see /tmp/binlog_to_purge_$(date +%F).log" >> /var/log/mysql/purge.log逻辑说明:
- 先用
ls -t按时间倒序列出 binlog,再通过awk结合时间戳估算文件生成时间,生成待删清单; PURGE命令后立即记录日志,包含具体时间点和清单文件路径,满足审计要求;- 不做
cp备份(太耗 IO),只记录文件名,真要恢复时再从备份服务器拉取。
参数说明:
RETENTION_DAYS=7可按业务 RPO 调整,金融类建议 14 天,内部系统可缩至 3 天;ts_file估算逻辑需根据实际 binlog 生成频率调整(如每小时切一个,则num差 1 ≈ 1 小时);your_pass同样建议改用配置文件,避免密码明文出现在 cron 中。
5. 权限与配置收敛:用mysqld --validate-config和mysqlpump实现配置漂移防控
开发提测环境和线上环境的max_connections=1000,上线后才发现线上是200,结果压测直接雪崩——这种“配置漂移”比代码 bug 更难定位。本节用 MySQL 5.7+ 原生能力,把配置管理从“人肉比对”升级为“机器校验”。
5.1 用mysqld --validate-config检测 my.cnf 语法与参数冲突
# 检查配置文件语法(不启动服务) mysqld --defaults-file=/etc/my.cnf --validate-config # 输出示例: # 2024-06-15T08:23:41.123456Z 0 [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. # 2024-06-15T08:23:41.123456Z 0 [ERROR] unknown variable 'innodb_log_file_size=512M'关键点:
--validate-config会加载my.cnf并检查所有参数是否合法、是否存在拼写错误、是否被废弃;- 错误级别为
[ERROR]的参数会导致 mysqld 启动失败,必须修复;[Warning]级别需评估是否影响业务(如explicit_defaults_for_timestamp在 5.7+ 默认关闭,但某些 ORM 依赖它); - 该命令不检查参数值合理性(如
innodb_buffer_pool_size=20G在 8G 内存机器上会 OOM),需配合mysqltuner.pl等工具二次校验。
5.2 用mysqlpump导出权限语句,实现权限版本化管理
# 导出所有用户权限(不含数据) mysqlpump --no-data --skip-triggers --skip-routines --skip-events \ --include-users --exclude-databases=mysql,information_schema,performance_schema,sys \ --user=root --password='your_pass' > /backup/privileges_$(date +%F).sql # 查看导出内容(确认是否含 GRANT 语句) head -20 /backup/privileges_$(date +%F).sql逻辑说明:
mysqlpump是 MySQL 5.7+ 官方推荐的逻辑备份工具,比mysqldump更快、更可控;--include-users会导出CREATE USER和GRANT语句,--exclude-databases排除系统库避免污染;- 导出文件可纳入 Git 版本管理,每次权限变更都 commit,回滚时
mysql < privileges_2024-06-10.sql即可。
参数说明:
--skip-triggers --skip-routines --skip-events确保只导权限,不导存储过程等对象;your_pass同样建议使用--defaults-extra-file指向安全配置文件;- 生产环境建议加
--single-transaction(虽对权限无效,但保持命令一致性)。
6. 故障前的黄金 15 分钟:用pt-deadlock-logger+ 自定义告警构建主动防御链
所有维护动作的终点,不是“系统没挂”,而是“在用户投诉前 15 分钟,我已经知道哪里要挂”。本节落地一个真实可用的死锁主动发现方案——不用等SHOW ENGINE INNODB STATUS,而是让死锁日志自动落盘、自动解析、自动通知。
6.1 部署pt-deadlock-logger并配置轮转策略
# 1. 安装 Percona Toolkit(CentOS) yum install -y http://www.percona.com/downloads/percona-release/redhat/0.1-4/percona-release-0.1-4.noarch.rpm yum install -y percona-toolkit # 2. 创建死锁日志目录并授权 mkdir -p /var/log/mysql/deadlocks chown mysql:mysql /var/log/mysql/deadlocks # 3. 启动守护进程(后台常驻) pt-deadlock-logger \ --daemonize \ --run-time=86400 \ --interval=30 \ --dest D=percona,t=deadlocks \ --user=root \ --password='your_pass' \ --socket=/var/lib/mysql/mysql.sock \ --log=/var/log/mysql/deadlocks/pt-deadlock.log逻辑说明:
--daemonize后台运行;--run-time=86400表示运行 24 小时后自动退出(配合 systemd 重启);--interval=30每 30 秒扫描一次INFORMATION_SCHEMA.INNODB_TRX和INNODB_LOCK_WAITS;--dest D=percona,t=deadlocks将死锁事件写入percona.deadlocks表(需提前建表,见下文);--log指定守护进程自身日志,用于排查 pt 工具异常。
建表语句(执行一次):
CREATE DATABASE IF NOT EXISTS percona; USE percona; CREATE TABLE IF NOT EXISTS deadlocks ( server_id VARCHAR(32) NOT NULL, ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP, thread_id BIGINT NOT NULL, txn_id BIGINT NOT NULL, txn_time INT NOT NULL, user VARCHAR(16) NOT NULL, hostname VARCHAR(64) NOT NULL, ip VARCHAR(16) NOT NULL, db VARCHAR(64) NOT NULL, tbl VARCHAR(64) NOT NULL, idx VARCHAR(64) NOT NULL, lock_type VARCHAR(16) NOT NULL, lock_mode VARCHAR(16) NOT NULL, lock_id VARCHAR(128) NOT NULL, lock_index VARCHAR(128) NOT NULL, lock_trx_id BIGINT NOT NULL, lock_trx_time INT NOT NULL, lock_trx_query TEXT NOT NULL, PRIMARY KEY (server_id, ts, thread_id) ) ENGINE=InnoDB;6.2 构建 15 分钟级告警:当死锁频次 >3 次/分钟时触发
#!/bin/bash # /usr/local/bin/check-deadlocks.sh THRESHOLD=3 WINDOW_MINUTES=1 NOW=$(date -d "now" "+%Y-%m-%d %H:%M:%S") MINUTES_AGO=$(date -d "$NOW - $WINDOW_MINUTES minutes" "+%Y-%m-%d %H:%M:%S") COUNT=$( mysql -Nse "SELECT COUNT(*) FROM percona.deadlocks WHERE ts BETWEEN '$MINUTES_AGO' AND '$NOW';" \ -u root -p'your_pass' ) if [ "$COUNT" -gt "$THRESHOLD" ]; then echo "$(date): CRITICAL - Deadlock count $COUNT in last $WINDOW_MINUTES min" | logger -t mysql-deadlock # 此处可接入企业微信/钉钉 webhook,或写入监控系统 echo "ALERT: $COUNT deadlocks detected. Check /var/log/mysql/deadlocks/pt-deadlock.log" | mail -s "MySQL Deadlock Alert" admin@company.com fi提示:将此脚本加入 crontab 每分钟执行一次:
* * * * * /usr/local/bin/check-deadlocks.sh
参数说明:
THRESHOLD=3是经验值:偶发死锁(1~2 次/分钟)属正常,持续 >3 次/分钟大概率是应用层事务设计缺陷;WINDOW_MINUTES=1确保告警粒度足够细,避免滞后;mail命令需提前配置本地 sendmail 或 msmtp,生产环境建议替换为 API 调用。
我干了 7 年 MySQL 维护,最深的教训是:所有“事后复盘”都源于“事前没看见”。这份实验训练文档里藏着的,不是一堆命令的排列组合,而是把“看不见的风险”变成“看得见的数字”的方法论——比如INFORMATION_SCHEMA.INNODB_METRICS里的buffer_pool_read_requests,它每分钟涨 5000 次,背后可能是某个没加索引的LIKE '%keyword%'查询正在拖垮缓冲池;比如pt-deadlock-logger记录的lock_trx_query,它反复出现UPDATE orders SET status=2 WHERE user_id=? AND status=1,说明业务层没处理好并发扣减。这些细节不会写在 docx 的标题里,但它们才是让数据库真正“活”下来的关键。希望帮到你。
本文还有配套的精品资源,点击获取