站长必避的3个MySQL设计陷阱
|
去年3月,凌晨三点十七分,我盯着一条慢查询日志发呆——不是执行时间长,是它根本没走索引,却在查主键。我原以为是SQL写错了,结果发现表结构里那个字段叫`user_id`,类型是varchar(255),而业务代码传过来的是纯数字字符串,MySQL在隐式转换时默默走了全表扫描。运维老张电话打进来,说“你那库CPU又上92了”,我没回他,手指停在键盘上,听见自己后槽牙咬得发酸。 窗外天亮了,灰白光从百叶窗缝里渗进来,照在显示器右下角的系统时间上。我泡的面早凉透了,汤凝成一层薄油膜。这破事拖到第五天,还是靠业务方一句“我们接口从来没改过”,我才想起来翻三个月前的git记录——他们把原来用int存的ID字段,为了“兼容未来扩展”,改成varchar了。没人告诉我,没人评审,没人测性能。就一个ALTER语句,像往井里扔块石头,水面晃两下,大家各自去忙了。 第一个陷阱,就是这种“看着安全”的类型宽松。不是弱口令,不是未授权,是字段类型和实际使用之间那层薄冰。你说它错吗?不报错。能跑通吗?能。出事那天,攻击流量并不大,就是一阵一阵的、带参数的GET请求,每轮几十个IP,但每次参数都拼一个超长的`user_id=12345678901234567890...`(后面跟两百个零),MySQL直接拿它当字符串去匹配,索引失效,线程池瞬间塞满。我后来查过执行计划,explain出来全是type: ALL,key: NULL。可我说不清是扫描器自动试探出来的,还是谁手抖填错的测试数据——反正没在WAF日志里找到明确特征,防火墙日志一直在刷,密密麻麻全是SYN_RECV,但源头混在正常流量里,根本没法揪。 第二个,是那个“全站唯一ID”字段,建了唯一索引,但没加NOT NULL。同事小陈凌晨两点甩给我一个截图:报错 `Duplicate entry '' for key 'uk_user_code'`。空字符串重复?怎么可能有空字符串进得来?我扒代码,发现是上游调用方有个异常分支,没传参,后端默认给了空字符串,而MySQL对varchar的空字符串和NULL是两个东西,索引只拦住了NULL,拦不住''。于是三百多个并发一压,一半失败,一半卡死。我删了索引重加,加了`NOT NULL DEFAULT ''`——结果第二天早上,业务方说“登录页跳转异常”,我一看,他们把用户code缓存到Redis用了,code为空时直接塞了个空串进去,前端再取的时候JSON.parse报错。我骂了句脏话,手机一直在震,是另一个项目的告警群在@我,没点开。 其实最扎心的不是这个。是第三个:所有表都加了create_time和update_time,默认CURRENT_TIMESTAMP,连历史归档表都加。听起来很规范吧?可有个订单详情表,每天增量五十万行,字段不多,就七八个,但update_time用了ON UPDATE CURRENT_TIMESTAMP。问题是,业务逻辑里有个定时任务,每小时批量更新所有未完成订单的状态字段——哪怕状态本来就是“处理中”,也update一次。等于每小时给五十万行重新写一遍update_time。磁盘IO半夜开始狂飙,备份失败三次。我盯了一整夜IOPS曲线,最后把它改成手动SET update_time = NOW(),才喘上气。运维老张来交接班,看我眼睛血丝密布,递了包烟,说:“你记得咱刚入职那会儿,DBA还管不让用TIMESTAMP么?”我点点头,又摇摇头——现在没人管了,都交给ORM,交给自动生成脚手架,交给“先上线再说”。 说到这儿想起上周工单。有个老系统要迁库,开发提了个需求:把所有tinyint(1)的布尔字段改成enum('Y','N'),说“更语义化”。我回:“你确定?MySQL的enum是字符串比较,不是数值比较;而且一旦加新值,alter table锁表时间不可控。”他回复:“那用bit(1)?”我直接关了对话框。晚饭吃的食堂炒饭,蛋炒得老了,米粒干巴巴的。反正这类事,吵一架比改三次表更快。业务方昨天还说:“你们测试不是应该帮我们兜底吗?”我叼着烟站在楼道口,没说话。兜底?我连自己写的自动化脚本里,那个模拟大批量UPDATE的case,都一直没补上update_time触发场景的断言。 站长必避的3个MySQL设计陷阱,站得住的地方,其实是优胜劣汰——活下来的站点,不是因为多懂SQL优化,是被坑过三回以上,疼得记住了。那些还在用varchar存手机号、用datetime存毫秒级时间戳、用TEXT存JSON还不加校验的,不是水平低,是还没撞够南墙。我上次复盘时翻了下监控,发现有个子查询嵌套七层的报表SQL,居然跑了三年没报警,因为QPS一直低于1。它就搁那儿,像根鱼刺,不疼,但你咽东西的时候总觉着不对劲。
文章配图,仅供参考 现在我把所有建表DDL都扫一遍,强制要求注明“为何选此类型”“此字段是否参与查询条件”“是否允许为空”。当然,业务方不填,我就标红挂起。反正最近在补一个事情:把自动化用例里的数据构造逻辑,全换成随机长度+类型混合生成,再也不信开发说的“永远只有数字”了。这事防不住。最多熬得久点,眼尖点,等下次哪天凌晨三点十七分,日志又飘出一行熟悉的ALL扫描,我能比上次快十秒看出问题在哪。(编辑:站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |


高效信息流设计:8个后端提效工具
大数据架构师避而不谈的3大代码陷阱
Go服务器性能调优:9年实战破选型陷阱,QPS翻倍
多媒体开发必修:视觉惊艳与秒开兼得的网站设计法则
小户型扩容神器:12个设计师私藏科技网站
媒体网站无障碍设计:9个易被忽视的技术适配点
游戏体验官实测:5大技术陷阱,90%玩家第3关中招

