MySQL之join查询优化方式

 更新时间:2023年03月12日 15:49:49   作者:布道  
这篇文章主要介绍了MySQL之join查询优化方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

MySQL join查询优化

在日常的开发中,我们经常遇到这样情况:select * from TableA  inner join TableB...它响应速度一直很快的,随着数据的增长,突然有一天开始很慢了。那该怎么破?

对,驱动表是突破口,

1. 那什么是驱动表呢?

  • 指定了联接条件时,满足查询条件的记录行数少的表为驱动表
  • 未指定联接条件时,行数少的表为驱动表(Important!)

如果你搞不清楚该让谁做驱动表、谁 join 谁,就别指定谁 left/right join 谁了,请交给 MySQL优化器 运行时决定吧。

2. 复杂的sql怎么识别驱动表呢?

按经验谈,使用EXPLAIN, 第一行出现的表就是驱动表。

3. 关联查询原理是怎样的?

MySQL 表关联的算法是 Nest Loop Join,是通过驱动表的结果集作为循环基础数据,然后一条一条地通过该结果集中的数据作为过滤条件到下一个表中查询数据,然后合并结果。

//例: user表10000条数据,class表20条数据
select * from user u left join class c u.userid=c.userid

上面sql的后果就是需要用user表循环10000次才能查询出来,而如果用class表驱动user表则只需要循环20次就能查询出来。

4. 该如如何优化?

优化的目标是尽可能减少JOIN中Nested Loop的循环次数,以此保证:永远用小结果集驱动大结果集。

排序的字段也有影响,有条原则:对驱动表可以直接排序,对非驱动表(的字段排序)需要对循环查询的合并结果(临时表)进行排序!

5. 实例

explain select * from user u left join class c on u.userid=c.userid INNER JOIN subject s on c.subjectId=s.id 
 WHERE 1=1 ORDER BY u.create_time DESC limit 0,10

够复杂吧。假如,user表有千万级记录,class表要少得多,从执行计划的得知驱动表(数据到千万级)。由于动用了“LEFT JOIN”,所以相当于已经指定了驱动表。

如何优化?

//优化第一步:LEFT JOIN改为JOIN,对,直接 join!
explain select * from user u join class c on u.userid=c.userid INNER JOIN subject s on c.subjectId=s.id 
 WHERE 1=1 ORDER BY u.create_time DESC limit 0,10
//优化第二步:从上面执行计划得知, 有Using temporary(临时表);Using filesort,解决方法是调整排序字段(借助前面讲过排序的原则)
explain select * from user u join class c on u.userid=c.userid INNER JOIN subject s on c.subjectId=s.id 
 WHERE 1=1 ORDER BY c.id DESC limit 0,10

总之,sql优化中explain工具是非常重要的武器。

MySQL优化(关联查询优化)

准备数据

#分类
CREATE TABLE IF NOT EXISTS `class` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`card` INT(10) UNSIGNED NOT NULL,
PRIMARY KEY (`id`)
);
#图书
CREATE TABLE IF NOT EXISTS `book` (
`bookid` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`card` INT(10) UNSIGNED NOT NULL,
PRIMARY KEY (`bookid`)
);
 
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20)));
 
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));
INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));

left join左外连接

看这个分析结果发现:在 class 表上添加的索引起的作用不大。

结论: 

- **小表驱动大表**

  • - 小表:相对来说记录较少的表
  • - 大表:相对来说记录较多的表

- 驱动方式识别

  • left join:左边驱动右边(此时把小表放在左边)
  • right join:右边驱动左边(此时把小表放在右边)

- 加索引的方式:通常建议在大表(被驱动)的表加索引,效率提升更明显。

- 原因:

  • 原因1:被驱动表加了索引之后,收益更大。从 ALL -> ref
  • 原因2:外连接首先读取驱动表的全部数据,被驱动只读取满足连接条件的数据。

inner join:MySQL会自动根据表中的数据选择驱动表

小结:

- 保证被驱动表的 join 字段被索引。join 字段就是作为连接条件的字段。

- left join 时,选择小表作为驱动表(放左边),大表作为被驱动表(放右边)

- inner join 时,mysql 会自动将小结果集的表选为驱动表。

- 子查询尽量不要放在被驱动表,衍生表建不了索引

- 能够直接多表关联的尽量直接关联,不用子查询

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • MySQL MVVC多版本并发控制的实现详解

    MySQL MVVC多版本并发控制的实现详解

    在多版本并发控制中,为了保证数据操作在多线程过程中,保证事务隔离的机制,降低锁竞争的压力,保证较高的并发量。在每开启一个事务时,会生成一个事务的版本号,被操作的数据会生成一条新的数据行
    2022-08-08
  • MySQL使用innobackupex备份连接服务器失败的解决方法

    MySQL使用innobackupex备份连接服务器失败的解决方法

    这篇文章主要为大家详细介绍了MySQL使用innobackupex备份连接服务器失败的解决方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-02-02
  • mysql 8.0.14 安装配置方法图文教程(通用)

    mysql 8.0.14 安装配置方法图文教程(通用)

    这篇文章主要为大家详细介绍了mysql 8.0.14 安装配置方法图文教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-02-02
  • SQL查询表字段信息详细图文教程

    SQL查询表字段信息详细图文教程

    最近工作用到SQL语句查询表中所有字段的名称,下面这篇文章主要给大家介绍了关于SQL查询表字段信息的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-03-03
  • mysql 8.0.11安装教程图文解说

    mysql 8.0.11安装教程图文解说

    本文通过图文并茂的形式给大家介绍了mysql 8.0.11安装教程,非常不错,具有一定的参考借鉴价值 ,需要的朋友可以参考下
    2019-04-04
  • mysql Innodb表空间卸载、迁移、装载的使用方法

    mysql Innodb表空间卸载、迁移、装载的使用方法

    从MySQL的Innodb特性中我们知道,Inndob的表空间有共享和独享的特点,如果是共享的。则默认会把表空间存放在一个文件中(ibdata1),当开启独享表空间参数Innodb_file_per_table时,会为每个Innodb表创建一个.ibd的文件。文章讨论在独享表空间卸载、装载、迁移Innodb表的情况
    2013-11-11
  • MySQL SQL优化教程之in和range查询

    MySQL SQL优化教程之in和range查询

    这篇文章主要给大家介绍了关于MySQL SQL优化教程之in和range查询的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • 详解Mysql中的视图

    详解Mysql中的视图

    mysql中的视图,视图与表有很多相似的地方,视图也是由若干个字段以及若干条记录构成,视图也可以作为select语句的数据源
    2017-04-04
  • redhat7.1 安装mysql 5.7.10步骤详解(图文详解)

    redhat7.1 安装mysql 5.7.10步骤详解(图文详解)

    这篇文章主要介绍了redhat7.1 安装mysql 5.7.10的步骤详细介绍本文图文并茂给大家介绍的非常详细,需要的朋友可以参考下
    2016-11-11
  • 分析MySQL抛出异常的几种常见解决方式

    分析MySQL抛出异常的几种常见解决方式

    在本文中,总结了开发过程中最为常见的几种 MySQL 抛出的异常以及如何解决,包括高版本驱动的问题、时区配置问题、SSL 连接问题等。于我个人而言,这一篇足以解决目前项目中所有遇到的MySQL问题。同时,也希望本文能对 MySQL 数据库初学者有一定的引导入门作用。
    2021-05-05

最新评论