草庐IT

从join的实现窥探MySQL迭代器

GreatSQL 2023-03-28 原文

以如下left join查询语句为范例:

select * from t1 left join t2 on t1.c=t2.a ;

以下初始化数据:

1  DROP TABLE IF EXISTS `t1`;
2    CREATE TABLE `t1` (
3      `a` int DEFAULT NULL,
4      `b` varchar(20) DEFAULT NULL
5    )
6    INSERT INTO `t1` VALUES (1, 'a');
7    INSERT INTO `t1` VALUES (1, 'b');
8    INSERT INTO `t1` VALUES (4, 'a');
9    INSERT INTO `t1` VALUES (5, 'a');
10    
11    DROP TABLE IF EXISTS `t2`;
12    CREATE TABLE `t2` (
13      `c` int DEFAULT NULL,
14      `d` varchar(20) DEFAULT NULL
15    )
16    INSERT INTO `t2` VALUES (9, 'i');
17    INSERT INTO `t2` VALUES (1, 'i');
18    INSERT INTO `t2` VALUES (2, 'i');
19    INSERT INTO `t2` VALUES (3, 'i');

1.处理join的yacc入口

sys_yacc.yy文件内解析t1 left join t2 on t1.c=t2.a;对应处理位置

1  table_reference outer_join_type table_reference ON_SYM expr
2    {
3  $$= NEW_PTN PT_joined_table_on($1, @2, $2, $3, $5);
4    }

其中outer_join_type对应

1  outer_join_type:
2    LEFT opt_outer JOIN_SYM          { $$= JTT_LEFT; }
3  | RIGHT opt_outer JOIN_SYM         { $$= JTT_RIGHT; }

入参处理在函数T_joined_table_on

2.移步到函数PT_joined_table_on

PT_joined_table_on声明可知其继承PT_joined_table函数,入参左右表赋值为PT_joined_table内定义的tr1tr2

函数PT_joined_table_on将输入join的左右表加入context内,并调用add_join_on将on内的条件加入右表,记录后续数据过滤条件。

3.执行阶段函数do_command(thd)

具体对应执行函数int mysql_execute_command(THD *thd, bool first_level),语句解析以及相应参数保存完成后,进入函数int mysql_execute_command(THD *thd, bool first_level),此函数内根据前面解析到的命令类型switch (lex->sql_command)调用对应的处理函数,如当前语句为例查询命令解析为lex->sql_command = SQLCOM_SELECT则进入函数lex->m_sql_cmd->execute(thd);其对应为sql_select.cc内函数bool Sql_cmd_dml::execute(THD *thd)

4.优化器操作,生成access_paths

sql_select.cc内函数bool Sql_cmd_dml::execute(THD *thd)函数内主要操作为函数execute_inner,在函数execute_inner内首先会对当前的执行优化操作,

  1. 调用查询表达式Query_expression的优化器unit->optimize,此函数中会对该Query_expression的内的每个查询块query_block分别先进行优化操作,
  2. 查询块内函数bool JOIN::optimize()内会将每个查询块优化生成查询执行计划 ,具体执行函数为函数JOIN::create_access_paths()create_root_access_path_for_join()函数,以当前查询为例在函数create_root_access_path_for_join内根据参数条件主要调用ConnectJoins函数
  3. 在函数ConnectJoins内调用FindSubstructure判断是join类型内连接、外连接、半链接等类型
  4. 根据FindSubstructure返回join类型调用相应的函数生成path,当前查询为例执行调用CreateHashJoinAccessPath生成path。

至此查询块query_block的优化操作和path生成完成,查询块优化操作完成后再执行整体表达式Query_expression的优化和path的生成,因为目前范例仅为一个查询块,所以当前无需再做整体表达式的优化和path生成。

5.创建迭代器iterator

根据上一步生成的path调用CreateIteratorFromAccessPath函数生成迭代器,用于循环操作各表数据。

在此函数内会根据path的类型调用生成不同类型的迭代器,以目前范例为例,会调用迭代器类型为HashJoinIterator

6.上述4、5步执行完成后,执行迭代器iterator

在函数execute_inner内执行完成上述4、5步骤操作后主要继续执行unit->execute(thd)函数,其对应执行查询表达式函数bool Query_expression::ExecuteIteratorQuery(THD *thd)

  1. 函数Query_expression::ExecuteIteratorQuery内主要执行m_root_iterator->Init(),迭代器iterator初始化,当前范例为使用HashJoinIterator类型迭代器,因此对应执行迭代器函数HashJoinIterator::Init()
  2. 执行m_build_input->Init()来初始右表table句柄,用于下面函数BuildHashTable()内读取右表数据以便初始化返回数据存储表hashtable,值得注意的是BuildHashTable函数内会根据处理流程调用SetReadingProbeRowState设置执行状态用于引导后续迭代器iterator执行流程。
  3. 函数内最后调用InitProbeIterator执行m_probe_input->Init()初始左表table句柄用于下面函数读取左表数据。
  4. 上面操作完成后执行m_root_iterator->Read()函数,以当前查询为范例其对应int HashJoinIterator::Read()函数,执行过程中根据前面SetReadingProbeRowState设置的流程状态再选择对应的操作函数,以当前范例则会循环读取左表数据,而在操作函数内也会调用SetReadingProbeRowState来设置迭代器iterator下一步操作,直至迭代器处理完成,其中在函数Query_expression::ExecuteIteratorQuery,每次读取一条成功后就会调用send_data操作将结果发送至客户端,直至所有查询结果发送完成。

7.至此客户端收到相应显示查询结果。


Enjoy GreatSQL ?

关于 GreatSQL

GreatSQL是由万里数据库维护的MySQL分支,专注于提升MGR可靠性及性能,支持InnoDB并行查询特性,是适用于金融级应用的MySQL分支版本。

相关链接: GreatSQL社区 Gitee GitHub Bilibili

GreatSQL社区:

欢迎来GreatSQL社区发帖提问
https://greatsql.cn/

技术交流群:

微信:扫码添加GreatSQL社区助手微信好友,发送验证信息加群

有关从join的实现窥探MySQL迭代器的更多相关文章

  1. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  2. ruby - 为什么 Ruby 的 each 迭代器先执行? - 2

    我在用Ruby执行简单任务时遇到了一件奇怪的事情。我只想用每个方法迭代字母表,但迭代在执行中先进行:alfawit=("a".."z")puts"That'sanalphabet:\n\n#{alfawit.each{|litera|putslitera}}"这段代码的结果是:(缩写)abc⋮xyzThat'sanalphabet:a..z知道为什么它会这样工作或者我做错了什么吗?提前致谢。 最佳答案 因为您的each调用被插入到在固定字符串之前执行的字符串文字中。此外,each返回一个Enumerable,实际上您甚至打印它。试试

  3. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  4. 使用canal同步MySQL数据到ES - 2

    文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co

  5. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  6. MIMO-OFDM无线通信技术及MATLAB实现(1)无线信道:传播和衰落 - 2

     MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO

  7. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

  8. ruby - Arrays Sets 和 SortedSets 在 Ruby 中是如何实现的 - 2

    通常,数组被实现为内存块,集合被实现为HashMap,有序集合被实现为跳跃列表。在Ruby中也是如此吗?我正在尝试从性能和内存占用方面评估Ruby中不同容器的使用情况 最佳答案 数组是Ruby核心库的一部分。每个Ruby实现都有自己的数组实现。Ruby语言规范只规定了Ruby数组的行为,并没有规定任何特定的实现策略。它甚至没有指定任何会强制或至少建议特定实现策略的性能约束。然而,大多数Rubyist对数组的性能特征有一些期望,这会迫使不符合它们的实现变得默默无闻,因为实际上没有人会使用它:插入、前置或追加以及删除元素的最坏情况步骤复

  9. ruby - "public/protected/private"方法是如何实现的,我该如何模拟它? - 2

    在ruby中,你可以这样做:classThingpublicdeff1puts"f1"endprivatedeff2puts"f2"endpublicdeff3puts"f3"endprivatedeff4puts"f4"endend现在f1和f3是公共(public)的,f2和f4是私有(private)的。内部发生了什么,允许您调用一个类方法,然后更改方法定义?我怎样才能实现相同的功能(表面上是创建我自己的java之类的注释)例如...classThingfundeff1puts"hey"endnotfundeff2puts"hey"endendfun和notfun将更改以下函数定

  10. ruby-on-rails - 无法安装 mysql2 0.3.14 gem - 2

    我看到其他人也遇到过类似的问题,但没有一个解决方案对我有用。0.3.14gem与其他gem文件一起存在。我已经完全按照此处指示完成了所有操作:https://github.com/brianmario/mysql2.我仍然得到以下信息。我不知道为什么安装程序指示它找不到include目录,因为我已经检查过它存在。thread.h文件存在,但不在ruby​​目录中。相反,它在这里:C:\RailsInstaller\DevKit\lib\perl5\5.8\msys\CORE\我正在运行Windows7并尝试在Aptana3中构建我的Rails项目。我的Ruby是1.9.3。$gemin

随机推荐