news 2026/3/27 15:05:36

面试官:MySQL JOIN 表太多,你有哪些优化思路?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
面试官:MySQL JOIN 表太多,你有哪些优化思路?

工作中,我们有时会遇到 MySQL join 表太多的情况,可能来自两个背景,一个是历史老代码,一个是去 o(Oracle) 改造,从 Oracle 迁移到 MySQL 的 SQL。

多张表的 join 很可能会带来问题,引发生产事故,增加后期维护成本。一个新系统上线时可能测不出问题,但随着数据量的增加,问题就会逐渐暴露出来了。

阿里开发手册中明确规定禁止三个表禁止 join。

那对于 MySQL 中 join 表多的 SQL,一般该怎么优化呢?

多个表使用 join 语句的根本原因是业务代码需要整合多张表里面的字段才能完成处理。那具体怎样优化呢?先来模拟一个多表 join 的 SQL,这里我们创建 5 张表:

CREATE TABLE`test1` ( `id`TINYINT(3) NOTNULLCOMMENT'主键ID', `a`VARCHAR(20) DEFAULTNULL, `b`VARCHAR(20) DEFAULTNULL, `c`VARCHAR(200) DEFAULTNULL, `d`TINYINT(3) DEFAULTNULL, `create_time`TIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMPCOMMENT'创建时间', `update_time`TIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMPCOMMENT'更新时间', PRIMARY KEY (`id`), KEY`a` (`a`), KEY`b` (`b`), KEY`c` (`c`), KEY`d` (`d`) ) ENGINE=INNODBDEFAULTCHARSET=utf8 CREATETABLE test2 LIKE test1; CREATETABLE test3 LIKE test1; CREATETABLE test3 LIKE test1; CREATETABLE test4 LIKE test1;

假如我们有这样一个包括多个表 join 的 SQL:

SELECT t1.id ,t1.a,t2.b,t3.c,t4.d FROM test1 t1 JOIN test2 t2 ON t1.a=t2.a JOIN test3 t3 ON t1.b=t3.b AND t3.id <= 1000 JOIN test4 t4 ON t1.c=t4.c;

1.拆分 SQL

把多张表 join 的 SQL 拆解成多个 join 语句,在应用代码中进行组合。比如拆解成 2 个 SQL:

SELECT t1.id ,t1.a,t2.b,t3.c FROM test1 t1 JOIN test2 t2 ON t1.a=t2.a JOIN test3 t3 ON t1.b=t3.b; SELECT t1.id ,t1.a,t4.d FROM test1 t1 JOIN test4 t4 ON t1.c=t4.c;

在业务代码中对两个 SQL 结果进行组合。

2.使用临时表

在上面的优化中,我们使用了 SQL 拆分的方式。如果 test3 表的数据量比较大,比如有 100万。但 test3 表使用到的结果集只有 1000 条,可以使用临时表:

CREATE TEMPORARY TABLE temp_t3(id TINYINT PRIMARY KEY, b VARCHAR(20),INDEX(b))ENGINE=INNODB; SELECT t1.id ,t1.a,t2.b,t3.c FROM test1 t1 JOIN test2 t2 ON t1.a=t2.a JOIN temp_t3 t3 ON t1.b=t3.b; SELECT t1.id ,t1.a,t4.d FROM test1 t1 JOIN test4 t4 ON t1.c=t4.c;

3.使用冗余字段

比如我们把 test4 表的 d 字段冗余到 test1 表中,假定字段名叫 t4c,这样就可以减少一个 join(当然,这样违反范式了)。最后只用下面的 SQL 就可以了:

SELECT t1.id ,t1.a,t2.b,t3.c,t1.t4c FROM test1 t1 JOIN test2 t2 ON t1.a=t2.a JOIN test3 t3 ON t1.b=t3.b AND t3.id <= 1000;

这样需要先在 test1 表中增加新字段 t4c,然后把 t4c 字段的值从 test4 表中更新过去。

改造需要注意两点,一个是评估更新字段的开销,第二个是要注意数据一致性,每次更新 test4 表中的 d 字段时也需要同步更新 test1 表中的 t4c 字段。

4.用好索引

join 语句对索引的使用非常重要,我们要注意下面几点:

  • 驱动表(MySQL 会选择 where 语句筛选出记录少的表作为驱动表)和被驱动表的 join 列都应该有索引;

  • 如果 join 语句涉及表的多个列,可以考虑为这些列建一个复合索引,比如下面 SQL:

SELECT t1.id ,t1.a,t2.b,t3.c FROM test1 t1 JOIN test2 t2 ON t1.a = t2.a AND t1.b = t2.b AND t1.c = t2.c;
  • 避免索引失效,比如 = 两端数据类型不同、使用函数、表达式等情况要避免;

  • 优化 join 顺序,如果我们能确定哪个表做驱动表更合适,这时我们可以考虑使用 straight_join;

SELECT t1.id ,t1.a,t2.b,t3.c FROM test1 t1 straight_join test2 t2 ON t1.a = t2.a AND t1.b = t2.b AND t1.c = t2.c;
  • order by、limit 使用到的列尽量加上索引;

  • 通过执行计划查看索引使用情况。

5.修改查询语句

如果某一个 join 表只是判断数据行是否存在,不需要使用表里面的字段时,我们可以考虑使用 exists 或 in 语句进行优化。对于下面这个 SQL:

SELECT t1.id ,t1.a,t2.b,t3.c FROM test1 t1 JOIN test2 t2 ON t1.a=t2.a JOIN test3 t3 ON t1.b=t3.b AND t3.id <= 1000 JOIN test4 t4 ON t1.c=t4.c;

可以优化成如下 SQL:

SELECT t1.id ,t1.a,t2.b,t3.c FROM test1 t1 JOIN test2 t2 ON t1.a=t2.a JOIN test3 t3 ON t1.b=t3.b AND t3.id <= 1000 WHERE EXISTS(SELECT id FROM test4 t4 WHERE t4.d=t1.d);

6.减少结果集

减少结果集,也是一种优化手段:

  • 通过增加 where 条件来让驱动表结果集降到最小;

  • 限制返回给应用的数据量,比如对返回结果做分页;

  • 对于返回结果的列,如果不用则去掉,这样对 join_buffer 的使用也会有好处。

7.修改数据库配置

当然,也可以修改数据库一些配置,比如 join_buffer_size、tmp_table_size,增加 join_buffer 和临时表大小,但是数据库参数的修改影响范围太大了,尤其是对于老系统,坑很多,不好做影响分析,所以不建议使用。

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案
【点击此处即可/免费获取】​​​

8.引入大数据工具

如果 join 表的数据量都很大,我们也可以考虑引入大数据工具,比如 ETL、数据湖,将表数据抽取到数据仓库(比如 ClickHouse)中进行加工后把数据结果提供出来。当然,这样存在的问题是数据时效性低。

9.汇总表

如果查询时效性要求不高,可以通过定时任务把查询结果放到一张汇总表,查询的时候直接查询这张汇总表。也可以把结果放到缓存,从缓存中查询。

CREATE TABLE`test_join_result` ( `id`TINYINT(3) NOTNULLCOMMENT'主键ID', `a`VARCHAR(20) DEFAULTNULL, `b`VARCHAR(20) DEFAULTNULL, `c`VARCHAR(200) DEFAULTNULL, `d`TINYINT(3) DEFAULTNULL, `e`TINYINT(1) DEFAULTNULL, `create_time`TIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMPCOMMENT'创建时间', `update_time`TIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMPCOMMENT'更新时间', PRIMARY KEY (`id`) ) ENGINE=INNODBDEFAULTCHARSET=utf8 --定时任务执行下面 SQL insertinto test_join_result(id,a,b,c,d) SELECT t1.id ,t1.a,t2.b,t3.c,t4.d FROM test1 t1 JOIN test2 t2 ON t1.a=t2.a JOIN test3 t3 ON t1.b=t3.b AND t3.id <= 1000JOIN test4 t4 ON t1.c=t4.c;

最后,对于新系统、新代码,使用多表 join 的情况比较少,因为开发规范一般不允许这样做。但是老系统或者做过数据库迁移的系统,可能会遇到这种情况。要多个因素综合考虑再下手优化。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/27 9:51:34

621-6550电源输出模块

621-6550 电源输出模块简介&#xff1a; 621-6550 是工业控制系统中使用的电源输出模块 主要用于为系统内其他模块或现场设备提供稳定电源 可将系统电源进行分配并输出到不同负载 适用于机架式或分布式控制系统结构 与控制器、电源输入模块配合使用 输出电压稳定&#xff…

作者头像 李华
网站建设 2026/3/28 6:39:53

621-9000逻辑控制器模块

621-9000 逻辑控制器模块简介&#xff1a; 621-9000 是工业自动化系统中的逻辑控制器模块 主要负责系统控制逻辑的运算与处理 可根据输入信号执行预设的控制程序 支持顺序控制、联锁控制等常见控制方式 用于协调各类输入、输出模块的工作 可作为控制系统的核心处理单元使用…

作者头像 李华
网站建设 2026/3/25 9:50:59

SpringAi-mcp高德

1.创建key 进入高德官网注册&#xff0c;创建key https://console.amap.com/dev/id/phone(官网) 2.编写yml文件 引入自己的key #高德的key AMAP-KEY: #自己的key,复制上 3.创建工具类 3.1AmapService package com.jiazhong.mingxing.ai.siliconflow.mcp.glm.service;im…

作者头像 李华
网站建设 2026/3/25 20:25:17

PDF转存CKEDITOR时文字重叠如何避免?

【穷学生の逆袭】99元预算搞定Word粘贴公式渲染的CMS升级方案 &#xff08;附JSP后端Vue2代码接单群彩蛋&#x1f389;&#xff09; 一、需求拆解与白嫖策略 作为川软大三狗&#xff0c;面对导师的"Word粘贴全家桶"需求&#xff0c;我摸了摸仅剩的99元&#xff0c;…

作者头像 李华
网站建设 2026/3/26 9:53:49

2026必备!专科生毕业论文AI论文工具TOP8测评

2026必备&#xff01;专科生毕业论文AI论文工具TOP8测评 2026年专科生论文写作工具测评&#xff1a;为何需要这份榜单&#xff1f; 随着AI技术在教育领域的不断渗透&#xff0c;越来越多的专科生开始借助智能工具提升论文写作效率。然而&#xff0c;面对市场上五花八门的AI论…

作者头像 李华
网站建设 2026/3/27 1:52:18

粘贴WORD到CKEDITOR时表格变形如何处理?

军工级富文本内容迁移解决方案技术评估日志 2023年X月X日 于长沙研发中心 一、需求拆解与技术调研 1.1 核心需求矩阵 需求类型技术指标信创要求Word粘贴保留形状/表格/公式等复杂样式&#xff0c;图片自动转存OSS支持银河麒麟V10飞腾FT-2000环境文档导入支持Office/PDF格式解…

作者头像 李华