Join并行

Join并行

1. 多表join介绍

2. 多表Join的方式

  • 不使用Join buffer
  • 使用Join buffer

3. Join执行流程(老执行器)

1. 多表join介绍

JOIN子句用于根据两个或多个表之间的相关列来组合它们。 例如:

Orders:

OrderID

CustomerID

OrderDate

10308

2

1996-09-18

10309

37

1996-09-19

10310

77

1996-09-20

Customers:

CustomerID

CustomerName

ContactName

Country

1

Alfreds Futterkiste

Maria Anders

Germany

2

Ana Trujillo Emparedados y helados

Ana Trujillo

Mexico

3

Antonio Moreno Taquería

Antonio Moreno

Mexico

SELECT Orders.OrderID, Customers.CustomerName, Orders.OrderDate
FROM Orders
INNER JOIN Customers ON Orders.CustomerID=Customers.CustomerID;

OrderID

CustomerName

OrderDate

10308

Ana Trujillo Emparedados y helados

9/18/1996

10365

Antonio Moreno Taquería

11/27/1996

10383

Around the Horn

12/16/1996

10355

Around the Horn

11/15/1996

10278

Berglunds snabbköp

8/12/1996

2. 多表Join的方式

Hash join使用新执行器实现,在这里不做讨论

MySQL支持的都是Nested-Loop Join,以及它的变种。

不使用Join buffer

a) Simple Nested-Loop

对r表的每一行,完整扫描s表,根据r[i]-s[i]组成的行去判断是否满足条件,并返回满足条件的结果给客户端。

mysql> show create table t1;
+-------+----------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                   |
+-------+----------------------------------------------------------------------------------------------------------------+
| t1    | CREATE TABLE `t1` (`id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci |
+-------+----------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)mysql> show create table t3;
+-------+--------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                       |
+-------+--------------------------------------------------------------------------------------------------------------------+
| t3    | CREATE TABLE `t3` (`id` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci |
+-------+--------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)mysql> explain select /*+ NO_BNL() */ * from t1, t3 where t1.id = t3.id;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | t1    | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    2 |   100.00 | NULL        |
|  1 | SIMPLE      | t3    | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    2 |    50.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)

b) Index Nested-Loop

对r表的每一行,先根据连接条件去查询s表索引,然后回表查到匹配的数据,并返回满足条件的结果给客户端。

mysql> show create table t2;
+-------+---------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                          |
+-------+---------------------------------------------------------------------------------------------------------------------------------------+
| t2    | CREATE TABLE `t2` (`id` int(11) NOT NULL,KEY `index1` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci |
+-------+---------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)mysql> explain select * from t1, t2 where t1.id = t2.id;
+----+-------------+-------+------------+------+---------------+--------+---------+------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key    | key_len | ref        | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+--------+---------+------------+------+----------+-------------+
|  1 | SIMPLE      | t1    | NULL       | ALL  | NULL          | NULL   | NULL    | NULL       |    2 |   100.00 | NULL        |
|  1 | SIMPLE      | t2    | NULL       | ref  | index1        | index1 | 4       | test.t1.id |    1 |   100.00 | Using index |
+----+-------------+-------+------------+------+---------------+--------+---------+------------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)

使用Join buffer

a) Block Nested Loop

从r表读取一部分数据到join cache中,当r表数据读完或者join cache满后,做join操作。

JOIN_CACHE_BNL::join_matching_records(){do {//读取s表的每一行qep_tab->table()->file->position(qep_tab->table()->record[0]);//针对s的每一行,遍历join bufferfor(each record in join buffer) {get_record();rc = generate_full_extensions(get_curr_rec());//如果不符合条件,直接返回if (rc != NESTED_LOOP_OK) return rc;}} while(!(error = iterator->Read()))
}
mysql> explain select  * from t1, t3 where t1.id = t3.id;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                                              |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
|  1 | SIMPLE      | t1    | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    2 |   100.00 | NULL                                               |
|  1 | SIMPLE      | t3    | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    2 |    50.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
2 rows in set, 1 warning (0.00 sec)

b) Batched Key Access

从r表读取一部分数据到join cache中,s表中记录r表被连接的列的值作为索引,查询所有符合条件的索引,然后将这些符合条件的索引排序,然后统一回表查询记录。

其中,对于每一个cached record,都会有一个key,通过这个key去s表扫描所需的数据。

dsmrr_fill_buffer(){while((rowids_buf_cur < rowids_buf_end) &&!(res = h2->handler::multi_range_read_next(&range_info))){//下压的index条件if (h2->mrr_funcs.skip_index_tuple &&h2->mrr_funcs.skip_index_tuple(h2->mrr_iter, curr_range->ptr))continue;memcpy(rowids_buf_cur, h2->ref, h2->ref_length);}varlen_sort(rowids_buf, rowids_buf_cur, elem_size,[this](const uchar *a, const uchar *b) { return h->cmp_ref(a, b) < 0; });
}dsmrr_next(){do{if (rowids_buf_cur == rowids_buf_last) {dsmrr_fill_buffer();}// first matchif (h2->mrr_funcs.skip_record &&h2->mrr_funcs.skip_record(h2->mrr_iter, (char *)cur_range_info, rowid))continue;res = h->ha_rnd_pos(table->record[0], rowid);break;} while(true);
}JOIN_CACHE_BKA::join_matching_records(){while (!(error = file->ha_multi_range_read_next((char **)&rec_ptr))) {get_record_by_pos(rec_ptr);rc = generate_full_extensions(rec_ptr);if (rc != NESTED_LOOP_OK) return rc;}
}
mysql> show create table t1;
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                    |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------+
| t1    | CREATE TABLE `t1` (`f1` int(11) DEFAULT NULL,`f2` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)mysql> show create table t2;
+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                                                   |
+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| t2    | CREATE TABLE `t2` (`f1` int(11) NOT NULL,`f2` int(11) NOT NULL,`f3` char(200) DEFAULT NULL,KEY `f1` (`f1`,`f2`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci |
+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)mysql> explain SELECT /*+ BKA() */ t2.f1, t2.f2, t2.f3 FROM t1,t2 WHERE t1.f1=t2.f1 AND t2.f2 BETWEEN t1.f1 and t1.f2 and t2.f2 + 1 >= t1.f1 + 1;
+----+-------------+-------+------------+------+---------------+------+---------+-------------+------+----------+---------------------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref         | rows | filtered | Extra                                                         |
+----+-------------+-------+------------+------+---------------+------+---------+-------------+------+----------+---------------------------------------------------------------+
|  1 | SIMPLE      | t1    | NULL       | ALL  | NULL          | NULL | NULL    | NULL        |    3 |   100.00 | Using where                                                   |
|  1 | SIMPLE      | t2    | NULL       | ref  | f1            | f1   | 4       | test1.t1.f1 |    7 |    11.11 | Using index condition; Using join buffer (Batched Key Access) |
+----+-------------+-------+------------+------+---------------+------+---------+-------------+------+----------+---------------------------------------------------------------+
2 rows in set, 1 warning (0.00 sec)

c) Batched Key Access(unique)

与Batched Key Access不同的是,r中的列是s的唯一索引,在r记录写入join cache的时候,会记录一个key的hash table,仅针对不同的key去s表中查询。(疑问,为什么只有unique的时候才能用这种方式?不是unique的话,s表中可能会扫描出多条数据,也可以用这种方式去处理,减少s表的重复扫描)。

JOIN_CACHE_BKA_UNIQUE::join_matching_records(){while (!(error = file->ha_multi_range_read_next((char **)&key_chain_ptr))) {do(each record in chain){get_record_by_pos(rec_ptr);rc = generate_full_extensions(rec_ptr);if (rc != NESTED_LOOP_OK) return rc;}}
}
mysql> show create table city;
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                                                                                                                                                                                                       |
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| city  | CREATE TABLE `city` (`ID` int(11) NOT NULL AUTO_INCREMENT,`Name` char(35) NOT NULL DEFAULT '',`Country` char(3) NOT NULL DEFAULT '',`Population` int(11) NOT NULL DEFAULT '0',PRIMARY KEY (`ID`),KEY `Population` (`Population`),KEY `Country` (`Country`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci |
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)mysql> show create table country;
+---------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table   | Create Table                                                                                                                                                                                                                                                                                                                                                      |
+---------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| country | CREATE TABLE `country` (`Code` char(3) NOT NULL DEFAULT '',`Name` char(52) NOT NULL DEFAULT '',`SurfaceArea` float(10,2) NOT NULL DEFAULT '0.00',`Population` int(11) NOT NULL DEFAULT '0',`Capital` int(11) DEFAULT NULL,PRIMARY KEY (`Code`),UNIQUE KEY `Name` (`Name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci |
+---------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.01 sec)mysql> EXPLAIN SELECT city.Name, country.Name FROM city,country WHERE city.country=country.Code AND  country.Name LIKE 'L%' AND city.Population > 100000;
+----+-------------+---------+------------+-------+--------------------+---------+---------+--------------------+------+----------+--------------------------------------------------------------+
| id | select_type | table   | partitions | type  | possible_keys      | key     | key_len | ref                | rows | filtered | Extra                                                        |
+----+-------------+---------+------------+-------+--------------------+---------+---------+--------------------+------+----------+--------------------------------------------------------------+
|  1 | SIMPLE      | country | NULL       | index | PRIMARY,Name       | Name    | 208     | NULL               |    1 |   100.00 | Using where; Using index                                     |
|  1 | SIMPLE      | city    | NULL       | ref   | Population,Country | Country | 12      | test1.country.Code |    1 |   100.00 | Using where; Using join buffer (Batched Key Access (unique)) |
+----+-------------+---------+------------+-------+--------------------+---------+---------+--------------------+------+----------+--------------------------------------------------------------+
2 rows in set, 1 warning (0.01 sec)

3. Join执行流程(老执行器)

sub_select <--------------------------------------------+| -> iterator::read() // 读一行数据                    || -> evaluate_join_record()  //检查这行数据是否符合条件 || -> next_select() ---+                               ||                               |
sub_select_op  <--------+                               || -> op->put_record() // 前表数据写入join cache        || -> put_record_in_cache()                          || -> join->record()                                 || -> join_matching_records()                      || -> (qep_tab->next_select)(join, qep_tab + 1, 0) // 继续调用next_select| -> end_send()

点击这里→了解更多精彩内容

数据库实践丨MySQL多表join分析相关推荐

  1. 阿里不让 MySQL 多表 Join ?我偏要!

    一. 问题提出:<阿里巴巴JAVA开发手册>里面写超过三张表禁止join,这是为什么? 二.问题分析:对这个结论,你是否有怀疑呢?也不知道是哪位先哲说的不要人云亦云,今天我设计sql,来验 ...

  2. MySQL小表join大表的正确使用姿势(straight_join 关键字的使用)

    网上有种说法是:由于一般是采用小表join大表的方式(可以提高效率),所以有人说将小表放在左边,让它先执行,记住,这种说法是错误的!!!有例为证: 我们看上例: film inner join fil ...

  3. 数据库性能优化—MySQL单表最大记录数超过多少时性能会严重下降

    以前没有想过MySQL数据库的单表最大行数,直到最近interview时被问到c语言中int类型的最大值是多少时才想到Mysql单表最大行数的问题. 一开始被问到C语言中int类型的最大值有点懵逼,一 ...

  4. Flink进行Kafka事实表与Mysql维度表Join(纯DDL/DML方式)

    概述: 對參考鏈接[1]進行DDL上的復現. 一些基本的業務常识   來源載體 數據特點 維表 Mysql/Csv/Hbase 很少變化 事實表 Kafka 不停變化 开发环境与准备工作 组件 版本 ...

  5. 自定义字段MongoDb与Mysql 扩展表性能分析

    1.  需求背景         对于saas应用来说,不同的租户对于表单有不同的定义,为了满足租户的千人千面,自定义表单字段就成为的必不可少的功能. 2. 技术方案         从传统数据库my ...

  6. 如何把纯真ip数据库导入到MySQL数据表中

    一.下载最新版的QQWry.Dat 二.下载IPLook     使用IPLook把QQWry.Dat文件解压到IPData.txt文件     打开IPData.txt文件格式是这样的     0. ...

  7. mysql大表join小表速度很慢_mysql多表join中,为什么子查询会那么慢,怎么解决-问答-阿里云开发者社区-阿里云...

    下面的sql执行后cpu100%,但是这三张join的表数据量都在2W左右,不应该啊,是不是数据库服务器配置出了问题啊.sql如下:select a.excel_id, a.rpt_id, a.acc ...

  8. 纯真ip数据库 转mysql_如何把纯真ip数据库导入到MySQL数据表中

    一.下载最新版的QQWry.Dat 二.下载IPLook 使用IPLook把QQWry.Dat文件解压到IPData.txt文件 打开IPData.txt文件格式是这样的 0.0.0.0    0.2 ...

  9. Mysql两表 join 查询方式

    一.SQL基本语法格式 SELECT DISTINCT< select_list > FROM< left_table > < join_type > JOIN & ...

最新文章

  1. 【直播预告】如何设计性能更强的CNN模型
  2. 【C++拾遗】 从内存布局看C++虚继承的实现原理
  3. jfoenix jdk8 pom依赖
  4. 内网通免广告_3D打印进军广告发光字领域,成为名副其实的智能打印工厂
  5. 通过DataWorks数据集成归档日志服务数据至MaxCompute进行离线分析
  6. Android studio ERROR: Software caused connection abort: recv failed 解决方法
  7. Hibernate之DetachedCriteria类详解
  8. 机器学习与计算机视觉(数据集的选择)
  9. 限制本机访问某一些网站,本人不再花时间看新闻了
  10. Linux运维六:用户管理及用户权限设置
  11. BGPVRP5.0的10条选路原则,以及NE20的IBGP负载均衡
  12. 一道综合练习题实践list及dictionary集合类
  13. 《JavaScript设计模式》读书笔记模板方法模式
  14. 一个比CAM350好用的看GERBER软件
  15. Oracle客户端工具安装(PL/SQL Developer 和 instantclient)
  16. tl wn322g linux驱动下载,TP-Link TL-WN322G+网卡驱动
  17. 2021-06-02使用Digispark(ATTINY85)制作一个Badusb
  18. 为什么员工 996 多猝死,而企业家 996 甚至 9127 却很少听到有猝死的?答案原来是.....
  19. WIN10 下 autocad2006 及其他版本打开文件多窗口不能一个窗口的解决办法
  20. 开放信息抽取(OIE)系统(三)-- 第二代开放信息抽取系统(人工规则, rule-based, 先抽取关系)

热门文章

  1. 视觉SLAM十四讲学习笔记-第二讲-开发环境搭建
  2. ROS Rviz 显示地图 Python
  3. Git笔记(19) 生成SSH公钥
  4. oracle中rac是什么意思,oracle rac是什么?
  5. 12v电源正负极区分_解决冬天车辆无法启动的难题——车用应急启动电源选购要点及评测...
  6. Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.4.4 延迟初始化的bean...
  7. perl hash array 嵌套 push
  8. APP发布Xcode7
  9. [obc学习日记]3.10
  10. Metro 应用无法打开解决办法