semi-join Materialization 是用于semi-join的一种特殊的子查询物化技术。通常包含两种策略:
1.Materialization/lookup
2.Materialization/scan

考虑一个查询欧洲有大城市的国家:

  1. select * from Country
  2. where Country.code IN (select City.Country
  3. from City
  4. where City.Population > 7*1000*1000)
  5. and Country.continent='Europe'

子查询是非相关子查询。也即是我们可以独立运行内查询。semi-materialization的思想是使用city.country中可能的值填充一个临时表,然后和欧洲的国家进行关联。

这个join可以从两个方向进行:
1.从物化表到国家表
2.从国家表到物化表

第一个方向涉及一个全表扫描(在物化表上的全表扫描),因此被称为"Materialization-scan"
如果从第二个方向进行,最廉价的方式是使用主键从物化表中lookup出匹配的记录。这种方式被称为"Materialization-lookup"。

Materialization-scan
如果我们寻找人口超过700万的城市,优化器将使用materialize-scan,EXPLAIN输出结果也会显示这一点:

  1. MariaDB [world]> explain select * from Country where Country.code IN (select City.Country from City where City.Population > 7*1000*1000);
  2. +----+--------------+-------------+--------+--------------------+------------+---------+--------------------+------+-----------------------+
  3. | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
  4. +----+--------------+-------------+--------+--------------------+------------+---------+--------------------+------+-----------------------+
  5. | 1 | PRIMARY | <subquery2> | ALL | distinct_key | NULL | NULL | NULL | 15 | |
  6. | 1 | PRIMARY | Country | eq_ref | PRIMARY | PRIMARY | 3 | world.City.Country | 1 | |
  7. | 2 | MATERIALIZED | City | range | Population,Country | Population | 4 | NULL | 15 | Using index condition |
  8. +----+--------------+-------------+--------+--------------------+------------+---------+--------------------+------+-----------------------+
  9. 3 rows in set (0.01 sec)

从上可以看到:

1.仍然有两个select(id=1和id=2)

2.第二个select(id=2)的select_type是MATERIALIZED。这表示会执行并将结果存储在一个在所有列上带有一个唯一性索引的临时表。这个唯一性索引可以避免有重复的记录

3.第一个select中接收到一个名为subquery2的表,这是从第二个select(id=2)获取的物化的表

优化器选择在物化的表上执行全表扫描。这就是Materialization-Scan策略的示例。

至于执行成本,我们将从表City读取15行,将15行写入物化表,然后读取它们(优化器假设不会有任何重复),然后对表Country执行15次eq_ref访问。总共,我们将进行45次读取和15次写入。

相比之下,如果你在MySQL中运行EXPLAIN,你会得到如下结果:

  1. MySQL [world]> explain select * from Country where Country.code IN (select City.Country from City where City.Population > 7*1000*1000);
  2. +----+--------------------+---------+-------+--------------------+------------+---------+------+------+------------------------------------+
  3. | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
  4. +----+--------------------+---------+-------+--------------------+------------+---------+------+------+------------------------------------+
  5. | 1 | PRIMARY | Country | ALL | NULL | NULL | NULL | NULL | 239 | Using where |
  6. | 2 | DEPENDENT SUBQUERY | City | range | Population,Country | Population | 4 | NULL | 15 | Using index condition; Using where |
  7. +----+--------------------+---------+-------+--------------------+------------+---------+------+------+------------------------------------+

读的记录是(239 + 239*15) = 3824。

Materialization-Lookup

让我们稍微修改一下查询,看看哪些国家的城市人口超过1百万(而不是7百万):

  1. MariaDB [world]> explain select * from Country where Country.code IN (select City.Country from City where City.Population > 1*1000*1000) ;
  2. +----+--------------+-------------+--------+--------------------+--------------+---------+------+------+-----------------------+
  3. | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
  4. +----+--------------+-------------+--------+--------------------+--------------+---------+------+------+-----------------------+
  5. | 1 | PRIMARY | Country | ALL | PRIMARY | NULL | NULL | NULL | 239 | |
  6. | 1 | PRIMARY | <subquery2> | eq_ref | distinct_key | distinct_key | 3 | func | 1 | |
  7. | 2 | MATERIALIZED | City | range | Population,Country | Population | 4 | NULL | 238 | Using index condition |
  8. +----+--------------+-------------+--------+--------------------+--------------+---------+------+------+-----------------------+
  9. 3 rows in set (0.00 sec)

explain的输出结果和Materialization-scan类似,除了:
1.subquery2表是通过eq_ref访问的
2.access使用了索引distinct_key
这意味着优化器计划对物化表执行索引查找。换句话说,我们将使用Materialization-lookup策略。

在MySQL中(或者使用optimizer_switch='semi-join=off,materialization=off'),会得到这样的执行计划:

  1. MySQL [world]> explain select * from Country where Country.code IN (select City.Country from City where City.Population > 1*1000*1000) ;
  2. +----+--------------------+---------+----------------+--------------------+---------+---------+------+------+-------------+
  3. | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
  4. +----+--------------------+---------+----------------+--------------------+---------+---------+------+------+-------------+
  5. | 1 | PRIMARY | Country | ALL | NULL | NULL | NULL | NULL | 239 | Using where |
  6. | 2 | DEPENDENT SUBQUERY | City | index_subquery | Population,Country | Country | 3 | func | 18 | Using where |
  7. +----+--------------------+---------+----------------+--------------------+---------+---------+------+------+-------------+

可以看出,这两个执行计划都将对国家表进行全面扫描。对于第二步,MariaDB将填充物化表(238行从表City读取并写入临时表),然后对表Country中的每个记录执行惟一的键查找,结果是238个惟一的键查找。总的来说,第二步将花费(239+238)= 477读取和238 temp.table的写入。

MySQL的第二步计划是使用City上的索引读取18行。它为表国家接收的每个记录的国家。计算出来的成本为(18*239)= 4302读取。如果有更少的子查询调用,这个计划将比物化的计划更好。顺便说一下,MariaDB也可以选择使用这样的查询计划(请参阅FirstMatch策略),但是它没有选择。

带有group by的子查询

当子查询带有分组的时候,MariaDB可以使用semi-join物化策略(这种场景下,其他semi-join策略不适用)

这允许高效地执行搜索某个组中最佳/最后一个元素的查询。

举个例子,我们来看看每个大陆上人口最多的城市:

  1. explain
  2. select * from City
  3. where City.Population in (select max(City.Population) from City, Country
  4. where City.Country=Country.Code
  5. group by Continent)
  6. +------+--------------+-------------+------+---------------+------------+---------+----------------------------------+------+-----------------+
  7. | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
  8. +------+--------------+-------------+------+---------------+------------+---------+----------------------------------+------+-----------------+
  9. | 1 | PRIMARY | <subquery2> | ALL | distinct_key | NULL | NULL | NULL | 239 | |
  10. | 1 | PRIMARY | City | ref | Population | Population | 4 | <subquery2>.max(City.Population) | 1 | |
  11. | 2 | MATERIALIZED | Country | ALL | PRIMARY | NULL | NULL | NULL | 239 | Using temporary |
  12. | 2 | MATERIALIZED | City | ref | Country | Country | 3 | world.Country.Code | 18 | |
  13. +------+--------------+-------------+------+---------------+------------+---------+----------------------------------+------+-----------------+
  14. 4 rows in set (0.00 sec)

 城市是:

  1. +------+-------------------+---------+------------+
  2. | ID | Name | Country | Population |
  3. +------+-------------------+---------+------------+
  4. | 1024 | Mumbai (Bombay) | IND | 10500000 |
  5. | 3580 | Moscow | RUS | 8389200 |
  6. | 2454 | Macao | MAC | 437500 |
  7. | 608 | Cairo | EGY | 6789479 |
  8. | 2515 | Ciudad de México | MEX | 8591309 |
  9. | 206 | São Paulo | BRA | 9968485 |
  10. | 130 | Sydney | AUS | 3276207 |
  11. +------+-------------------+---------+------------+

  

Semi-join materialization
1.可以用于非相关的in子查询。子查询可以含有分组、和/或聚合函数
2.在explain输出中,子查询会有type=Materialized;父表子查询中有table=<subqueryN>
3.开启需要将变量optimizer_switch中的materialization=on、semijoin=on
4.Non-semijoin materialization与materialization=on|off标记共享

https://mariadb.com/kb/en/library/semi-join-materialization-strategy/

 

semi-join子查询优化 -- semi-join Materialization策略的更多相关文章

  1. 转载:left join和left semi join的联系和区别

    1.联系 他们都是 hive join 方式的一种,join on 属于 common join(shuffle join/reduce join),而 left semi join 则属于 map ...

  2. hive中left join、left outer join和left semi join的区别

    先说结论,再举例子.   hive中,left join与left outer join等价.   left semi join与left outer join的区别:left semi join相当 ...

  3. join 和子查询优化

    一次在家查看数据的时候,列表展示特别慢,就查看了一下,把sql语句拿出来运行居然要4,5秒,当时就感觉有问题,语句用的join链接2个表,感觉没啥错误,为啥会这么慢,然后改用了子查询链接,发现快了许多 ...

  4. semi-join子查询优化 -- FirstMatch策略

    FirstMatch执行semi-join子查询的一种策略. 类似于MySQL 5.x中如何执行in.exists子查询. 让我们以搜索拥有大城市的国家为例: select * from Countr ...

  5. semi-join子查询优化 -- Duplicate Weedout策略

    duplicate weedout是执行semi-join子查询的一种策略. 将semi-join作为一个常规的inner join.然后使用一个临时表,将重复的记录排除. 假设,你有一个查询,你在寻 ...

  6. SQL查询优化 LEFT JOIN和INNER JOIN

    作者:VerySky 推荐:陈敬(Cathy) SQL查询优化 LEFT JOIN和INNER JOIN 1,连接了八个数据库表,而且全部使用LEFT JOIN,如下所示: Resource_Reso ...

  7. 数据库子查询和join的比较

    子查询进行SELECT语句嵌套查询,可以一次完成很多逻辑上需要多个步骤才能完成的SQL操作.子查询虽然很灵活,但是执行效率并不高. select goods_id,goods_name from go ...

  8. 为什么MySQL不推荐使用子查询和join

    前言: 1.对于mysql,不推荐使用子查询和join是因为本身join的效率就是硬伤,一旦数据量很大效率就很难保证,强烈推荐分别根据索引单表取数据,然后在程序里面做join,merge数据. 2.子 ...

  9. java 多线程 Thread.join子线程结束父线程再运行;join(long):等待超时毫秒数

    Join的使用 目的:当子线程运行结束后,父线程才能再继续运行 /** * @ClassName ThreadJoinExample * @projectName: object1 * @author ...

  10. semi-join子查询优化 -- LooseScan策略

    LooseScan执行semi-join子查询的一种策略. 我们将通过示例来演示这种松散(LooseScan)策略.假设,我们正在查找拥有卫星的国家.我们可以通过以下查询获得它们(为了简单起见,我们忽 ...

随机推荐

  1. xshell 连接出现 The remote SSH server rejected X11 forwarding request

    如果本文对你有用,请爱心点个赞,提高排名,帮助更多的人.谢谢大家!❤ yum install xorg-x11-xauth 同时sshd的config文件开启X11Forwarding yes vim ...

  2. Trunk 实现跨交换机 VLAN 通信

    当网络中有多台交换机时,位于不同交换机上的相同VLAN的主机之间时如何通信的呢?我们使用Trunk实现跨交换机VLAN通信.还有以太网通道的操作哦. 实验拓扑 两台交换机直连,每台下面再连接两台VPC ...

  3. 【HICP Gauss】数据库 数据库管理(文件 用户管理 系统权限 对象权限 profile)-7

    数据库运行 依赖不同类型的文件 ,数据文件 参数文件 控制文件 redo日志文件 运行日志文件 审计日志文件等 数据文件 就是表空间文件 存储数据库的数据文件 参数文件 用户修改的配置信息 控制文件 ...

  4. InitContainer

    InitContainer 初始化容器 在Pod中,容器是共享存储资源和网络资源的,所以Init Container容器产生的数据是可以被其他容器作用到的.初始化容器有点类似于postStart 钩子 ...

  5. PAT甲级1011水题飘过

    题目分析:对于输入的数据分三条,选出每条中最大值记录下来,按照题目要求算出最大可能的获利即可 #include<iostream> using namespace std; ]; //k数 ...

  6. Centos7服务器搭建部署显卡计算环境以及常用软件的安装使用

    安装好anaconda的服务器上会more你已经安装好jupyter notebook,执行下面的命令可以提供链接地址允许远程浏览器打开并访问: jupyter notebook --no-brows ...

  7. http消息与webservice

    别人的:在一台配置较低的PC上,同时开启服务端与客户端,10000条数据,使用基于http的消息逐条进行传递,从开始传递至全部接收并处理完毕,大概需要465秒的时间:而在同一台机器上,使用WebSer ...

  8. 深度学习Keras框架笔记之激活函数详解

    激活函数也是神经网络中一个很重的部分.每一层的网络输出都要经过激活函数.比较常用的有linear,sigmoid,tanh,softmax等.Keras内置提供了很全的激活函数,包括像LeakyReL ...

  9. test20190901 NOI2019 模拟赛

    0+0+0=0.还是太菜,看不出题型. Masodik 你要从 (0,0) 点走到 (n,m),每次只能往 x 轴或者 y 轴正方向移动一个单位距离.从 (i,j) 移动到 (i,j+1) 的代价为 ...

  10. 分享STM32 FLASH 擦除(以及防止误擦除程序代码)、写入

    编译环境:我用的是(Keil)MDK4.7.2   stm32库版本:我用的是3.5.0一.本文不对FLASH的基础知识做详细的介绍,不懂得地方请查阅有关资料. 对STM32 内部FLASH进行编程操 ...