MySql语法(5)—join连接

1. Join语法

实例表表A和表B.png
  1. 内连接:只返回满足条件的数据

INNER JOIN语法:

select * from A INNER JOIN B ON A.A1=B.B1

返回的数据是:

image.png
  1. 外连接:外连接又分为左外连接和右外连接,选择某个表为基表,基表数据全显示。

左外连接语法

select * from A LEFT JOIN B ON A.A1=B.B1;

上面的语句最终得到的连接顺序:AB,然后以左表为基表。只返回满足左表的数据,若右表无数据,那么返回null。

左外连接.png

右外连接语法:

select * from A RIGHT JOIN B ON A.A1=B.B1;

上面的语句最终得到的连接顺序:AB,然后以右边为基表。值返回满足右边的数据,若左边无数据,那么返回null。

右外连接.png

2. Join连接的算法

Mysql的联表算法是基于嵌套循环算法(nested-loop algorithm)而衍生出来的一系列算法,根据不同条件而选用不同的算法

2.1 使用索引

驱动表(小表)循环拿出一条数据,那么去被驱动表(大表)中进行索引查询。

例:t_img_log表为大表,t_req_log表为小表。

EXPLAIN SELECT * from t_img_log c1 INNER JOIN t_req_log c2 ON c1.req_id=c2.req_id

+----+-------------+-------+------------+------+---------------+-----------+---------+-----------------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key       | key_len | ref             | rows | filtered | Extra                 |
+----+-------------+-------+------------+------+---------------+-----------+---------+-----------------+------+----------+-----------------------+
|  1 | SIMPLE      | c2    | NULL       | ALL  | index_id      | NULL      | NULL    | NULL            | 1861 |   100.00 | NULL                  |
|  1 | SIMPLE      | c1    | NULL       | ref  | key_index     | key_index | 153     | exam.c2.req_key |    1 |   100.00 | Using index condition |
+----+-------------+-------+------------+------+---------------+-----------+---------+-----------------+------+----------+-----------------------+

2.2 未使用索引

在未使用索引的情况下,有Simple Nested-Loop joinBlock Nested-Loop join 两种算法;

Simple Nested-Loop join 简单嵌套算法

一次从一个循环中的第一个表中读取行,并将每行传递到一个嵌套循环中,该循环处理联接中的下一个表。重复此过程的次数与要连接的表的次数相同。

这种算法简单粗暴(n*m),但毫无性能可言。mysql不会采用这种算法。

Block Nested-Loop 缓存块嵌套算法

一次性缓存多条驱动表的数据到join buffer(缓存块),然后拿join buffer里的数据批量和被驱动表的数据进行匹配。

例如:驱动表结果集是100行,使用NLJ算法需要扫描被驱动表100次,如果使用BLJ算法,先把驱动表每次读取10行记录放到join buffer,然后在被驱动表中直接匹配这10行记录,内存循环就可以和这10行匹配,这样就需要匹配10次,对被驱动表的扫描减少了9/10。索引BNJ算法能够显著减少内层循环表扫描的次数。

被驱动表上无索引且被驱动表在where过滤条件上也没索引时,常常会借助这种算法来完成联表。

mysql> show index from article;
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table   | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| article |          0 | PRIMARY  |            1 | id          | A         |     4170338 | NULL     | NULL   |      | BTREE      |         |               |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)
 
mysql> show index from user;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| user  |          0 | PRIMARY  |            1 | id          | A         |      261958 | NULL     | NULL   |      | BTREE      |         |               |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)
 
mysql> explain select sql_no_cache  * from user  join article on(user.name = article.name) where article.name like 'user_%';
+----+-------------+---------+------------+------+---------------+------+---------+------+---------+----------+----------------------------------------------------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows    | filtered | Extra                                              |
+----+-------------+---------+------------+------+---------------+------+---------+------+---------+----------+----------------------------------------------------+
|  1 | SIMPLE      | user    | NULL       | ALL  | NULL          | NULL | NULL    | NULL |  261958 |   100.00 | NULL                                               |
|  1 | SIMPLE      | article | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 4170338 |     1.11 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+---------+------------+------+---------------+------+---------+------+---------+----------+----------------------------------------------------+
2 rows in set (0.00 sec)

Index Nested-Loop

索引嵌套循环,是基于被驱动表的索引进行连接的算法;驱动表的记录逐条与被驱动表进行匹配,避免和被驱动表的每条记录进行比较,减少了对驱动表的匹配次数。

2.3 join_buffer_size的设置

join_buffer_size即join连接的缓存块。

相关的命令:

# join连接的缓存块
SELECT @@join_buffer_size;
# sort排序的缓存块
SELECT @@sort_buffer_size;

如果应用中,很少出现join语句,则可以不用太在乎join_buffer_size参数的设置大小。如果join语句不是很少的话,个人建议可以适当增大join_buffer_size到1MB左右,如果内存充足可以设置为2MB...

MySQL使用Join Buffer有以下要点:

  1. join_buffer_size变量决定buffer大小。
  2. 只有在join类型为all, index, range的时候才可以使用join buffer。
  3. 能够被buffer的每一个join都会分配一个buffer, 也就是说一个query最终可能会使用多个join buffer。
  4. 第一个nonconst table不会分配join buffer, 即便其扫描类型是all或者index。
  5. 在join之前就会分配join buffer, 在query执行完毕即释放。
  6. join buffer中只会保存参与join的列, 并非整个数据行。

3. 规范:不推荐使用复杂的多表查询

阿里巴巴开发手册.png

在《高性能mysql第三版》第6.3章查询性能优化中是这样描述的:根据实际情况,看看是否有必要将一个复杂的查询分解成多个简单的查询,并不一定将所有的工作全部交给数据库(Join拆解的核心就是利用In关键字)。

mysql连接和断开都是很轻量级,返回一个小的查询结果方面很高效。所以运行多个小查询不是存在性能瓶颈,数据库的优化器性能比较差,涉及到多个表的查询,往往得不到很好的查询计划。

而将join拆分为多个单表连接优点:

  • 利于缓存:应用系统可以方便的缓存单笔查询对应的结果;
  • 减少竞争:将查询分解后,执行单个查询可以减少锁的竞争;
  • 易于扩展:更加容易对数据库进行拆分;
  • 性能提升:使用in()来代替关联查询,可以让mysql按照ID顺序进行查询,这可能比随机的关联查询更加高效;
  • 减少冗余:在应用层做关联查询,意味着某些记录应用只需要查询一次,而在数据库中做关联查询,则可能需要重复地访问一部分数据。

在很多场景下,通过重构查询将关联放到应用程序中将会更加高效,这样的场景有很多,比如:当应用能够方便的缓存当个查询的结果的时候、当可以将数据分布到不同的mysql服务器上的时候、当能够使用IN()的方式代替关联查询的时候、当查询中使用的是同一个数据表的时候。

join和in的区别

Mysql优化器会将in()中子查询优化为join方式。所以当相同的select结果下,in的效率会比join效率低。因为in子查询每次执行内部查询的时候都必须重新构造一个JOIN结构,并且还需要完成析构过程。

历史文章

mybatis&&数据库优化&&缓存目录

JAVA && Spring && SpringBoot2.x — 学习目录

文章参考

MySQL性能优化之Block Nested-Loop Join(BNL)

解析SQL中内连接、外连接、完全连接、笛卡尔积关系

阿里规定超过三张表禁止join,为啥?

mysql子查询(in)的实现

神奇的 SQL 之 联表细节 → MySQL JOIN 的执行过程(一)

神奇的 SQL 之 联表细节 → MySQL JOIN 的执行过程(二)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352