数据库IO瓶颈怎么优化?6个实用技巧让查询快3倍

阿木 发布于 5 小时前 2 次阅读


2026年2月12日,晚上六点多的时候,刚刚把线上的一个慢SQL日志给翻完了,我感觉到眼睛疼。

又是因为缓存放不下,磁盘在那嘎吱嘎吱读。

其实不想拆库的。

确实是这样,一旦想到当那些分布式事务被拆解完之后,以及跨库join的情况,还有运维人员在半夜打电话过来询问你“主键冲突了要怎么去处理”,就会让人感觉到头皮一阵发麻。

然而毫无办法,数据量不顾一切地疯长,恰似春季庭院之中的杂草,你于昨日刚剔除完此批,至今日彼批又迅猛冒尖。

磁盘读IO瓶颈怎么办

有着太多的热点数据,buffer pool的大小却仅仅只有那么一点儿,恰似那快挤爆的像早高峰时候的地铁。

你明明清楚这行数据处于磁盘的某个扇区之中,然而却无法触及,必须排队等候。

最气的是,有些字段压根不怎么查,却占着整行的缓存坑位。

扔长文本,扔备注,扔 JSON 配置于扩展表。主表仅留订单号,仅留用户 ID,仅留状态,仅留金额。一页数据可多塞几十行,缓存命中率肉眼可见地回升了。

别用join。我知道你手痒,但忍一下。

在业务层面,首先去查询主表,进而获取到ID列表,接着通过in操作去查询扩展表,代码层面虽说显得有些啰嗦,然而数据库却呈现出了满意的状态。

SQL里塞满join、group by、order by

见过那种一个SQL写四五十行的吗。

还全表扫描。

索引都没用对,还怪数据库慢。

建立合适的索引有那么难吗

组合索引的顺序搞反了,选择性高的列放后面,等于白建。

更别提那些给日期字段加上函数的,就是那种,where date(create_time) = ‘2026-02-12’ 这种情况。

索引失效,全表扫描,CPU瞬间飘红。

你倒是省事了,数据库快被你逼疯了。

把计算的逻辑,向外进行挪动,在业务层进行计算,由缓存层来承担,不要什么都一股脑儿地塞给数据库这个老实巴交的角色,使其不堪重负。

单表几千万行还在硬扛

水平分表其实没想象中那么可怕。

按用户ID哈希一下,拆成16张表。

每一张表之中,存在着几百万行的数据,索引的深度,从原本的4层降低到了3层,如此一来,执行计划看起来都变得舒展了许多。

不过分表键一定要选对。

90%的查询都带上这个字段,你才能享受分片带来的红利。

要是没带该怎么进行查询呢,是采用映射表的方式、基因法的方式或者——说实话——直接将其怼到ES宽表当中去吗。

分都分了,就别指望一个筐装天下。

非热点字段拖累整张表

垂直分表这事,最适合那种“表里啥都有”的设计。

有一个用于存放用户相关信息的表,其中存着手机号,还有昵称,另外还存了身份证照片以 base64 形式呈现的数据,以及最后一次登录时的 IP 归属地,并且还有用户所撰写的长达三千字的自我介绍。

数据行的宽度,如同高速公路那般宽阔,在数据库里,一页之中读不出几行数据,热点数据反倒被排挤出去了。

把活跃字段留在主表,不常用的、大字段的,扔给扩展表。

这样扫描效率翻倍,IO压力也下来了。

分页排序怎么变慢了

分页查询的噩梦通常发生在排序字段不是分片键的时候。

你要从十六个分片里头,各自取出前面的一百条,接着在内存当中进行归并排序,随后再取出前面的一百条。

内存消耗、网络开销,都比单库单表复杂得多。

非分片键排序为什么更慢

要么去接纳这个代价,要么致使业务侧作出妥协,即默认仅仅查询最近三个月的数据,且默认按照分片键进行排序。

有时候,技术解决不了的问题,需求可以。

跨库事务到底怎么办

两阶段提交,XA协议。

听起来很标准,用起来很沉重。

协调节点一挂,所有参与者都得等,锁释放不了,业务直接卡死。

所以现在大家学聪明了。

若无必要,请勿跨库。于设计分片键之际,尽可能将那些要求严苛一致性的数据放置于同一分片之内。

实在不行,上Seata这种分布式事务框架。

但心里得清楚,一致性是有代价的。

到底拆还是不拆

有个原则我贴在工位挡板上了:

能不分尽量不分。

先试试升级硬件、读写分离、索引优化、冷热归档。

诸多被谓之“大数据量”者,实则唯慢查询为之作祟罢了,索引构建得当,问题便消解大半矣。

真的走到分库分表这一步,也不是终点。

只是换了一个更复杂的环境,继续面对那些永恒的命题。

数据一致性、查询效率、运维成本。

没有银弹,只有权衡。

窗户外天已经黑透了。

咖啡凉了,但线上那个订单库终于稳住了。

下周还得盘一下日志表,那玩意儿也快两亿行了。

啧,又是分表。

不过那是下周的事。

今晚,先回家。