乐趣区

关于mysql:从join的实现窥探MySQL迭代器

以如下 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 社区助手 微信好友,发送验证信息 加群

退出移动版