数据库实践丨MySQL多表join分析
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分析相关推荐
- 阿里不让 MySQL 多表 Join ?我偏要!
一. 问题提出:<阿里巴巴JAVA开发手册>里面写超过三张表禁止join,这是为什么? 二.问题分析:对这个结论,你是否有怀疑呢?也不知道是哪位先哲说的不要人云亦云,今天我设计sql,来验 ...
- MySQL小表join大表的正确使用姿势(straight_join 关键字的使用)
网上有种说法是:由于一般是采用小表join大表的方式(可以提高效率),所以有人说将小表放在左边,让它先执行,记住,这种说法是错误的!!!有例为证: 我们看上例: film inner join fil ...
- 数据库性能优化—MySQL单表最大记录数超过多少时性能会严重下降
以前没有想过MySQL数据库的单表最大行数,直到最近interview时被问到c语言中int类型的最大值是多少时才想到Mysql单表最大行数的问题. 一开始被问到C语言中int类型的最大值有点懵逼,一 ...
- Flink进行Kafka事实表与Mysql维度表Join(纯DDL/DML方式)
概述: 對參考鏈接[1]進行DDL上的復現. 一些基本的業務常识 來源載體 數據特點 維表 Mysql/Csv/Hbase 很少變化 事實表 Kafka 不停變化 开发环境与准备工作 组件 版本 ...
- 自定义字段MongoDb与Mysql 扩展表性能分析
1. 需求背景 对于saas应用来说,不同的租户对于表单有不同的定义,为了满足租户的千人千面,自定义表单字段就成为的必不可少的功能. 2. 技术方案 从传统数据库my ...
- 如何把纯真ip数据库导入到MySQL数据表中
一.下载最新版的QQWry.Dat 二.下载IPLook 使用IPLook把QQWry.Dat文件解压到IPData.txt文件 打开IPData.txt文件格式是这样的 0. ...
- mysql大表join小表速度很慢_mysql多表join中,为什么子查询会那么慢,怎么解决-问答-阿里云开发者社区-阿里云...
下面的sql执行后cpu100%,但是这三张join的表数据量都在2W左右,不应该啊,是不是数据库服务器配置出了问题啊.sql如下:select a.excel_id, a.rpt_id, a.acc ...
- 纯真ip数据库 转mysql_如何把纯真ip数据库导入到MySQL数据表中
一.下载最新版的QQWry.Dat 二.下载IPLook 使用IPLook把QQWry.Dat文件解压到IPData.txt文件 打开IPData.txt文件格式是这样的 0.0.0.0 0.2 ...
- Mysql两表 join 查询方式
一.SQL基本语法格式 SELECT DISTINCT< select_list > FROM< left_table > < join_type > JOIN & ...
最新文章
- 【直播预告】如何设计性能更强的CNN模型
- 【C++拾遗】 从内存布局看C++虚继承的实现原理
- jfoenix jdk8 pom依赖
- 内网通免广告_3D打印进军广告发光字领域,成为名副其实的智能打印工厂
- 通过DataWorks数据集成归档日志服务数据至MaxCompute进行离线分析
- Android studio ERROR: Software caused connection abort: recv failed 解决方法
- Hibernate之DetachedCriteria类详解
- 机器学习与计算机视觉(数据集的选择)
- 限制本机访问某一些网站,本人不再花时间看新闻了
- Linux运维六:用户管理及用户权限设置
- BGPVRP5.0的10条选路原则,以及NE20的IBGP负载均衡
- 一道综合练习题实践list及dictionary集合类
- 《JavaScript设计模式》读书笔记模板方法模式
- 一个比CAM350好用的看GERBER软件
- Oracle客户端工具安装(PL/SQL Developer 和 instantclient)
- tl wn322g linux驱动下载,TP-Link TL-WN322G+网卡驱动
- 2021-06-02使用Digispark(ATTINY85)制作一个Badusb
- 为什么员工 996 多猝死,而企业家 996 甚至 9127 却很少听到有猝死的?答案原来是.....
- WIN10 下 autocad2006 及其他版本打开文件多窗口不能一个窗口的解决办法
- 开放信息抽取(OIE)系统(三)-- 第二代开放信息抽取系统(人工规则, rule-based, 先抽取关系)
热门文章
- 视觉SLAM十四讲学习笔记-第二讲-开发环境搭建
- ROS Rviz 显示地图 Python
- Git笔记(19) 生成SSH公钥
- oracle中rac是什么意思,oracle rac是什么?
- 12v电源正负极区分_解决冬天车辆无法启动的难题——车用应急启动电源选购要点及评测...
- Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.4.4 延迟初始化的bean...
- perl hash array 嵌套 push
- APP发布Xcode7
- [obc学习日记]3.10
- Metro 应用无法打开解决办法