对于单条语句,explain看下key,加个索引

多个条件,加复合索引

where a = ? order by b 加(a,b)的复合索引

上面都是比较基本的,这篇我们分析一些复杂的情况——join的算法

如下两张表做join

10w            100w
tb R tb S
r1 s1
r2 s2
r3 s3
... ...
rN sN

Ⅰ、nested_loop join

1.1、simple nested_loop join 这个在数据库中永远不会使用

For each row r in R do
For each row s in S do
If r and s satisfy the join condition
Then output the tuple <r,s>



扫描成本 O(Rn*Sn),也就是笛卡儿积

1.2、index nested_loop join 最推荐

For each row r in R do
lookup in S index
if found s == row
Then output the tuple<r,s>

扫描成本 O(Rn)

优化器倾向于使用小表做驱动表,内表上创建索引即可

select * from a,b where a.x=b.y

a中的每条记录,去b上面的index(y)中去找这个x的记录,a表和b表,哪个是R,哪个是S

R:驱动表(外表) S:内表

对于inner join,a b 和 b a join出来结果一样,用的索引来查询,100w和1000w扫描成本都一样,都是外表的行数,所以只要驱动表越小,优化器就倾向于用它做驱动表

对于left join,左表要全表扫描所有记录,所以一定是驱动表

比如,一列10w行,另一列100w行,优化器会喜欢把10w的这一列做外表,然后在100w的列上建索引,外表只要扫描10w次,假设100w记录索引B+ tree高度是3,那一共就是10w * 3次io操作,反过来,就是100w * 3次,所以,mysql只要两张表关联,非常建议两张表上一定要有索引,索引应该创建在S表上,也就是大表,如果索引加反了,这时候100w的表就成了驱动表

但是线上肯定不会直接两张表关联,where后面还有很多过滤条件,优化器会把这些条件考虑进去,过滤掉这些条件,看哪张表示小表就是驱动表,但是索引有倾斜,优化器在选择上可能会出错

1.3 block nested_loop join 两张表上没有索引的时候才会使用的算法

用来优化simle nested_loop join,减少内部表的扫描次数

For each tuple r in R do
store used columns as p from R in join buffer
For each tuple s in S do
if p and s satisfy the join condition
Then output the tuple <p,s>



加一个内存,join_buffer_size变量,空间换时间,这个变量决定了Join Buffer的大小

Join Buffer可被用于联接是ALL,index,range的类型

Join Buffer只存储需要进行查询操作的相关列数据(要关联的列),而不是整行的记录(千万要记住),所以总的来说还是蛮高效的,

扫描成本呢?和simple一样

Table R join buffer Table S

将多条R中的记录一下子放到join buffer中,join buffer 一次性和Table S中每条记录进行比较,这样来减少内表的扫描次数,假设join buffer足够大,大到能把驱动表中所有记录cache起来,那这样就只要扫一次内表

举例:

A    B
1 1
2 2
3 3
4
外 内 SNLP BNLP
外表扫描次数 1 1
内表扫描次数 3 1
比较次数 12 12

综上:比较次数是节省不下来的

如果是百万级别,mysql勉强可以跑出来结果,调优的话,不谈索引,我们就要调大join_buffer_size这个参数,默认256k,如果这个列是8个字节,256k显然存不下太多记录,这个参数可以调很大,但是不要过分了,不然机器内存不够,数据库挂了就不好了,一般来说1g最大了

join_buffer_size是私有的,每个线程可以用的内存大小

网上有个特别错误的观点,mysql加速join查询,加大join_buffer_size。这个是两张表关联没有索引用这个方法才有用,你有了索引,再怎么加大这个参数也没用

本身也不太建议用这个算法,除非某些特殊的查询用不到索引,或者当时没建索引,这是个临时处理方法,比较次数是笛卡儿积啊 我的天那,如何把这个比较次数降下来呢?

tips:

1、不知道哪个表是驱动表,那就简单点,两个关联的表的相关列都加索引,让优化器自己选择

2、oracle中百万级别的表进行关联,用index nested_loop join,千万级以上用hash join这个说法怎么看?心法口诀是什么

3、join如果有索引,为什么千万级别的join不行?这句话是错的,是可以的,只是速度很慢罢了,主要就是因为要回表

select max(l_extendedprice) from orders,lineitem
where o_orderdate betweent '1995-01-01' and '1995-01-31' and
l_orderkey=o_orderkey;

join的列不是主键,这种类型的查询就是基于索引的,join不太适用的场景

看一个问题

(root@localhost) [dbt3]> explain select * from orders where o_orderdate > '1997-01-01';
+----+-------------+--------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| 1 | SIMPLE | orders | NULL | ALL | i_o_orderdate | NULL | NULL | NULL | 1483643 | 50.00 | Using where |
+----+-------------+--------+------------+------+---------------+------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.00 sec) (root@localhost) [dbt3]> explain select * from orders where o_orderdate > '1999-01-01';
+----+-------------+--------+------------+-------+---------------+---------------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+-------+---------------+---------------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | orders | NULL | range | i_o_orderdate | i_o_orderdate | 4 | NULL | 1 | 100.00 | Using index condition |
+----+-------------+--------+------------+-------+---------------+---------------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)

为什么第一条sql没走索引第二条走了呢?

这不是优化器不稳定,这个索引是一个二级索引,现在select * 所以要回表,回表对于我们这个sql来说,假设通过索引定位到回表的记录一共是50w条(表一共是150w条记录,每条记录大小是100字节),那就要回表50w次,大约是50w次io(看B+ tree 高度,这里假设高度就是1)

如果不走索引,直接扫150w行主键一共需要150w/(16k/100)次io(每行大小100字节,一个页就是16k/100条记录,150w除以这个值,就是一共的记录数),到这里看基本上就是1w次io,这就是优化器没走索引的原因,而且,回表用的io是随机的,扫主键用的是顺序io,后者比前者最起码快十倍

另外,这个选择肯定是基于cost的,但不要追究cost,官方没有说明cost是怎么计算的,通过源码可以看出一点,但是5.7版本又不一样了

那针对这种情况我们应该如何优化呢?——MRR(5.7之后才有,和oracle一样)

(root@localhost) [dbt3]> explain select /* +MRR(orders) */ * from orders where o_orderdate > '1998-01-01';
+----+-------------+--------+------------+-------+---------------+---------------+---------+------+--------+----------+----------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+-------+---------------+---------------+---------+------+--------+----------+----------------------------------+
| 1 | SIMPLE | orders | NULL | range | i_o_orderdate | i_o_orderdate | 4 | NULL | 317654 | 100.00 | Using index condition; Using MRR |
+----+-------------+--------+------------+-------+---------------+---------------+---------+------+--------+----------+----------------------------------+
1 row in set, 1 warning (0.22 sec)

MRR:Multi-Range-Read:多范围的read,空间换时间,解决回表的性能问题

随机io回表,不能保证key和pk都是顺序的,对于二级索引来说它的key是排序了,但是不能保证里面保存的pk也排序了

所以现在弄了个内存,把这个二级索引里面的pk都放进去,等放不下了,去sort一把,然后再去回表,这时候io就比较顺序了,这样通过随机转顺序做了个优化



Ⅱ、classic hash join

把外表中的数据放到一个内存中,放进去之后不是直接去和内表比较,对内存中的列创建了一个hash表,然后再扫描内表,内表中的每条记录去探测hash表,通常查一个记录只要探测一次

A    B
1 1
2 2
3 3
4

外表扫1次,内表扫1次,比较次数是4(S表去hash表中探测),走索引的话也是1 1 4,看起来哈希和索引看起来成本一样,hash不用创建索引,不用回表

hash join 就不存在回表的问题,还可以用来做并发,如果join要回表,用基于索引的方式做join算法效率比较差,对于oracle的话,hash join就会好非常多

hash join有个缺点是只支持等值的查询,A.x=B.y >= 这种就歇菜了,不过通常join也是等值查询

听说8.0会支持

Ⅲ、batched key access join(5.6开始支持)

用来解决如果关联的列是二级索引,对要回表的列先cache起来,排序,再回表,性能会比较好一点

bka join调的是mrr接口,但是现在这个算法有bug,默认是永远不启动的,要启用得写hint,这是mysql现在比较大的问题

上面讲了一堆mysql的不是,join算法的痤,上面这些多少w行join,线上业务会用到这些吗?我们线上都是简单查询,这些都是很复杂的查询了,233333

错误做法:有人说业务层做join,这样不会比在数据库层做快

join算法分析的更多相关文章

  1. MySQL入门,了解下、

    本人菜鸡一个,一份简单MySQL笔记送给大家,希望大家喜欢.(●'◡'●) Ⅰ. 数据备份与导入导出 1.1.备份基本概念介绍 1.2.mysqldump详解 1.3.mydumper浅析 1.4.M ...

  2. MySQL优化小结

    数据库的配置是基础.SQL优化最重要(贯穿始终,每日必做),由图可知,越往上优化的面越小,最基本的SQL优化是最重要的,往上各个参数也没太多调的,也不可能说调一个innodb参数性能就会好多少,而动不 ...

  3. MySQL Nested-Loop Join算法学习

    不知不觉的玩了两年多的MySQL,发现很多人都说MySQL对比Oracle来说,优化器做的比较差,其实某种程度上来说确实是这样,但是毕竟MySQL才到5.7版本,Oracle都已经发展到12c了,今天 ...

  4. BigData – Join中竟然也有谓词下推!?

    本文由  网易云发布. 在之前的文章中简要介绍了Join在大数据领域中的使用背景以及常用的几种算法-broadcast hash join .shuffle hash join以及 sort merg ...

  5. SQL Server-聚焦IN VS EXISTS VS JOIN性能分析(十九)

    前言 本节我们开始讲讲这一系列性能比较的终极篇IN VS EXISTS VS JOIN的性能分析,前面系列有人一直在说场景不够,这里我们结合查询索引列.非索引列.查询小表.查询大表来综合分析,简短的内 ...

  6. SQL Server-聚焦NOT IN VS NOT EXISTS VS LEFT JOIN...IS NULL性能分析(十八)

    前言 本节我们来综合比较NOT IN VS NOT EXISTS VS LEFT JOIN...IS NULL的性能,简短的内容,深入的理解,Always to review the basics. ...

  7. Nested Loops join时显示no join predicate原因分析以及解决办法

    本文出处:http://www.cnblogs.com/wy123/p/6238844.html 最近遇到一个存储过程在某些特殊的情况下,效率极其低效, 至于底下到什么程度我现在都没有一个确切的数据, ...

  8. c# Enumerable中Aggregate和Join的使用

    参考页面: http://www.yuanjiaocheng.net/ASPNET-CORE/asp.net-core-environment.html http://www.yuanjiaochen ...

  9. 超详细mysql left join,right join,inner join用法分析

    下面是例子分析表A记录如下: aID        aNum 1           a20050111 2           a20050112 3           a20050113 4   ...

随机推荐

  1. 积木式编程——自制app点灯

    技术:51单片机.串口.蓝牙.积木式编程   概述 这里简单入门了积木式编程,做了个简易APP用来开灯关灯,当然你可以继续添加内容,可以做智能家居.遥控汽车和环境监控等等. 详细 代码下载:http: ...

  2. Faiss安装

    一.上策:使用现成的faiss 找到别人(同事或同学)的python目录,找到faiss文件夹,复制到本地,并将其添加到PYTHONPATH下. 二.中策:使用anaconda anaconda上面的 ...

  3. Ubuntu16.04安装串口调试工具gtkterm

    gtkterm是一个用GTK+写的串口终端. 安装:sudo apt install gtkterm 配置文件的位置为:~/.gtktermrc [default] port = /dev/ttyUS ...

  4. Linux内核剖析(二)Linux内核绪论

    什么是内核 内核是操作系统最基本的部分.它是为众多应用程序提供对计算机硬件的安全访问的一部分软件,这种访问是有限的,并且内核决定一个程序在什么时候对某部分硬件操作多长时间.内核的分类可分为单内核和双内 ...

  5. Content of "Essential Software Test Design"

    Content of "Essential Software Test Design" 2015-11-16 PART I 7 TEST DESIGN TECHNIQUES: AN ...

  6. 科普知识普及 - 桥接VS中继

    首先要说明一个很多人理解的误区,中继比桥接好用,真的是这么回事么? 答案是否定的. 我们在说桥接和中继的时候我们要先了解,桥接和中继的工作原理.还有一个问题,估计很多人都想不明白:为什么中继搜到的信号 ...

  7. java 中 热部署与卸载关系

    今天发现早年在大象笔记中写的一篇笔记,之前放在ijavaboy上的,现在它已经访问不了了.前几天又有同事在讨论这个问题.这里拿来分享一下. 在web应用开发或者游戏服务器开发的过程中,我们时时刻刻都在 ...

  8. 在windows 上自动重启 tomcat 的方法

    在windows 上自动重启 tomcat 的方法 实现思路: Windows 上监控tomcat 进程并且自动重启的脚本 一类是 定时重启 tomcat 一类是 监控并重启 写一个守护tomcat进 ...

  9. 【FM】算法

    https://www.cnblogs.com/AndyJee/p/7879765.html

  10. php.ini 开发和线上配置的差异

    比对了一下php自带的php.ini-development和php.ini-production,备忘. display_errors = Ondisplay_startup_errors = On ...