上周有个朋友半夜给我打电话,说公司的MySQL数据库突然挂了,业务直接瘫痪,客户投诉电话打爆了。他一脸懵逼地问我:“我不是做了主从复制吗?怎么还是出问题了?”我问他:“你监控了吗?你备份恢复演练了吗?你做过压力测试吗?”电话那头沉默了几秒。这种情况我见得太多了,很多人以为搭个主从复制就算高可用了,以为每天跑个mysqldump就算数据安全了。MySQL数据库运维这件事,坑多着呢,今天我就把我这些年踩过的坑、攒下的经验掰开揉碎了聊一聊。

先说说备份这件事。很多人觉得备份就是跑个脚本,每天凌晨把数据库导出来存到磁盘上就完事了。但你有没有想过,如果磁盘坏了呢?如果备份文件损坏了,但你没发现,等真出事的时候才发现恢复不了,那场面得多酸爽。我见过一个公司,备份文件每天正常生成,但他们从来不检查备份的完整性。直到有一次数据被误删,他们兴冲冲地跑去恢复备份,结果发现备份文件早就损坏了,整整一个月的备份全是废的。从那以后,我给自己定了个规矩:备份不仅要“有”,还要“好”。每天备份完成后,必须做一次恢复测试,至少把备份文件导入到一个测试库,检查表结构和数据行数是否正常。另外,备份文件绝对不能和数据库放在同一台服务器上,最好是异地备份,云存储也好,另一台服务器也好,要保证数据库服务器挂了,备份还能活着。
说完备份,再聊聊监控。监控这件事,很多人觉得装个Zabbix或者Prometheus就完事了,然后就不管了。但你真的知道监控什么才是关键吗?我见过一个运维小哥,每天盯着CPU使用率和磁盘空间看,结果数据库慢查询把业务拖死了,他完全没发现。MySQL的监控,我认为至少要看这几个方面:慢查询日志、连接数、InnoDB的缓冲池命中率、主从复制的延迟时间。尤其是慢查询,很多人觉得慢查询日志开了影响性能,就不开。但你要知道,一个慢查询可能只消耗几秒钟,但如果并发量上来了,这几秒钟的延迟就能把数据库拖垮。我一般建议所有生产环境的MySQL都开启慢查询日志,把执行时间超过1秒的SQL记录下来,然后定期分析,该加索引的加索引,该改SQL的改SQL。另外,主从复制的延迟也是一个容易被忽略的点,很多公司的主从复制延迟了几分钟甚至几小时,业务还在读从库,用户看到的数据都是老的,体验极差。所以监控里一定要有复制延迟的告警,延迟超过一定阈值就立刻通知。
接下来聊一个老生常谈但很多人依然搞不定的话题:主从复制的搭建和运维。很多人觉得主从复制就是改个配置文件,然后执行个CHANGE MASTER TO就完事了。但实际运维中,主从复制出问题的情况太多了。比如网络抖动导致复制中断,比如主库上执行了一个大事务导致从库延迟飙升,再比如从库的磁盘空间满了导致复制卡住。我见过最离谱的一次,是有人把从库的binlog格式改成了STATEMENT,结果主库执行了一条随机函数,从库复制的数据跟主库完全对不上。所以,主从复制的运维,我强烈建议:第一,binlog格式一定要用ROW,虽然日志量会大一些,但数据的准确性有保障;第二,要定期检查复制状态,写个脚本每天跑一遍,看看SlaveIORunning和SlaveSQLRunning是不是都是Yes;第三,如果复制中断了,不要急着用SET GLOBAL SQLSLAVESKIP_COUNTER跳过错误,要先搞清楚错误原因,否则数据不一致的风险很大。
再说一个很多运维人员容易忽略的点:MySQL的安全配置。很多人觉得数据库就是给应用程序用的,只要应用程序能连上就行,安全配置随便搞搞。但你想想,如果数据库暴露在公网上,或者弱口令被爆破,那后果有多严重?我见过一家公司,MySQL的root密码设置成了123456,还开了公网访问,结果被人拖库了,用户信息全泄露,公司差点倒闭。MySQL的安全运维,我认为至少要做到这几点:第一,严禁使用root账户进行日常操作,每个应用都用独立的账号,权限按需分配,最小化原则;第二,数据库服务器不要暴露在公网上,如果必须远程访问,要用SSH隧道或者VPN;第三,开启审计日志,记录所有DDL和DML操作,万一出事了,能追溯到是谁做了什么;第四,定期修改密码,而且密码要足够复杂,至少12位以上,包含大小写、数字和特殊字符。
再聊聊MySQL的版本升级和补丁管理。很多人觉得数据库稳定运行就别动了,升级什么的太麻烦,容易出问题。但你有没有想过,旧版本的MySQL可能存在严重的安全漏洞或者性能问题?比如MySQL 5.6的某些版本就有内存泄漏的问题,跑久了内存占用越来越高,OOM被系统杀掉。我见过一个公司,MySQL 5.5跑了五六年,从来没升级过,直到有一天数据库突然崩溃,查了半天才发现是某个已知Bug触发了。从那以后,我定了个规矩:MySQL的版本不能落后两个大版本以上,而且小版本的补丁也要及时打。升级之前,先在测试环境跑一遍,看看有没有兼容性问题,特别是SQL语法和存储引擎的变化。另外,升级的时候要注意停机时间,尽量选择业务低峰期,而且要做回滚预案,万一升级失败了,能快速切回旧版本。别想着升级失败了我再慢慢修,业务等不起。
最后聊聊一个容易被很多人忽视但非常关键的点:数据库的容量规划和性能调优。很多人觉得数据库慢就加索引,或者加内存,但真正的问题可能出在磁盘IO、网络带宽或者SQL语句的写法上。我见过一个案例,公司业务增长很快,数据库的磁盘空间快满了,运维人员慌慌张张地加了一块磁盘,但没做数据迁移,结果新磁盘和旧磁盘挂载到了不同的目录,数据分散了,查询性能反而更差了。容量规划这件事,不能等磁盘满了再想办法,要提前做。比如,根据业务增长趋势,预估未来半年到一年的数据量,提前准备好扩容方案。另外,性能调优也不能靠感觉,要用工具说话。比如用pt-query-digest分析慢查询日志,用Percona Toolkit检查表结构和索引的使用情况,用SHOW ENGINE INNODB STATUS查看InnoDB的内部状态。调优的时候,不要一上来就改内核参数,先看看SQL语句和索引有没有优化空间,往往改一条SQL就能解决80%的性能问题。
说真的,MySQL数据库运维这件事,没有一劳永逸的方案,也没有万能的神器。备份、监控、主从复制、安全配置、版本升级、容量规划,每一个环节都有无数细节,每一个细节都可能成为压垮业务的一根稻草。但只要你把这些基础工作做扎实了,把每一个环节都当作“万一出事怎么办”来思考,你的数据库就能在大多数情况下稳如老狗。别等到数据库挂了才想起备份没做,别等到数据丢了才想起恢复测试没跑,别等到业务瘫痪了才想起监控没配。数据安全与高可用,不是靠运气,而是靠每一个运维人员日复一日地死磕细节。


