疑问

表:sl_sales_bill_head 订单抬头表 数据行:8474

表:sl_sales_bill          订单明细 数据行:8839

字段:SALES_BILL_NO 订单号

情况1

没有任何索引 sql语句

EXPLAIN select * from sl_sales_bill_copy1 lb
join sl_sales_bill_head_copy1 lh on lh.SALES_BILL_NO = lb.SALES_BILL_NO

lh为主表 lb为子表

改一下sql语句

EXPLAIN select * from  sl_sales_bill_head_copy1 lh
join sl_sales_bill_copy1 lb on lh.SALES_BILL_NO = lb.SALES_BILL_NO

疑问:为什么sql语句无论主表是哪个 lh都先执行

情况2

sl_sales_bill_head_copy1 的SALES_BILL_NO为主键索引

ALTER TABLE `sl_sales_bill_head_copy1` ADD PRIMARY KEY (`SALES_BILL_NO`) ;

sql语句1:

EXPLAIN select * from  sl_sales_bill_head_copy1 lh
join sl_sales_bill_copy1 lb on lh.SALES_BILL_NO = lb.SALES_BILL_NO

sql语句2:

EXPLAIN select * from   sl_sales_bill_copy1 lb
join sl_sales_bill_head_copy1 lh on lh.SALES_BILL_NO = lb.SALES_BILL_NO

疑问:为什么无论怎么通过sql语句改变主表 始终是lb先执行

情况3

lh.SALES_BILL_NO创建索引

ALTER TABLE `sl_sales_bill_head_copy1` ADD PRIMARY KEY (`SALES_BILL_NO`) ;
 EXPLAIN select * from   sl_sales_bill_copy1 lb
join sl_sales_bill_head_copy1 lh on lh.SALES_BILL_NO = lb.SALES_BILL_NO
where lb.SALES_BILL_NO='HP20190410000099'
 EXPLAIN select * from   sl_sales_bill_copy1 lb
join sl_sales_bill_head_copy1 lh on lh.SALES_BILL_NO = lb.SALES_BILL_NO
where lh.SALES_BILL_NO='HP20190410000099'

都会正常走索引 同时也是lh先执行

如果改为lb的其他字段

EXPLAIN select * from   sl_sales_bill_copy1 lb
join sl_sales_bill_head_copy1 lh on lh.SALES_BILL_NO = lb.SALES_BILL_NO
where lb.id='0001c3fd44454a65a4122b259283f979'

无索引情况

ID有索引情况

变成了lb先执行

情况4

sl_sales_bill_head_copy1 的SALES_BILL_NO为主键索引

ALTER TABLE `sl_sales_bill_head_copy1` ADD PRIMARY KEY (`SALES_BILL_NO`) ;

SQL语句

EXPLAIN select * from   sl_sales_bill_copy1 lb
join sl_sales_bill_head_copy1 lh on lh.SALES_BILL_NO = lb.SALES_BILL_NO

sql语句

EXPLAIN select * from   sl_sales_bill_copy1 lb
left join sl_sales_bill_head_copy1 lh on lh.SALES_BILL_NO = lb.SALES_BILL_NO

疑问:为什么left join没有走索引了

Join匹配原理

说明

mysql只支持一种算法Nested-Loop Join(嵌套循环链接),不像其他商业数据库可以支持哈希链接和合并连接,不过MySQL的Nested-Loop Join(嵌套循环链接)

Simple Nested-Loop

图片来源:InsideMySQ

R表为驱动表每扫描一行去S表找匹配的数据 这种算法是最耗时的 总扫描次数为驱动表行数*非驱动表行数

比如R表有200表数据 S表有100条 总扫描次数为200*100 可以看出这种算法效率最低

Index Nested-Loop Join

R表为驱动表每扫描一行 根据匹配条件通过索引去S表找 这种算法需要非驱动表有索引 一般我们on r.sid=s.id 索引时给非驱动表用的

比较高效

Block Nested-Loop Join

mysql 5.5对Simple Nested-Loop的优化  先扫描驱动表一定量(根据join_buffer_size来定) 放到join_buffer  然后遍历非驱动表 非驱动表每次匹配join_buffer里面的数据 减少扫描次数

比如我们的join_buffer最多只能存放r表3条数据  遍历R表 每遍历3条将数据放到join_buffer然后 然后再去遍历一次s表  每s表遍历一行跟join_buffer里面的数据进行匹配 遍历完成释放join_buffer 重复上面操作

在MySQL当中,我们可以通过参数join_buffer_size来设置join buffer的值,然后再进行操作。默认情况下join_buffer_size=256K

解决疑惑

情况1

lh数据条数8274  lb数据条数8721

疑问:为什么驱动表都是lh表

解答:mysqlsql优化器 默认会将小表作为驱动表

好处:

Block Nested-Loop Join算法

比如lh有4条数据 lb数据条数6 join_buffer是只能存放2条数据

计算规则为(驱动表遍历次数*驱动表行数)+(非驱动表遍历次数*非驱动表行数)=总遍历次数

我们将lb作为驱动表 扫描行数为(1*6)+(3*4)=18  总扫描行数

我们将lh作为驱动表 (1*4)+(2*6)=16 总扫描行数

可以发现小表作为驱动表扫描的行数更低

情况2:

lh数据条数8274  lb数据条数8721

疑问:为什么lh.SALES_BILL_NO为主键索引 驱动表始终是lb

解答:mysql优化器还是以小表为原则 如果大表关联关系有索引而小表没有则以有索引的表为驱动表

好处:

这里使用的Index Nested-Loop Join算法

如果使用lh驱动表 首先会遍历8274次  每次去lb去找 因为关联关系lb.SALES_BILL_NO没有做索引 所以非驱动表lb也会全表扫描 总扫描次数就变成8274*8721

如果使用lb为驱动表会遍历lb表每次通过SALES_BILL_NO去非驱动表lh找 因为lh做了索引 所以通过索引扫描一次就可以找到数据 总扫描次数:8274*1

情况3

lh数据条数8274  lb数据条数8721

疑问:为什么就lh.SALES_BILL_NO有主键索引 无论搜索条件是lb.SALES_BILL_NO还是lh.SALES_BILL_NO 都是lh先执行

解答:

因为on lb.SALES_BILL_NO=lh.SALES_BILL_NO  where lb.SALES_BILL_NO='HP20190410000099'

这个时候虽然lb.SALES_BILL_NO没有索引  但是关联查找为lb.SALES_BILL_NO=lh.SALES_BILL_NO  and lb.SALES_BILL_NO='HP20190410000099'

正常查找是lb全表扫描得到HP20190410000099然后去lh通过索引得到SALES_BILL_NO=lh.SALES_BILL_NO的数据

如果设置成lb.SALES_BILL_NO=lh.SALES_BILL_NO  and lh.SALES_BILL_NO='HP20190410000099' 得到结果相同  以小表为驱动表原则sql优化器会优化为类似这样的语句查找

EXPLAIN select * from   sl_sales_bill_copy1 lb
join sl_sales_bill_head_copy1 lh on lh.SALES_BILL_NO = lb.SALES_BILL_NO
where lb.id='0001c3fd44454a65a4122b259283f979'

lh.SALES_BILL_NO 有索引 然后lb.id无论有无索引都是 lb为驱动表 因为lb.id已经缩小了数据范围  小表原则 所以始终是lb为驱动表

情况4

因为left join相当于强制要求了lb为主表 虽然lh.SALES_BILL_NO有索引 但是join索引主要是给非驱动表用的  所以出现以上情况

join优化原则

尽量减少驱动表条数 非驱动表关联条件建立索引

虽然大部分会经过mysql优化器自动优化,复杂sql最好通过执行计划查看一下 是否有性能瓶颈

注意不要通过left join 影响sql优化器 将大表作为驱动表

记住join 索引只有在非驱动表上面才能体现作用

MySql join匹配原理的更多相关文章

  1. mysql join 底层原理

    你知道 Sql 中 left join 的底层原理吗? 2019-09-10阅读 7130 https://cloud.tencent.com/developer/column/2367   01.前 ...

  2. mysql join优化原理

    http://blog.itpub.net/22664653/viewspace-1692317/ http://itindex.net/detail/46772-%E4%BC%98%E5%8C%96 ...

  3. MySQL JOIN原理

    先看一下实验的两张表: 表comments,总行数28856 表comments_for,总行数57,comments_id是有索引的,ID列为主键. 以上两张表是我们测试的基础,然后看一下索引,co ...

  4. MySQL JOIN原理(转)

    先看一下实验的两张表: 表comments,总行数28856 表comments_for,总行数57,comments_id是有索引的,ID列为主键. 以上两张表是我们测试的基础,然后看一下索引,co ...

  5. mysql join 和left join 对于索引的问题

    今天遇到一个left join优化的问题,搞了一下午,中间查了不少资料,对MySQL的查询计划还有查询优化有了更进一步的了解,做一个简单的记录: select c.* from hotel_info_ ...

  6. MYSQL索引结构原理、性能分析与优化

    [转]MYSQL索引结构原理.性能分析与优化 第一部分:基础知识 索引 官方介绍索引是帮助MySQL高效获取数据的数据结构.笔者理解索引相当于一本书的目录,通过目录就知道要的资料在哪里, 不用一页一页 ...

  7. MySQL InnoDB 索引原理

    本文由  网易云发布. 作者:范鹏程,网易考拉海购 InnoDB是 MySQL最常用的存储引擎,了解InnoDB存储引擎的索引对于日常工作有很大的益处,索引的存在便是为了加速数据库行记录的检索.以下是 ...

  8. 数据库MySQL 之 索引原理与慢查询优化

    数据库MySQL 之 索引原理与慢查询优化 浏览目录 索引介绍方法类型 聚合索引辅助索引 测试索引 正确使用索引 组合索引 注意事项 查询计划 慢查询日志 大数据量分页优化 一.索引介绍方法类型 1. ...

  9. MySQL Optimization 优化原理

    MySQL Optimization 优化原理 MySQL逻辑架构 如果能在头脑中构建一幅MySQL各组件之间如何协同工作的架构图,有助于深入理解MySQL服务器.下图展示了MySQL的逻辑架构图. ...

随机推荐

  1. Objective-C 对象的类型与动态结合

    创建: 2018/01/21 更新: 2018/01/22 标题前增加 [Objective-C] 完成: 2018/01/24 更新: 2018/01/24 加红加粗属性方法的声明 [不直接获取内部 ...

  2. 【废弃】【WIP】JavaScript Object

    创建: 2017/11/03 废弃: 2019/02/19 重构此篇.原文归入废弃  增加[废弃中]标签与总体任务 结束: 2019/03/03 完成废弃, 删除[废弃中]标签, 添加[废弃]标签 T ...

  3. 栗染-Myeclispe连接SQL Server数据库

    第一步,在SQL server方面 这里是以身份验证登录. 这里我是建了一个hw的数据库,其他没啥说的. 第二步,最主要的一部分 因为第一次连接SQL Server数据库,所以就不知道还有这一步.不然 ...

  4. Linux安装FTP文档服务器

    1.检查是否安装 了vsftpd,如果未安装 则安装vsftpd. 1)查看系统中是否安装了vsftpd,可以通过执行命令 :rpm -qa | grep vsftpd 2)如果没有安装 vsftpd ...

  5. 自动构造词法分析器的步骤——正规式转换为最小化DFA

    正规式-->最小化DFA 1.先把正则式-->NFA(非确定有穷自动机) 涉及一系列分解规则 2.再把NFA通过"子集构造法"-->DFA 通过子集构造法将NFA ...

  6. linux学习之路1 Linux系统安装

    VMware workstation虚拟器 网上下载VMware workstation,然后安装任一系统的linux系统,不过选的系统一定要跟你下载好的linux镜像保持一致,博主装的是Red Ha ...

  7. java大数轻松过

    import java.util.Scanner; import java.math.BigInteger; public class Main { public static void main(S ...

  8. mongodb的安装以及客户端

    mongodb是一种非关系型的数据库,与传统的sql有很大的不同,但都是用于数据管理的,本人也是初学,很多地方都是模仿,在这里只是记录本人初次安装mongodb和客户端,记录一下安装的步骤,以便以后用 ...

  9. gdb如何保存和读取断点

    刚开始在linux下学编程使用gdb的同学可能会发现,每次用gdb设置断点调试程序,但下次打开的时候所有断点都没有了,很不方便.下面介绍保存和读取断点的方法. 1. 保存断点 先用info b 查看一 ...

  10. App保持登录状态的常用方法

    我们在使用App时,一次登录后App如果不主动退出登录或者清除数据,App会在很长一段时间内保持登录状态,或者让用户感觉到登录一次就不用每次都输入用户密码才能进行登录.银行.金融涉及到支付类的App一 ...