【GreatSQL优化器-09】make_join_query_block

【GreatSQL优化器-09】make_join_query_block

一、make_join_query_block介绍

GreatSQL优化器对于多张表join的连接顺序在前面的章节介绍过的best_access_path函数已经执行了,接着就是把where条件进行切割然后推给合适的表。这个过程就是由函数make_join_query_block来执行的。

下面用几个简单的例子来说明join连接中条件推送是什么。

CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 INT,date1 DATETIME);
INSERT INTO t1 VALUES (1,10,'2021-03-25 16:44:00.123456'),(2,1,'2022-03-26 16:44:00.123456'),(3,4,'2023-03-27 16:44:00.123456'),(5,5,'2024-03-25 16:44:00.123456'),(7,null,'2020-03-25 16:44:00.123456'),(8,10,'2020-10-25 16:44:00.123456'),(11,16,'2023-03-25 16:44:00.123456');
CREATE TABLE t2 (cc1 INT PRIMARY KEY, cc2 INT);
INSERT INTO t2 VALUES (1,3),(2,1),(3,2),(4,3),(5,15);
CREATE TABLE t3 (ccc1 INT, ccc2 varchar(100));
INSERT INTO t3 VALUES (1,'aa1'),(2,'bb1'),(3,'cc1'),(4,'dd1'),(null,'ee');
CREATE INDEX idx1 ON t1(c2);
CREATE INDEX idx2 ON t1(c2,date1);
CREATE INDEX idx2_1 ON t2(cc2);
CREATE INDEX idx3_1 ON t3(ccc1);

下面这个例子((t1.c1 = t3.ccc1) or (t3.ccc1 < 3))条件推送给t1

greatsql> EXPLAIN FORMAT=TREE SELECT * FROM t1 join t3 ON t1.c1=t3.ccc1 or t3.ccc1<3;
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN                                                                                                                                                                                                                                                         |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -> Filter: ((t1.c1 = t3.ccc1) or (t3.ccc1 < 3))  (cost=5.26 rows=35)
    -> Inner hash join (no condition)  (cost=5.26 rows=35)
        -> Index scan on t1 using idx2  (cost=0.34 rows=7)
        -> Hash
            -> Table scan on t3  (cost=0.75 rows=5)
 |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

下面例子(t1.c1 < 3)条件推给t1,(ccc1=t1.c1)条件推给t3

greatsql> EXPLAIN FORMAT=TREE SELECT * FROM t1 join t3 ON t1.c1=t3.ccc1 and t3.ccc1<3;
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN                                                                                                                                                                                                                          |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -> Nested loop inner join  (cost=2.40 rows=2)
    -> Filter: (t1.c1 < 3)  (cost=1.70 rows=2)
        -> Index scan on t1 using idx2  (cost=1.70 rows=7)
    -> Index lookup on t3 using idx3_1 (ccc1=t1.c1)  (cost=0.30 rows=1)
 |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

下面例子((t3.ccc1 = t1.c2) or (t3.ccc1 is null) or (t3.ccc2 like 'a%'))条件推给t3,(((t3.ccc1 = t1.c2) and (t2.cc1 = t1.c1)) or (t3.ccc1 is null) or (t3.ccc2 like 'a%'))条件推给t2

greatsql> EXPLAIN SELECT * FROM t2,t1,t3 WHERE t1.c1=t2.cc1 AND t1.c2=t3.ccc1 OR t3.ccc1 IS NULL OR t3.ccc2 LIKE 'a%';
| -> Filter: (((t3.ccc1 = t1.c2) and (t2.cc1 = t1.c1)) or (t3.ccc1 is null) or (t3.ccc2 like 'a%'))  (cost=14.27 rows=85)
    -> Inner hash join (no condition)  (cost=14.27 rows=85)
        -> Index scan on t2 using idx2_1  (cost=0.09 rows=5)
        -> Hash
            -> Filter: ((t3.ccc1 = t1.c2) or (t3.ccc1 is null) or (t3.ccc2 like 'a%'))  (cost=4.70 rows=17)
                -> Inner hash join (no condition)  (cost=4.70 rows=17)
                    -> Table scan on t3  (cost=0.07 rows=5)
                    -> Hash
                        -> Index scan on t1 using idx2  (cost=0.95 rows=7)

二、make_join_query_block代码解释

make_join_query_block函数通过join表顺序和每张表的table_map属性以及cond条件的属性来决定cond条件添加到哪张表,并且可能会重新对表的索引进行check找出cost更低的索引,下面是代码解析。

bool JOIN::optimize() {
  make_join_query_block();
}

static bool make_join_query_block(JOIN *join, Item *cond) {
  for (uint i = join->const_tables; i < join->tables; i++) {
      // 这四个变量说明见表一
      JOIN_TAB *const tab = join->best_ref[i];
      const plan_idx first_inner = tab->first_inner();
      const table_map used_tables = tab->prefix_tables();
      const table_map current_map = tab->added_tables();
      if (cond)
        // 这里通过table_map属性决定了是否给这个表添加条件,见下面表二、表四和表五说明
        tmp = make_cond_for_table(thd, cond, used_tables, current_map, false);
     // 如果recheck_reason=true,这里需要重新做一次确认,找出cost最低的索引。见表六
      if (recheck_reason)
        test_if_order_by_key();
        test_if_cheaper_ordering();
        test_quick_select();
      }  
      /* Add conditions added by add_not_null_conds(). */
      if (and_conditions(&tmp, tab->condition())) return true;
      if (join->attach_join_conditions(i)) return true;
  }
}
// 条件添加基本原则是条件带有表列的添加到该表,但是如果属性不一致的话也不会添加,只会添加到最后一张表。具体解释见下面实际例子。

表一:上面四个变量解释

变量解释说明
tab当前检测是否需要加条件的表这里是排序后的表
first_inner多张表join的时候排序后的第一张表例如:SELECT * FROM t1 LEFT OUTER JOIN (t2 JOIN t3) ON X
used_tables包括当前表以及之前的左连接表的信息如果该值=0代表所有表,used_tables信息在之前的set_prefix_tables()获得
current_map当前检测表信息包含被添加的一些信息

表二:make_cond_for_table()动作

场景解释返回
AND条件遍历Item_cond包含的所有list,判断list是否有包含该表左连接的表的列,有的话加入新的Item_cond_andnew Item_cond_and
OR条件遍历Item_cond包含的所有list,判断list是否有包含该表左连接的表的列,有的话加入新的Item_cond_ornew Item_cond_or
其他Item如果条件没有涉及左连接的表,或者给所有表添加条件cond->is_expensive()nullptr(无条件)
其他Item如果条件涉及左连接的表并且item与表的属性一致(见表四)该Item条件

表三:is_expensive_processor()函数

Itemis_expensive说明
Item_udf_functrue这个Item作为条件不会添加到所有join表里面
Item_func_sptrue这个Item作为条件不会添加到所有join表里面
普通Itemfalse这个Item作为条件可能会添加到join表里面

表四:Item的table_map属性

table_map使用的Item说明举例
INNER_TABLE_BITItem_trigger_field,Item_sp_variable,Item_param,Item_func_connection_id,Item_func_get_system_var,Item_func_user,Item_load_file, Item_func_sp,Item_func_get_user_var确定的常量,在表每行都一样f1(1)@@optimizer_search_depth
OUTER_REF_TABLE_BITItem_field,Item_ref,Item_view_ref,Item_outer_ref内部field需要外部query block的item信息select (select t1.a from t1 as t2 limit 1) from t1 group by t1.pk,t1.a就是OUTER_REF_TABLE_BIT
RAND_TABLE_BITItem_func_rand,Item_func_sleep,Item_func_uuid,Item_func_sysdate_local,Item_func_reject_if,Item_func_sp,Item_func_get_user_var不确定的,表每行都要换数据,非只有一行表,跟INNER_TABLE_BIT相反f1(t1.c1)
PSEUDO_TABLE_BITS包含以上三个

表五:表连接添加的属性

table_map第一张表中间的表最后一张表
INNER_TABLE_BITyes
OUTER_REF_TABLE_BITallow_outer_refs
RAND_TABLE_BITyes

表六:表的索引是否要重新check

recheck_reason说明
DONT_RECHECK没有索引的表不需要重新check
NOT_FIRST_TABLE不是第一张表需要重新check
LOW_LIMIT带有limit语句的sql需要重新check
三、实际例子说明

接下来看几个例子来说明上面的代码。

首先看一下最后确定的连接顺序,为t1,t3,t2,因为条件不带有RAND_TABLE_BIT的Item,因此最后是按照cond含有的列推送给对应表来实现的。

例子一:

greatsql> EXPLAIN SELECT * FROM t2,t1,t3 WHERE t1.c1=t2.cc1 AND t1.c2=t3.ccc1 OR t3.ccc1 IS NULL OR t3.ccc2 LIKE 'a%';
+----+-------------+-------+------------+-------+-------------------+--------+---------+------+------+----------+---------------------------------------------------------+
| id | select_type | table | partitions | type  | possible_keys     | key    | key_len | ref  | rows | filtered | Extra                                                   |
+----+-------------+-------+------------+-------+-------------------+--------+---------+------+------+----------+---------------------------------------------------------+
|  1 | SIMPLE      | t1    | NULL       | index | PRIMARY,idx1,idx2 | idx2   | 11      | NULL |    7 |   100.00 | Using index                                             |
|  1 | SIMPLE      | t3    | NULL       | ALL   | idx3_1            | NULL   | NULL    | NULL |    5 |    48.80 | Using where; Using join buffer (hash join)              |
|  1 | SIMPLE      | t2    | NULL       | index | PRIMARY           | idx2_1 | 5       | NULL |    5 |   100.00 | Using where; Using index; Using join buffer (hash join) |
+----+-------------+-------+------------+-------+-------------------+--------+---------+------+------+----------+---------------------------------------------------------+

表一:是否把cond条件推送给表

CONDt1[t1,]t3[t1,t3,]t2
(t1.c1=t2.cc1)nonoyes
(t3.ccc1 = t1.c2)noyesyes
(t3.ccc1 is null)noyesyes
(t3.ccc2 like 'a%')noyesyes

注:这里的中括号代表当前检测表的左连接表,中括号右边就是当前正在检测的表

表二:表的table_map值

CONDt1t3t2
best_ref->table_ref->map()0x0100x1000x001
best_ref->prefix_tables()INNER_TABLE_BIT+ OUTER_REF_TABLE_BIT+0x010INNER_TABLE_BIT+ OUTER_REF_TABLE_BIT+0x010+0x100INNER_TABLE_BIT+OUTER_REF_TABLE_BIT +RAND_TABLE_BIT+0x010+0x100+0x001
best_ref->added_tables()INNER_TABLE_BIT+ OUTER_REF_TABLE_BIT+0x0100x100RAND_TABLE_BIT+0x001

注:这里的INNER_TABLE_BIT和OUTER_REF_TABLE_BIT在函数JOIN::set_prefix_tables()默认加上了

看一下结果是否符合预期,确实如上表所述。这里看到又执行了一次test_quick_select()来确定走哪个索引。

"attaching_conditions_to_tables": {
              "original_condition": "(((`t3`.`ccc1` = `t1`.`c2`) and (`t2`.`cc1` = `t1`.`c1`)) or (`t3`.`ccc1` is null) or (`t3`.`ccc2` like 'a%'))",
              "attached_conditions_computation": [
                {
                  "table": "`t2`",
                  "rechecking_index_usage": { 这里对索引重新做了一次check
                    "recheck_reason": "not_first_table",
                    "range_analysis": {
                      "table_scan": {
                        "rows": 5,
                        "cost": 3.6
                      },
                      "potential_range_indexes": [
                        {
                          "index": "PRIMARY",
                          "usable": true,
                          "key_parts": [
                            "cc1"
                          ]
                        },
                        {
                          "index": "idx2_1",
                          "usable": false,
                          "cause": "not_applicable"
                        }
                      ],
                      "best_covering_index_scan": {
                        "index": "idx2_1",
                        "cost": 0.751098,
                        "chosen": true
                      },
                      "setup_range_conditions": [
                      ],
                      "group_index_range": {
                        "chosen": false,
                        "cause": "not_single_table"
                      },
                      "skip_scan_range": {
                        "chosen": false,
                        "cause": "not_single_table"
                      }
                    }
                  }
                }
              ],
              "attached_conditions_summary": [
                {
                  "table": "`t1`",
                  "attached": null
                },
                {
                  "table": "`t3`",
                  "attached": "((`t3`.`ccc1` = `t1`.`c2`) or (`t3`.`ccc1` is null) or (`t3`.`ccc2` like 'a%'))"
                },
                {
                  "table": "`t2`",
                  "attached": "(((`t3`.`ccc1` = `t1`.`c2`) and (`t2`.`cc1` = `t1`.`c1`)) or (`t3`.`ccc1` is null) or (`t3`.`ccc2` like 'a%'))"
                }
              ]
            }
          },
          {
            "finalizing_table_conditions": [
              {
                "table": "`t3`",
                "original_table_condition": "((`t3`.`ccc1` = `t1`.`c2`) or (`t3`.`ccc1` is null) or (`t3`.`ccc2` like 'a%'))",
                "final_table_condition   ": "((`t3`.`ccc1` = `t1`.`c2`) or (`t3`.`ccc1` is null) or (`t3`.`ccc2` like 'a%'))"
              },
              {
                "table": "`t2`",
                "original_table_condition": "(((`t3`.`ccc1` = `t1`.`c2`) and (`t2`.`cc1` = `t1`.`c1`)) or (`t3`.`ccc1` is null) or (`t3`.`ccc2` like 'a%'))",
                "final_table_condition   ": "(((`t3`.`ccc1` = `t1`.`c2`) and (`t2`.`cc1` = `t1`.`c1`)) or (`t3`.`ccc1` is null) or (`t3`.`ccc2` like 'a%'))"
              }
            ]
          },
          {
            "refine_plan": [
              {
                "table": "`t1`"
              },
              {
                "table": "`t3`"
              },
              {
                "table": "`t2`"
              }
            ]
          }
        ]
      }
    }

如果条件带有RAND_TABLE_BIT的Item,那么即使cond带有表的列,也不会推送给对应的表,而是推送到最后一张表。看下面的t1.c1 < rand()这个条件。

例子二:

greatsql> SELECT * FROM t2,t1,t3 WHERE t1.c1=t2.cc1 AND t1.c2=t3.ccc1 OR t3.ccc1 IS NULL AND t1.c1 < rand();
              "attached_conditions_summary": [
                {
                  "table": "`t1`",
                  "attached": null
                },
                {
                  "table": "`t3`",
                  "attached": "((`t3`.`ccc1` = `t1`.`c2`) or (`t3`.`ccc1` is null))"
                },
                {
                  "table": "`t2`",
                  "attached": "(((`t3`.`ccc1` = `t1`.`c2`) and (`t2`.`cc1` = `t1`.`c1`)) or ((`t3`.`ccc1` is null) and (`t1`.`c1` < rand())))"  看到条件t1.c1 < rand()没有推送给t1而是推送到最后一张表t2去了
                }
              ]
            }
          },
          {
            "finalizing_table_conditions": [
              {
                "table": "`t3`",
                "original_table_condition": "((`t3`.`ccc1` = `t1`.`c2`) or (`t3`.`ccc1` is null))",
                "final_table_condition   ": "((`t3`.`ccc1` = `t1`.`c2`) or (`t3`.`ccc1` is null))"
              },
              {
                "table": "`t2`",
                "original_table_condition": "(((`t3`.`ccc1` = `t1`.`c2`) and (`t2`.`cc1` = `t1`.`c1`)) or ((`t3`.`ccc1` is null) and (`t1`.`c1` < rand())))",
                "final_table_condition   ": "(((`t3`.`ccc1` = `t1`.`c2`) and (`t2`.`cc1` = `t1`.`c1`)) or ((`t3`.`ccc1` is null) and (`t1`.`c1` < rand())))"
              }
            ]
          },
          {
            "refine_plan": [
              {
                "table": "`t1`"
              },
              {
                "table": "`t3`"
              },
              {
                "table": "`t2`"
              }

看一下每张表的属性:

CONDt1t3t2
best_ref->table_ref->map()0x0100x1000x001
best_ref->prefix_tables()INNER_TABLE_BIT+ OUTER_REF_TABLE_BIT+0x010INNER_TABLE_BIT+ OUTER_REF_TABLE_BIT+0x010+0x100INNER_TABLE_BIT+OUTER_REF_TABLE_BIT +RAND_TABLE_BIT+0x010+0x100+0x001
best_ref->added_tables()INNER_TABLE_BIT+ OUTER_REF_TABLE_BIT+0x0100x100RAND_TABLE_BIT+0x001
四、总结

从上面优化器最早的步骤我们认识了make_join_query_block函数的作用,知道了通过join表顺序和每张表的table_map属性以及cond条件的属性来决定cond条件添加到哪张表,并且可能会重新对表的索引进行check找出cost更低的索引,需要注意的是有的带有表列的条件不会被添加到对应表,因为Item的属性跟表的属性不一致所以最后只会被添加到最后一张join表。

Enjoy GreatSQL :)

关于 GreatSQL

GreatSQL是适用于金融级应用的国内自主开源数据库,具备高性能、高可靠、高易用性、高安全等多个核心特性,可以作为MySQL或Percona Server的可选替换,用于线上生产环境,且完全免费并兼容MySQL或Percona Server。

相关链接: GreatSQL社区 Gitee GitHub Bilibili

GreatSQL社区:

image

社区有奖建议反馈: https://greatsql.cn/thread-54-1-1.html

社区博客有奖征稿详情: https://greatsql.cn/thread-100-1-1.html

(对文章有疑问或者有独到见解都可以去社区官网提出或分享哦~)

技术交流群:

微信&QQ群:

QQ群:533341697

微信群:添加GreatSQL社区助手(微信号:wanlidbc )好友,待社区助手拉您进群。

内容概要:本文详细探讨了基于樽海鞘算法(SSA)优化的极限学习机(ELM)在回归预测任务中的应用,并与传统的BP神经网络、广义回归神经网络(GRNN)以及未优化的ELM进行了性能对比。首先介绍了ELM的基本原理,即通过随机生成输入层与隐藏层之间的连接权重及阈值,仅需计算输出权重即可快速完成训练。接着阐述了SSA的工作机制,利用樽海鞘群体觅食行为优化ELM的输入权重和隐藏层阈值,从而提高模型性能。随后分别给出了BP、GRNN、ELM和SSA-ELM的具体实现代码,并通过波士顿房价数据集和其他工业数据集验证了各模型的表现。结果显示,SSA-ELM在预测精度方面显著优于其他三种方法,尽管其训练时间较长,但在实际应用中仍具有明显优势。 适合人群:对机器学习尤其是回归预测感兴趣的科研人员和技术开发者,特别是那些希望深入了解ELM及其优化方法的人。 使用场景及目标:适用于需要高效、高精度回归预测的应用场景,如金融建模、工业数据分析等。主要目标是提供一种更为有效的回归预测解决方案,尤其是在处理大规模数据集时能够保持较高的预测精度。 其他说明:文中提供了详细的代码示例和性能对比图表,帮助读者更好地理解和复现实验结果。同时提醒使用者注意SSA参数的选择对模型性能的影响,建议进行参数敏感性分析以获得最佳效果。
《芋道开发指南文档-2023-10-27更新》是针对软件开发者和IT专业人士的一份详尽的资源集合,旨在提供最新的开发实践、范例代码和最佳策略。这份2023年10月27日更新的文档集,包含了丰富的模板和素材,帮助开发者在日常工作中提高效率,保证项目的顺利进行。 让我们深入探讨这份文档的可能内容。"芋道"可能是一个开源项目或一个专业的技术社区,其开发指南涵盖了多个方面,例如: 1. **编程语言指南**:可能包括Java、Python、JavaScript、C++等主流语言的编码规范、最佳实践以及常见问题的解决方案。 2. **框架与库的应用**:可能会讲解React、Vue、Angular等前端框架,以及Django、Spring Boot等后端框架的使用技巧和常见应用场景。 3. **数据库管理**:涵盖了SQL语言的基本操作,数据库设计原则,以及如何高效使用MySQL、PostgreSQL、MongoDB等数据库系统。 4. **版本控制**:详细介绍了Git的工作流程,分支管理策略,以及与其他开发工具(如Visual Studio Code、IntelliJ IDEA)的集成。 5. **持续集成与持续部署(CI/CD)**:包括Jenkins、Travis CI、GitHub Actions等工具的配置和使用,以实现自动化测试和部署。 6. **云服务与容器化**:可能涉及AWS、Azure、Google Cloud Platform等云计算平台的使用,以及Docker和Kubernetes的容器化部署实践。 7. **API设计与测试**:讲解RESTful API的设计原则,Swagger的使用,以及Postman等工具进行API测试的方法。 8. **安全性与隐私保护**:涵盖OAuth、JWT认证机制,HTTPS安全通信,以及防止SQL注入、
该是一个在 Kaggle 上发布的数据集,专注于 2024 年出现的漏洞(CVE)信息。以下是关于该数据集的详细介绍:该数据集收集了 2024 年记录在案的各类漏洞信息,涵盖了漏洞的利用方式(Exploits)、通用漏洞评分系统(CVSS)评分以及受影响的操作系统(OS)。通过整合这些信息,研究人员和安全专家可以全面了解每个漏洞的潜在威胁、影响范围以及可能的攻击途径。数据主要来源于权威的漏洞信息平台,如美国国家漏洞数据库(NVD)等。这些数据经过整理和筛选后被纳入数据集,确保了信息的准确性和可靠性。数据集特点:全面性:涵盖了多种操作系统(如 Windows、Linux、Android 等)的漏洞信息,反映了不同平台的安全状况。实用性:CVSS 评分提供了漏洞严重程度的量化指标,帮助用户快速评估漏洞的优先级。同时,漏洞利用信息(Exploits)为安全研究人员提供了攻击者可能的攻击手段,有助于提前制定防御策略。时效性:专注于 2024 年的漏洞数据,反映了当前网络安全领域面临的新挑战和新趋势。该数据集可用于多种研究和实践场景: 安全研究:研究人员可以利用该数据集分析漏洞的分布规律、攻击趋势以及不同操作系统之间的安全差异,为网络安全防护提供理论支持。 机器学习与数据分析:数据集中的结构化信息适合用于机器学习模型的训练,例如预测漏洞的 CVSS 评分、识别潜在的高危漏洞等。 企业安全评估:企业安全团队可以参考该数据集中的漏洞信息,结合自身系统的实际情况,进行安全评估和漏洞修复计划的制定。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值