news 2026/4/18 7:51:04

面试官: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/4/18 7:19:19

【Vue】脚手架 v-html v-text v-bind v-on v-show v-if v-for v-model

文章目录 Ⅰ. 脚手架一、Vue开发方式1. 传统开发模式2. 工程化开发模式 二、准备工程化环境1. 安装 Nodejs2. 安装 yarn 或 pnpm 三、创建Vue工程化项目四、认识脚手架目录及文件五、分析3个入口文件的关系六、Vue单文件七、setup简写 插值表达式 响应式1. 传统写法2. 现代写…

作者头像 李华
网站建设 2026/4/16 14:42:27

经典智能体范式

一、引言&#xff1a;为什么需要智能体 1.1 大模型的局限 之前的GPT-4等大模型停留在聊天框中且存在一些问题&#xff1a;一是“幻觉”&#xff0c;可能生成错误信息或不存在的内容&#xff1b;二是时效性不足&#xff0c;对未训练的新内容缺乏准确认知&#xff1b;三是复杂任…

作者头像 李华
网站建设 2026/4/14 15:21:12

瀚高数据库常用驱动连接字符串

文章目录 文档用途详细信息 文档用途 本文列出了瀚高数据库常用的驱动连接字符串以便在测试和适配工作中使用驱动连接数据库。 详细信息 1、常见驱动字符串示例 以金蝶中间件为例 驱动&#xff1a;com.highgo.jdbc.Driver URL: jdbc:highgo://ip:端口号/数据库名 jdbc:high…

作者头像 李华
网站建设 2026/4/17 21:48:33

戴西软件发布3DViz设计与仿真数据轻量化平台

戴西软件发布3DViz设计与仿真数据轻量化平台以Web三维协同重塑工业研发数据的使用方式。近日&#xff0c;戴西软件正式发布3DViz设计与仿真数据轻量化软件。作为iDWS智能化研发平台的重要组成组件&#xff0c;3DViz面向工业研发中高价值的 CAD设计数据与CAE仿真结果&#xff0c…

作者头像 李华
网站建设 2026/4/13 19:40:13

python智能水务巡检预警应急调度与决策系统的设计与实现

目录智能水务巡检预警应急调度与决策系统的设计与实现摘要项目技术支持可定制开发之功能亮点源码获取详细视频演示 &#xff1a;文章底部获取博主联系方式&#xff01;同行可合作智能水务巡检预警应急调度与决策系统的设计与实现摘要 该系统基于Python技术栈&#xff0c;结合物…

作者头像 李华