广告:Codex Token 低价中转站稳定接口 · 快速接入 · 开发者备用通道
Engineering article

SQL查询优化技巧,建议收藏

SQL查询优化是真实场景中最让人抓狂的活儿。我见过太多人因为没用好索引,把整个数据库卡成狗。索引不是万能的,但没索引绝对是地狱。2024年以后,越来越多的项目开始用到分布式数据库,比如TiDB,这时候优化就不仅仅是调几个参数那么简单了。记得有一次,我写了一个用窗口函数的查询,结果没优化前每秒只能跑3条,加上物化视图和分区策略之后直接翻了十

SQL查询优化技巧,建议收藏
配图来源于网络和AI生成,仅供参考。
▌ 技术引导
SQL查询优化是真实场景中最让人抓狂的活儿。我见过太多人因为没用好索引,把整个数据库卡成狗。索引不是万能的,但没索引绝对是地狱。2024年以后,越来越多的项目开始用到分布式数据库,比如TiDB,这时候优化就不仅仅是调几个参数那么简单了。记得有一次,我写了一个用窗口函数的查询,结果没优化前每秒只能跑3条,加上物化视图和分区策略之后直接翻了十倍。关键是,这类优化往往是团队协作的结果,不是一个人能搞定的。真正有用的技巧包括:合理使用覆盖索引、避免全表扫描、调整join顺序、善用缓存机制、理解执行计划的细节,还有用explain分析慢查询。这些方法在2025年的实际场景中已经不是新鲜事,但很多人还是没掌握。

▌ 技术参考

一 在2024-2026年期间,SQL查询优化的核心在于对执行计划的深度理解。使用EXPLAIN命令查看查询的执行路径,是每个DBA必须掌握的技能。尤其在使用MySQL 8.0时,EXPLAIN的输出增加了cost模型,能更直观地看到每个操作的代价。例如,执行计划中的type字段,如果出现ALL,说明是全表扫描,必须想办法加索引或者进行分区。有些情况下,全表扫描反而比索引扫描快,比如数据量小于100万条的时候,这时候优化策略就需要调整。

二 覆盖索引是优化中一个非常实用的手段。它指的是查询所需的字段全部包含在索引中,避免回表操作。例如,创建联合索引(user_id, created_at, status)的时候,如果查询条件是user_id = 123 AND created_at > '2025-01-01',并且status字段是查询的一部分,这时候索引就能覆盖查询,减少I/O开销。但别以为只要把字段都加到索引里就行,要根据访问频率和查询模式来设计。有时候,一个包含太多字段的索引反而会拖慢插入和更新的速度,2025年很多公司开始在索引设计时引入索引合并策略。

三 避免全表扫描是优化的基本要求。在MySQL 8.0中,优化器会优先选择索引扫描,但如果索引条件不够精准,或者数据分布不均,它可能会错误地选择全表扫描。这时候需要手动干预,比如通过force index来强制使用某个索引。例如,在查询中加入FORCE INDEX (idx_user_id)可以让优化器忽略其他索引。不过这个方法要慎用,否则可能适得其反,甚至导致查询更慢。2026年某些大厂开始用索引统计信息+查询特征来动态选择索引,而不是硬编码。

四 join顺序在优化中有个很现实的影响。比如,当两个表的数据量差异很大时,优化器可能不会按你写的顺序来join。不过在MySQL 8.0中,有一些配置项可以影响这个行为,比如optimizer_switch里的join_buffer_size和block_nested_loop。在某些情况下,加上/+ join_buffer(512K) /这样的hint能有效提升性能。但2025年后的实例发现,这种hint在高并发环境下容易产生锁竞争,导致整体吞吐量下降,必须结合业务场景综合判断。

五 在分布式数据库中,比如TiDB,分区策略是优化的关键。TiDB的分区类型包括范围分区、列表分区、哈希分区等。对于时间序列数据,通常建议使用范围分区,比如按created_at字段划分,这样可以减少扫描的数据量。但分区太多也会带来管理成本,甚至影响写入性能。2026年的一些最佳实践显示,将数据按业务逻辑分块,比如按用户ID或业务类型分区,可以显著提升查询效率。此外,TiDB支持分区索引,这在某些复杂查询中表现更好。

六 有时候,优化不是靠更复杂的查询,而是靠更简单的结构。比如,把多个子查询合并成一个join,或者用临时表来减少重复计算。在PostgreSQL中,可以使用CTE(Common Table Expressions)来提升可读性和性能,特别是在反复使用子查询的情况下。例如,在查询中使用WITH子句建立临时结果集,可以避免重复执行相同逻辑,减少资源消耗。2024年一些高并发场景下,CTE的优化效果被证明比普通子查询更好,特别是在join操作中。

七 常见的踩坑场景之一是索引失效。比如,如果查询条件中使用了函数或者表达式,索引就会失效。例如SELECT FROM users WHERE YEAR(created_at) = 2025,这时候created_at字段的索引完全没用。解决办法是避免在索引字段上使用函数,或者改用范围查询。2025年以后很多系统开始用EXTRACT函数代替YEAR函数,这样索引就能正常工作。但要注意,EXTRACT在某些数据库中效率并不高,需要配合索引使用。

八 分区表在2026年已经成为主流,但很多人在使用时忽略了分区键的选择。比如,如果表是按时间分区,但查询经常涉及非时间字段,这时候分区的收益就会大打折扣。这时候可以考虑用组合分区键,比如将分区字段和业务字段结合。例如,在一张订单表中,按订单日期和用户ID分区,这样既能按时间快速筛选,又能按用户ID进行分组统计。但分区的粒度要控制好,太细的话管理成本太高,太粗的话又没用。

九 在MySQL中,有一个容易被忽视的参数是skip_name_resolve,它能显著减少查询时间。当这个参数开启后,数据库会直接使用IP地址而不是域名来连接,避免DNS解析带来的延迟。2024年之后,大多数高并发系统都会开启这个参数,尤其是在查询涉及大量JOIN操作时。不过,这个参数只能在全局配置中设置,不能在单个会话中调整,这意味着在应用层需要考虑是否支持这种配置。

十 索引的维护成本往往被低估。特别是在写入密集型的系统中,索引会成为性能瓶颈。2025年开始,一些公司开始使用延迟索引策略,也就是在写入数据之后,再异步更新索引。这种方法虽然能提升写入速度,但会增加查询延迟。在TiDB中,这种策略可以通过配置index_replication_mode来实现,但需要配合监控系统,确保查询不会因为索引未建立而崩溃。有些场景下,比如日志系统,这种延迟索引能带来50%以上的写入性能提升。

十一 在优化过程中,有一个很实用的技巧是用subquery改写。比如,将多个查询合并成一个子查询,减少网络传输和上下文切换的开销。在PostgreSQL中,可以使用LATERAL JOIN来替代子查询,这样能提升执行效率。例如,SELECT FROM orders o, LATERAL (SELECT COUNT() FROM users u WHERE u.id = o.user_id) u_count;这样的查询在2025年被多个团队实践,特别是在数据量大的情况下,效率提升非常明显。不过,LATERAL JOIN对某些老版本的数据库不支持,需要确认兼容性。

十二 有时候,优化一个查询需要考虑数据库的版本差异。比如,MySQL 5.7和8.0在join算法上就有很大不同,8.0引入了新的join优化器,能在某些情况下自动选择更优的join方式。但有时候,旧版本的join优化器会误判,导致性能下降。这时候可以通过配置optimizer_switch来调整join策略。例如,设置block_nested_loop=off可以禁用块嵌套循环算法,避免一些不必要的开销。不过,这种调整在2026年已经不算新鲜,很多系统会根据实际情况动态切换。

十三 在实际工作中,有一个常见的误区是盲目追求更复杂的查询结构,比如使用窗口函数或CTE,而不考虑执行计划的开销。例如,在PostgreSQL中使用窗口函数的时候,如果没有合适的索引,可能需要对整个表做排序,导致性能问题。这时候可以用物化视图或者临时表来替代,或者改用更简单的聚合操作。2025年有团队通过这种方式将原本几十秒的查询缩短到几毫秒,但前提是数据量稳定,不能频繁更新。

十四 在优化过程中,缓存机制往往被忽略。比如,MySQL的查询缓存在2024年之后已经被弃用,但InnoDB的缓冲池依然可以发挥巨大作用。通过调整innodb_buffer_pool_size参数,可以大幅提升读取性能。不过,在高并发写入的场景下,缓冲池可能会被写入操作占满,这时候需要配置innodb_io_capacity和innodb_max_dirty_pages_pct来平衡读写性能。2026年一些大规模系统开始用读写分离和缓存中间件来补充缓冲池的不足。

十五 如果查询本身无法优化,那么就要考虑数据库的架构。比如,在MySQL中使用分区表或者分库分表,可以有效提高处理能力。但这些手段需要配合合理的路由策略,否则反而会增加运维复杂度。在2025年,很多团队开始用ShardingSphere这类中间件来实现分库分表,同时兼顾了查询路由和索引管理。不过,这种方案对查询语句有较高的要求,不能随意改写,否则可能引入错误。