加入收藏 | 设为首页 | 会员中心 | 我要投稿 站长网 (https://www.beijidao.cn/)- 应用程序、AI行业应用、CDN、低代码、区块链!
当前位置: 首页 > 站长学院 > MySql教程 > 正文

MySQL运维与设计避坑指南:12年踩坑总结的5大致命陷阱

发布时间:2026-10-28 11:15:35 所属栏目:MySql教程 来源:DaWei
导读:  跟产品经理吵完三小时,他甩下句“你先按我PRD做,出问题再说”,转身点了杯燕麦拿铁——奶泡拉花歪得像我们上周上线的主键策略。我盯着他工位上那盆快枯死的绿萝,叶子边缘焦黄,和我们生产库的 innodb_buffer_pool_size

  跟产品经理吵完三小时,他甩下句“你先按我PRD做,出问题再说”,转身点了杯燕麦拿铁——奶泡拉花歪得像我们上周上线的主键策略。我盯着他工位上那盆快枯死的绿萝,叶子边缘焦黄,和我们生产库的 innodb_buffer_pool_size 一样,明明设了 75%,实际压根没用上。Chrome DevTools 切在 Network tab,但其实我在偷看 console 里上条 SQL 的 warning:`InnoDB: Cannot add or update a child row: a foreign key constraint fails`——这警告半年前我就见过,在那张被我们叫“祖宗表”的 user_profile 上,当时没当回事。


文章配图,仅供参考

  第一个坑:唯一索引当主键使。不是说不行,是你们改字段长度时真没看过 pt-online-schema-change 的 --chunk-index 参数?去年七月,测试环境跑得好好的,上线前夜我发现它悄悄选了 name 字段上的唯一索引(varchar(255))当 chunk 索引,而 real_name 字段刚被 PM 塞进一个正则校验,导致 varchar(255) 实际存了二百四十八个字符加六个 Unicode 零宽空格。F5 刷新三次:第一次抬手、第二次按键、第三次松手,页面卡在 loading 图标——不是前端的事,是 ALTER TABLE 卡在 copy phase 第七千八百二十三个 chunk,MySQL 写日志的线程自己把自己锁死了。这个我敢打包票:只要你在 utf8mb4 下建过带 emoji 的唯一索引,又动过 varchar 长度,你就中招过。旁边后端小哥端着保温杯去倒水,路过我桌时说“要不我帮你重放 binlog?”——他不知道那条 DROP INDEX 的语句,已经在 relay log 里躺了四十七分钟。


  键盘左下角的 Ctrl 键卡了一下,按下去弹不回来,我干脆用 Caps Lock 代替。第二个坑:timestamp 自动更新。我说的是那个默认 CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP 的列。不是它不能用,是我们总把它和业务时间混着用。上个月用户投诉“订单状态倒退”,查出来是某个触发器 update 了 order_status,连带把 updated_at 捂得严严实实——可业务逻辑真正依赖的“最后操作时间”是另一列 operator_updated_at,它根本没加 ON UPDATE。我原以为加个注释就能防住,结果 DBA 在 review SQL 时顺手删了注释,还加了句 `-- 已校验,此字段无业务语义`。汤洒了一滴在我右屏壁纸上——那是张三年前杭州机房巡检拍的冷凝水管照片,锈迹斑斑,和我们线上库的 `max_connections=300` 一样,表面正常,一压就漏。


  第三个坑:读写分离中间件的事务穿透。别信文档里写的“自动识别 BEGIN/COMMIT”。我们用的某开源 proxy,对 PREPARE/EXECUTE 语句压根不认,prepare 的连接在 write node,execute 却落到 read node,然后报错 `ERROR 1267 (HY000): Illegal mix of collations`。这错误不是字符集问题,是连接上下文丢了。PM 昨天发邮件问我:“为啥下单成功但查询不到?”——我截图 show processlist 给他,他回了个“?”外加一个正在加载的 GIF。测试同事蹲在我身后念叨:“我测过三轮,都是先点支付再刷新订单页”,她手指甲涂着褪色的薄荷绿,敲键盘的声音像雨打芭蕉。我没接话,因为她不知道我们凌晨两点手动切走流量时,proxy 日志里那行 `failover triggered by ping timeout: 2381ms > 2000ms` 是怎么被运维当成 P0 告警忽略的。


  CSS 命名这事真挺烦。`.btn-primary--large--disabled` 和 `.btn--primary-large-disabled` 到底哪个更符合 BEM?我们前端上周为这个 PR review 折腾掉一天半。可你知道最荒诞的是什么吗?我们数据库里一张配置表叫 `sys_config_v2`,V2 后面没注释、没迁移脚本、没人记得清 V1 是哪年删的——但它被三十一个服务强依赖着,其中十七个还用着 `SELECT FROM sys_config_v2 WHERE k = 'user_cache_ttl'`。而那个值,三个月前被某个实习生改成 `'30m'`(字符串),不是整数。没人改代码,全靠应用层 parseLong 时抛异常降级到默认值。无所谓吧,反正现在缓存雪崩也只影响十一秒。


  第四个坑:JSON 字段建虚拟列再加索引。听上去很酷,对吧?我们真这么干了。给 `profile JSON` 加了 `name_virtual VARCHAR(64) AS (JSON_UNQUOTE(JSON_EXTRACT(profile, '$.name')))`,又在上面建索引。结果呢?explain 显示走了索引,但执行时间比全表扫描慢四十毫秒。为啥?因为 JSON_EXTRACT 返回的是 utf8mb4_bin 排序规则,而我们的 collation_connection 是 utf8mb4_0900_as_cs。`SELECT ... WHERE name_virtual = '张三'` 实际执行等价于 `WHERE CONVERT(name_virtual USING utf8mb4_0900_as_cs) = '张三'`——索引失效,只是 MySQL 没吭声。我敢打包票:只要你在 MySQL 8.0.22+ 上建过 JSON 虚拟列,就踩过这个静默转换坑。那个实习生今天来交转正答辩材料,封面印着“基于 DDD 的微服务重构实践”,我没让他讲完,直接问:“你们用的 JSON Schema 校验库,支持 `$ref` 循环引用吗?”他愣了两秒,说:“应该……支持?”


  最后一个坑最贱:自增 ID 回收。没人明说,但所有人默认它“安全”——delete 一条,insert 一条,ID 就该续上。去年十月,我们把老用户表拆分,写了个脚本批量迁移数据,逻辑是 `INSERT IGNORE INTO new_user SELECT FROM old_user`。结果发现新表 ID 从 1 开始疯长,不是跳号,是直接按旧表最大 ID + 1 往上叠。查了三天,发现 `INSERT IGNORE` 触发了自增锁升级,而旧表有三十七万条记录,中间某次死锁让 auto_increment_offset 乱跳。这不是 bug,是设计。MySQL 就是这样,它不保证连续,只保证递增。我说不准它在 5.7 和 8.0 里行为是否一致,但我知道:只要你的监控告警里还写着 “auto_increment_ratio > 0.95”,你就在裸泳。


  工位右屏壁纸换过两次,上次是东京地铁站台的实时拥挤度热力图,红得刺眼;这次换成了一张模糊的云服务器控制台截图,日期看不清,但能看到右上角系统时间是 03:17:44。明天早上九点,我要带着这五条一起进架构评审会。他们肯定又要问:“有没有替代方案?”我准备说:“有。换 PostgreSQL。”但不说真话,只说:“先上线 pt-archiver 清历史数据,把 buffer pool 压到六成以下试试。”至于那个实习生——他刚给我微信发了个文档链接,标题叫《MySQL高可用与弹性伸缩最佳实践》,里面第七页写着:“推荐使用 UUID 作为主键以规避自增冲突”。我回了个句号。然后关掉了 Chrome 里开着的 twenty 三个 Tab,包括那个始终没点进去的 AWS RDS 迁移白皮书 PDF。

(编辑:站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!