
你的数据库跑了半年。一开始很快,现在越来越慢。你没动过它,没改过配置,没加过数据。为什么会慢?因为数据库不是“装好就不用管”。它会积累碎片,会留下没用过的索引,会写越来越多的日志,会存着你忘了删的历史数据。不维护,它就会慢慢变慢,直到你受不了。
今天讲6件定期要做的事。每月花半小时,让数据库一直保持健康。
先看一个数据
某DBA团队统计,超过70%的数据库性能下降,不是硬件问题,是缺乏日常维护。碎片堆积、索引失效、日志膨胀。这些问题不修,硬件升级了也没用。每月半小时,能避免90%的“数据库莫名其妙慢”。
第一件:查看表大小,找到增长过快的表
不是所有表都需要维护。先找出谁在“变胖”。
sql
SELECT
table_schema AS '数据库',
table_name AS '表名',
round(((data_length + index_length) / 1024 / 1024), 2) AS '大小(MB)'
FROM information_schema.tables
ORDER BY (data_length + index_length) DESC
LIMIT 10;
输出最大的是日志表、流水表。这些表通常会无限增长,需要定期清理或归档。如果某张表一个月涨了20%,要关注。可能代码有bug写入了不该写的数据,或者业务增长超预期,该考虑分表了。
做多久:1分钟。每月看一次。
第二件:清理binlog(二进制日志)
binlog记录所有数据变更,用于主从复制和恢复。不清理会一直占用磁盘,直到写满。很多数据库磁盘爆满,元凶是binlog。
查看binlog占用:
sql
SHOW BINARY LOGS;
设置自动清理:
sql
-- 保留最近7天的binlog SET GLOBAL expire_logs_days = 7;
在配置文件/etc/my.cnf里永久设置:
ini
expire_logs_days = 7 max_binlog_size = 100M
重启MySQL后永久生效。如果是主库,不要设太短,确认从库已经跟上了再删。一般保留3-7天。
做多久:2分钟。每月检查一次设置。
第三件:优化表(清理碎片)
频繁增删改的表,会产生碎片。文件变大了,但实际数据没那么多。碎片让查询变慢,因为要扫描更多无效空间。
sql
OPTIMIZE TABLE orders;
InnoDB表执行时会重建表,释放碎片空间,期间会锁表。在业务低峰期(凌晨)执行。查看哪些表碎片多:
sql
SELECT
table_name,
round(data_free / 1024 / 1024, 2) AS '碎片(MB)'
FROM information_schema.tables
WHERE table_schema = 'your_database'
ORDER BY data_free DESC;
data_free是碎片大小。大于100MB的表值得优化。不要对所有表执行OPTIMIZE,只挑碎片大的。
做多久:大表可能几分钟到几十分钟。每月或每季度一次,低峰期做。
第四件:检查慢查询日志
慢查询日志记录执行超过阈值的SQL。不主动看,可能你的数据库每天有几千条慢查询,你完全不知道。
开启慢查询日志(永久配置):
ini
slow_query_log = 1 slow_query_log_file = /var/log/mysql/slow.log long_query_time = 2 log_queries_not_using_indexes = 1
分析慢查询:
bash
# 用mysqldumpslow mysqldumpslow -s t -t 10 /var/log/mysql/slow.log # 或用pt-query-digest(推荐) pt-query-digest /var/log/mysql/slow.log
看最耗时的SQL,加索引或改代码。
做多久:每月分析一次,10分钟。
第五件:检查索引使用情况
加了索引,未必在用。没用到的索引浪费磁盘空间,还拖慢写入。
查看未使用的索引:
sql
SELECT
t.table_schema,
t.table_name,
index_name
FROM information_schema.statistics s
JOIN information_schema.tables t ON s.table_schema = t.table_schema AND s.table_name = t.table_name
WHERE s.table_schema NOT IN ('mysql', 'information_schema', 'performance_schema')
AND t.table_type = 'BASE TABLE'
AND index_name NOT IN (
SELECT DISTINCT index_name FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE index_name IS NOT NULL
);
输出是完全没有被查询使用过的索引。确认无用后删除:
sql
DROP INDEX unused_index_name ON table_name;
注意:刚建不久的索引可能还没有统计信息,等一段时间再看。
做多久:每季度一次。
第六件:验证备份可恢复
你有备份脚本,每天跑。但备份文件是好的吗?能恢复吗?半年没测过。真出事时可能恢复不了。
每月做一次恢复测试:拿一个最近的备份文件,恢复到测试环境,启动数据库,跑几条查询验证数据正确性。
自动验证脚本思路:
- 从备份目录取最新的备份文件
- 恢复到临时数据库(Docker一键起)
- 执行
SELECT COUNT(*) FROM core_tables,对比前几天记录 - 数据量差异过大则告警
做多久:每月一次,15分钟。
维护频率建议
| 任务 | 频率 | 耗时 |
|---|---|---|
| 查看表大小 | 每月 | 1分钟 |
| 清理binlog | 每月检查设置 | 2分钟 |
| 优化表 | 每季度 | 低峰期做 |
| 慢查询分析 | 每月 | 10分钟 |
| 检查未使用索引 | 每季度 | 5分钟 |
| 验证备份恢复 | 每月 | 15分钟 |
真实案例
一个电商数据库,跑了两年没维护。某天突然慢到无法下单。检查发现:binlog没设置自动清理,占满磁盘,数据库只读。日志表1.2TB,碎片占300MB。未使用的索引有8个。清理后,查询速度恢复,磁盘空间释放40%。
运维说:“我以为数据库不会变慢。它只是慢得很慢,你没发现。”
最后一句
数据库不是你不管它就不会出问题。它会慢慢变胖、变碎、变慢。
今天去检查你的数据库:表大小谁最大?binlog清理设了吗?慢查询日志有吗?备份能恢复吗?把这6件事写进日历,每月固定时间做一遍。数据库会保持健康,你也会少很多半夜被叫醒的机会。保养一次,安稳一月。




