如何加快MySQL双表INNER JOIN关联查询的执行速度
联表查询慢的实际优化方案
1. 优先排查缺失索引(90%同类慢查询的根因)
- 针对两张表的关联、过滤字段补全覆盖索引,彻底避免全表扫描:
- 给
driverReg表的关联字段ni建索引,最好把查询需要用到的该表所有字段纳入索引范围,完全避免回表查数据。建索引示例:CREATE INDEX idx_driverreg_ni_all ON driverReg(ni) INCLUDE (fullName, email, mobile, address1, town, postCode, utr, vatNo, driver_number, pt, siteLoc, vatCharge, vraRate, third_party_provider)(低版本MySQL不支持INCLUDE语法的话,直接把这些字段按查询顺序追加为联合索引的后续列即可) - 给
scheduling表的过滤条件ref、excluded建联合索引,同时把关联用的ni字段、实际需要返回的scheduling表字段纳入索引,避免过滤后还要回表取数。
- 给
2. 修正SQL写法本身的性能损耗
- 把SQL里的
sc.*替换成业务实际用到的scheduling表字段,不要全量拉取无关列——全量拉取不仅增加数据传输开销,还会因为索引覆盖范围不匹配,直接让之前建的覆盖索引失效,触发大量回表操作。 - 核对两个关联字段
ni的表结构:如果scheduling.ni和driverReg.ni的字段类型、字符集、排序规则不一致,联表时会触发隐式类型转换,哪怕建了索引也完全走不上,直接变成全表扫描。 - 替换当前直接拼接变量的SQL写法,改用参数化查询:用PDO或mysqli绑定
$ref参数,让数据库可以缓存预编译的执行计划,不用每次请求都重新做硬解析、生成执行路径,还能顺带解决SQL注入的安全问题。
3. 用执行计划定位慢的具体节点
- 跑一遍
EXPLAIN <你的SQL语句>,重点排查这几个指标:- 看
driverReg表对应的行查询类型是不是ALL(全表扫描),如果是就是ni字段没走到索引 - 看
Extra列有没有Using join buffer的提示,这代表数据库因为关联字段没索引,把驱动表的结果集放到内存里做嵌套循环匹配,开销极高 - 看预估扫描行数
rows是不是和实际业务量偏差极大,如果扫描行数接近全表总数据量,就是索引没命中
- 看
4. 小结果集场景的替代优化方案
- 当
sc.ref = 'xxx'过滤后匹配的scheduling表记录只有个位数时,完全可以把联表拆成两次查询:先查scheduling表拿到所有匹配的ni值,再用SELECT ... FROM driverReg WHERE ni IN (拿到的ni值列表)查司机信息,在PHP层做数组数据拼接。这种拆法比数据库做嵌套循环联表的开销更低,因为两次查询都可以精准走索引,没有额外的联表匹配开销。
内容的提问来源于stack exchange,提问作者Richard Jacobs
相关产品推荐
相关产品推荐

