我删了一行SQL,公司差点给我发律师函

2026-04-22 11 0

我删了一行SQL,公司差点给我发律师函

别慌,不是真的发律师函。但那天CTO看我的眼神,确实像在看一个准备跑路的员工。

事情是这样的。那是个普通的周四晚上,我正准备下班享受周末,突然监控报警:订单服务响应时间暴涨20倍,大量请求超时。看了眼日志,DB连接池耗尽。一查原因,某位同事白天加了段「看起来无害」的查询,没有加索引,拖垮了整个系统。

我的处理方式很简单粗暴:先临时禁用那个查询涉及的接口,等下周一再处理。但CTO问我能不能根本解决,我脱口而出:「加个索引就行,一行SQL。」然后我打开Navicat,连上生产库,准备执行。

就在这时,CTO按住了我的手。


你知道生产环境执行SQL意味着什么吗?

很多初中级工程师可能不理解:在生产库执行DDL(Data Definition Language,比如ALTER、CREATE、DROP)是有风险的。MySQL 5.6之前,ALTER TABLE会锁表,线上服务直接hang住。即使用了新版本,也可能导致主从延迟、binlog膨胀、缓冲池污染等问题。

更重要的是:没有回滚预案的操作就是裸奔

那天我运气好,CTO拦住了我。但不是所有人都这么幸运。我见过有团队直接在生产库执行DROP TABLE的——备份?没做。binlog?没开。数据?没了。

所以今天我要讲的不是「如何加索引」这种烂大街的话题。我想讲讲作为一个后端工程师,你对数据库做的每一个操作,都应该在你的掌控之中


数据库操作的安全规范(血泪教训版)

第一条:永远不要在生产库直接操作

这条听起来像废话,但80%的团队做不到。为什么?因为懒。或者因为「就加个索引,5秒钟的事」。

正确流程是什么?

1. 在测试环境验证SQL的正确性和执行时间
2. 确认有完整备份(不是上周的备份,是操作前的备份)
3. 准备回滚方案(保留原表结构、准备恢复脚本)
4. 在低峰期执行(通常是凌晨3点,别问我为什么知道)
5. 执行后持续监控,保留操作记录

我知道有人会说「我们有主从,切换一下就行」。但主从切换本身就是个高风险操作,而且很多公司的从库同步延迟堪忧,切换了可能数据不一致。

第二条:DDL操作要用pt-online-schema-change

如果你用MySQL,Percona Toolkit的pt-osc是标配。它的工作原理是:创建新表→逐步迁移数据→替换原表,整个过程不会锁表。

pt-online-schema-change --alter "ADD INDEX idx_xxx(xxx)" D=test t=orders --execute

但别以为用了pt-osc就万事大吉。迁移过程中会占用额外磁盘空间(通常是原表1.5-2倍),对CPU和IO也有压力。有次我给一个2亿行的大表加索引,pt-osc跑了3天,期间监控报警不断。

所以第三个建议是:大表操作前,先估时间和资源。可以用这个公式估算:

预估时间 = (表大小 / 迁移速度) * 迁移批次
# 一般单次迁移1000-5000行,太多了会锁表,太少了效率低

第三条:每一次操作都要可追溯

我见过太多团队,数据库被改了n次,但没人知道是谁改的、为什么改、什么时候改的。这种情况下一旦出问题,只能靠猜。

解决方案:用SQL审核平台。现在开源的比较成熟的有Yearning、Archery、SQLE等。这些平台可以做到:

  • SQL语法检查和优化建议
  • 执行必须走审批流程
  • 记录每次操作的执行人和执行时间
  • 支持回滚(基于binlog解析)

没有审核平台怎么办?至少建个Excel表,记录:操作人、操作时间、SQL内容、执行原因、回滚方案。这总比什么都没有强。


关于连接池,你可能一直理解错了

回到开头那个事故。最终我是怎么解决的?加索引确实解决了问题。但更重要的是,我们重新审视了整个服务的数据库连接池配置,发现了更严重的问题。

连接池大小应该设多少?很多人会告诉你「看你CPU核心数,8核就设8*2=16」或者「根据并发量来设」。这些说法对,但不够精确。

更精确的公式是:

最佳连接数 = ((核心数 * 2) + 磁盘数) * 预估QPS / 1000 + 备用容量

但这个公式也不是万能的。关键参数是预估QPS单Query的平均执行时间

假设你的数据库单Query执行时间是10ms,那一个连接每秒最多处理100个请求。如果你的QPS是1000,理论上需要10个连接。但如果你的应用服务器有10台,每台10个连接就够了。

但很多人犯的错误是:把连接池设得太大

连接池过大的危害:

  • 数据库端连接数过多,CPU context switch飙升
  • 每个连接都占用内存,数据库内存压力增大
  • 瞬时并发来的时候,大量连接同时竞争资源,反而更慢

连接池过小的危害:

  • 请求排队,服务响应延迟
  • 数据库资源浪费(连接数太少意味着很多查询是串行的)

那怎么找到最优值?我的经验是:先设一个保守值,然后压测观察。观察两个指标:

  1. 活跃连接数是否接近最大值(接近说明不够)
  2. 等待连接的线程数是否为0(为0说明够用)

如果活跃连接数长期在80%以上,等待线程数>0,就该加连接池大小。如果这两个指标长期在20%以下,说明设大了,可以适当缩小。


最后的忠告

写这篇文章的时候,我想起了那个差点被开除的夜晚。CTO后来跟我说,他不是怕我删数据,他是怕我养成「直接操作生产库」的习惯。这种习惯迟早会出问题,出问题的时候,往往是不可挽回的那种。

后端工程师最值钱的品质是什么?我认为是敬畏心。对生产环境的敬畏,对数据的敬畏,对每一个可能影响用户操作的敬畏。

你可以在测试环境横着来,但生产环境,多小的一个操作都要有backup和rollback plan。这不是胆小,这是专业。

下次有人跟你说「就加个索引,5秒钟的事」,你可以把这篇文章甩给他。

完。

相关文章

「搞不定部署?」来,小龙虾帮你一键搞定 🦞
「搞不定部署?」来,小龙虾帮你一键搞定 🦞
别把API设计成玄学:我踩过的10个设计大坑
别再用RESTful了,你的API设计可能从根子上就错了
🤖 AI探索|最近我在信息洪流里捞到的好东西
还在为部署AI工具抓狂?让小龙虾帮你搞定!🦞

发布评论