【GreatSQL优化器-11】finalize_table_conditions
【GreatSQL优化器-11】finalize_table_conditions
一、finalize_table_conditions介绍
GreatSQL的优化器在对join做完表排序后,在make_join_query_block
函数对表添加条件,添加完条件在finalize_table_conditions
会对条件再次进行确认,对ref扫描的条件进行删除,对需要cache的条件进行替换,生成的条件就是表执行查询最后用的条件。
下面用一个简单的例子来说明finalize_table_conditions
做什么事情。
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); greatsql > EXPLAIN SELECT * FROM t1 JOIN t2 JOIN t3 ON t1.c1=t2.cc1 AND t1.c1=t3.ccc1 AND t3.ccc1<5; +----+-------------+-------+------------+--------+---------------+---------+---------+-----------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+--------+---------------+---------+---------+-----------+------+----------+-------------+ | 1 | SIMPLE | t1 | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 3 | 100.00 | Using where | | 1 | SIMPLE | t2 | NULL | eq_ref | PRIMARY | PRIMARY | 4 | db1.t1.c1 | 1 | 100.00 | NULL | | 1 | SIMPLE | t3 | NULL | ref | idx3_1 | idx3_1 | 5 | db1.t1.c1 | 1 | 100.00 | NULL | +----+-------------+-------+------------+--------+---------------+---------+---------+-----------+------+----------+-------------+ { "attaching_conditions_to_tables": { "original_condition": "((`t2`.`cc1` = `t1`.`c1`) and (`t3`.`ccc1` = `t1`.`c1`) and (`t1`.`c1` < 5))", "attached_conditions_computation": [ ], "attached_conditions_summary": [ { "table": "`t1`", "attached": "(`t1`.`c1` < 5)" }, { "table": "`t2`", "attached": "(`t2`.`cc1` = `t1`.`c1`)" }, { "table": "`t3`", "attached": "(`t3`.`ccc1` = `t1`.`c1`)" } ] } }, { "finalizing_table_conditions": [ { "table": "`t1`", "original_table_condition": "(`t1`.`c1` < 5)", "final_table_condition ": "(`t1`.`c1` < 5)" }, { "table": "`t2`", "original_table_condition": "(`t2`.`cc1` = `t1`.`c1`)", 原始添加的条件 "final_table_condition ": null 经过finalize_table_conditions以后得到的结果,这里条件被删除了 }, { "table": "`t3`", "original_table_condition": "(`t3`.`ccc1` = `t1`.`c1`)", 原始添加的条件 "final_table_condition ": null 经过finalize_table_conditions以后得到的结果,这里条件被删除了 } ] },
二、finalize_table_conditions代码解释
finalize_table_conditions
的操作在优化器的中后阶段,用来对之前生成的每张表的条件进行替换或者删除。
bool JOIN::optimize(bool finalize_access_paths) { if (finalize_table_conditions(thd)) return true; } bool JOIN::finalize_table_conditions(THD *thd) { // 遍历之前已经排序好的表,找到每张表的条件,然后进行裁剪 for (uint i = const_tables; i < tables; i++) { Item *condition = best_ref[i]->condition(); if (condition == nullptr) continue; // 这里进行条件删减,操作见下面表一 reduce_cond_for_table(); if (condition != nullptr) { condition = condition->compile( // 这个函数确认cond条件是否需要cache,true的话给carg->cache_item赋值,以便下面函数生成对应的Item_cache // 如果条件属性是INNER_TABLE_BIT并且不满足表二的话需要创建对应的Item_cache &Item::cache_const_expr_analyzer, (uchar **)&analyzer_arg, // 这个函数对于需要Item_cache的Item生成对应的Item_cache &Item::cache_const_expr_transformer, (uchar *)&cache_arg); trace_cond.add("final_table_condition ", condition); } } }
表一:reduce_cond_for_table操作
Item类型 | 操作 | 结果 |
---|---|---|
Item_func::COND_AND_FUNC | 递归reduce_cond_for_table() | 为空的话删除,与cond不同的话替换 |
Item_func::COND_OR_FUNC | 递归reduce_cond_for_table() | 与cond不同的话替换 |
Item_func::TRIG_COND_FUNC | 递归reduce_cond_for_table() | 与cond不同的话替换 |
Item_func::EQ_FUNC | 通过test_if_ref()判断该条件是否使用ref方式扫描※重要 | true返回空,false返回原始cond |
其他类型 | 不操作 | 直接返回原始cond |
表二:不能生成Item cache的Item
序号 | Item类型 |
---|---|
1 | 常数类型 |
2 | 表的列 |
3 | 子查询 |
4 | ROW对象 |
5 | prepare的参数 |
6 | 已经被cache了 |
三、实际例子说明
接下来看几个例子来说明上面的代码:
greatsql > EXPLAIN SELECT * FROM t1 JOIN t2 JOIN t3 ON t1.c1=t2.cc1 AND t1.c1=t3.ccc1 AND t3.ccc1<5; { "plan_prefix": [ ], "table": "`t1`", "best_access_path": { "considered_access_paths": [ { "access_type": "ref", "index": "PRIMARY", "usable": false, "chosen": false }, { "rows_to_scan": 7, "filtering_effect": [ ], "final_filtering_effect": 0.428571, "access_type": "scan", "resulting_rows": 3, "cost": 1.7, "chosen": true } ] }, "condition_filtering_pct": 100, "rows_for_plan": 3, "cost_for_plan": 1.7, "rest_of_plan": [ { "plan_prefix": [ "`t1`" ], "table": "`t2`", "best_access_path": { "considered_access_paths": [ { "access_type": "eq_ref", 确定t2使用了ref方式扫描并且用到了主键索引 "index": "PRIMARY", "rows": 1, "cost": 3.3, "chosen": true, "cause": "clustered_pk_chosen_by_heuristics" }, { "access_type": "range", "range_details": { "used_index": "PRIMARY" }, "chosen": false, "cause": "heuristic_index_cheaper" } ] }, "condition_filtering_pct": 100, "rows_for_plan": 3, "cost_for_plan": 5, "rest_of_plan": [ { "plan_prefix": [ "`t1`", "`t2`" ], "table": "`t3`", "best_access_path": { "considered_access_paths": [ { "access_type": "ref", 确定t3使用了ref方式扫描并且用到了idx3_1索引 "index": "idx3_1", "rows": 1, "cost": 1.05, "chosen": true }, { "access_type": "range", "range_details": { "used_index": "idx3_1" }, "cost": 2.06, "rows": 4, "chosen": false, "cause": "cost" } ] }, "added_to_eq_ref_extension": false }, { "finalizing_table_conditions": [ { "table": "`t1`", "original_table_condition": "(`t1`.`c1` < 5)", "final_table_condition ": "(`t1`.`c1` < 5)" }, { "table": "`t2`", "original_table_condition": "(`t2`.`cc1` = `t1`.`c1`)", 这里发现t2.cc1等号条件用到了ref方式扫描,因此被裁剪了 "final_table_condition ": null 条件被删除 }, { "table": "`t3`", "original_table_condition": "(`t3`.`ccc1` = `t1`.`c1`)", 这里发现t3.ccc1等号条件用到了ref方式扫描,因此被裁剪了 "final_table_condition ": null 条件被删除 } ] }, { "refine_plan": [ { "table": "`t1`" }, { "table": "`t2`" }, { "table": "`t3`" } ]
下面加一个带有INNER_TABLE_BIT属性的Item条件,看看条件转换后的结果。
greatsql> SELECT * FROM t1 JOIN t2 JOIN t3 ON t1.c1=t2.cc1 AND t1.c1=t3.ccc1 WHERE t1.c2<@@optimizer_search_depth; { "attaching_conditions_to_tables": { "original_condition": "((`t2`.`cc1` = `t1`.`c1`) and (`t3`.`ccc1` = `t1`.`c1`) and (`t1`.`c2` < ))", "attached_conditions_computation": [ ], "attached_conditions_summary": [ { "table": "`t1`", "attached": "(`t1`.`c2` < )" }, { "table": "`t2`", "attached": "(`t2`.`cc1` = `t1`.`c1`)" }, { "table": "`t3`", "attached": "(`t3`.`ccc1` = `t1`.`c1`)" } ] } }, { "finalizing_table_conditions": [ { "table": "`t1`", "original_table_condition": "(`t1`.`c2` < )", "final_table_condition ": "(`t1`.`c2` < <cache>())" 这里看到条件里面的系统变量被转变为cache了 }, { "table": "`t2`", "original_table_condition": "(`t2`.`cc1` = `t1`.`c1`)", "final_table_condition ": null }, { "table": "`t3`", "original_table_condition": "(`t3`.`ccc1` = `t1`.`c1`)", "final_table_condition ": null } ] },
以下例子也会把条件转换为cache,因为f1(1)是INNER_TABLE_BIT属性,如果改为f1(t1.c2)就不能转为cache了,因为f1(t1.c2)是NO DETERMINISTIC不确定的,非INNER_TABLE_BIT属性。
SET GLOBAL log_bin_trust_function_creators=1; SET sql_mode=ORACLE; DELIMITER $$ CREATE OR REPLACE FUNCTION f1 (id int) RETURN INT DETERMINISTIC IS BEGIN RETURN id; END; $$ DELIMITER ; greatsql> SELECT * FROM t1 JOIN t2 JOIN t3 ON t1.c1=t2.cc1 AND t1.c1=t3.ccc1 WHERE t1.c2<f1(1);
四、总结
从上面优化器的步骤我们认识了finalize_table_conditions
函数的使用方法,也知道了什么时候表的条件需要进行删除或者转换,最后学会了Item cache的生成条件。到这里一个优化器的工作快要结束了,最后还有一个临时表需要创建,这个下一期讲。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
开箱你的 AI 语音女友「GitHub 热点速览」
随着大模型 API 服务的不断丰富,开发者无需再依赖昂贵的硬件,也能轻松开发出拥有强大 AI 能力的应用。这不仅降低了技术门槛,也激发了极客们的创造力。 就比如上周飙升 1.5k Star 的开源项目 xiaozhi-esp32,仅用低成本的 ESP32 开发板和 LLM API 服务,就能制作出一个聪明有趣、可实时对话的 AI "女友"(语音聊天机器人)。同样好玩的 MagicMirror,它能够帮助你打造个性化的智能镜,支持各种扩展模块和手势操控。把视线转回到程序员的工具箱,换一款能让你心情愉悦的终端工具吧!Tabby 不仅拥有超高的颜值,更是集成了各种实用功能。试试更快的 Jupyter Notebook IDE------zasper,你一定会为它的高性能表现感到惊叹(多任务并发)。 最后,这款可以在云存储上实现亚秒级搜索速度的开源搜索引擎,以丝滑的迁移体验和更低的维护成本脱颖而出,可作为 ES 和 Loki 的替代品。 本文目录 热门开源项目 1.1 基于 ESP32 的 AI 聊天机器人 :xiaozhi-esp32 1.2 更快的 Notebook IDE:zasper ...
- 下一篇
面向法律场景的大模型RAG检索增强解决方案
概述 在现代信息检索领域,检索增强生成(Retrieval-Augmented Generation, RAG)模型结合了信息检索与生成式人工智能的优点,从而在特定场景下提供更为精准和相关的答案。在特定场景下,例如法律等领域,用户通常需要精确且相关的信息来支持决策。传统生成模型虽然在自然语言理解和生成方面表现良好,但在专业知识的准确性上可能有所不足。RAG模型通过将检索与生成相结合,能有效提升回答的准确性和上下文相关性。本方案以人工智能平台PAI为基础产品,为您介绍面向法律场景的大模型RAG检索增强解决方案。 使用PAI-Designer构建知识库 您可以参照数据格式要求准备,使用PAI-Designer构建相应的检索知识库。 使用PAI-LangStudio进行模版构建 您在LangStudio中使用预置的RAG模版进行定制化,创建适合具体应用的模板。 使用PAI-Langstudio构建在线应用 LangStudio提供了用户友好的界面,使用户能够轻松提交查询并获取答案。您可以使用创建好的模板构建符合业务需求的在线应用。 一、前置准备 在开始执行操作前,请确认您已完成以下准备工作:...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2全家桶,快速入门学习开发网站教程
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- 2048小游戏-低调大师作品
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS6,CentOS7官方镜像安装Oracle11G
- CentOS关闭SELinux安全模块