Zombodb执行引擎
0.前言
我们在使用Zombodb时,会使用一些SQL查询,例如:
CREATE EXTENSION zombodb;
DROP EXTENSION zombodb;
CREATE INDEX idxtest_analyze_text ON test_analyze_text USING zombodb ((test_analyze_text.*));
DROP TABLE idxtest_analyze_text;
ALTER INDEX idxbook SET (options='id=<public.book_content.idxcontent>book_id');
SELECT *
FROM book
WHERE book ==> 'author:shakespeare and users.full_name:"John Doe"'
这里罗列的SQL不全,只是给大家简单看一下Zombodb的基本用法,像这些SQL从PostgreSQL(以下简称:PG)传递过来,到底经历了什么流程?上面不同类别的SQL语句到底会怎么去执行?ES的Bulk请求的上游是谁?谁来管理ES请求?==>
操作符又是怎么识别的?
带着这些疑问,我们来开始Zombodb执行引擎。
Zombodb执行引擎不像PG那样复杂,能搞若干年,Zombodb的执行引擎简单用一幅图概括其核心工作。
PG优化器、执行器重写
后面文章会详细展开
ES操作API
没错,这里就是来管理上一篇文章写的ES Bulk请求。
看样子是比简单的,但是深挖下去,这一篇文章搞不定,所以我先来个执行引擎轮廓吧。
1.Hooks
Zombodb执行引擎管理了很多Hooks,Zombodb在全局有一个可变的单例对象,所有的操作都是通过EXECUTE_MANAGER
来发起调用。
简单说一下Hooks是什么,PG代码里面会提供给第三方回调接口,这便是Hooks,逻辑为:
if (hook) {
your_hook();
} else {
default_behavior();
}
这里写了一段伪代码,如果你定义了hook那么就会走你的逻辑,不然就走PG默认行为,所以Hooks就是一堆这种hook的组合。Zombodb这边实现了:
优化器Planner
优化器里面做了不少工作,例如:ctid重写,表达式重写等等,这一块后面打算单独讲。
PG执行器开始 executor_start
执行器开始在Zombodb这边实现比较简单,就是内部维护了一个query_stack结构,往里面插入即可,query_stack结构在本文后续讲,这里理解为一个Vector即可。
PG执行器结束 executor_end
执行器结束在Zombodb这边实现就更简单了,直接从Vector的query_stack中pop出去,就完事了。
可以看到PG执行器的Hook逻辑就是类似于现场还原的一个实现。
工具类语句hook process_utility_hook
这里就是实现我们修改表、修改索引别名、删除操作。这里实现比较简单了,看一下代码就懂了。
当然还有其他我们内部定制的Hook,这些就不赘述。
2.执行引擎
接下来就是Zombodb执行引擎的结构了,内部包括:
每个表的元组描述符
元组描述符在这里表示你查询的时候的某些列信息。例如:这里有一个普通的查询,这里id就是tupledesc,另一个工具类查询语句结果中的QUERY PLAN就是tupledesc,这些内容会在PG里面用TupleDescData存起来。
每个表的bulk请求上下文
bulk请求是什么,可以看上一篇文章,这里存储的是BulkContex数据结构,这个数据结构在Zombodb里面结构如下图所示,具体可以看图中注释,没有提到的便是is_shadow字段,这个表示当前索引是否是影子索引。
所谓影子索引表示的它是否被包了一层,然后用户直接看不到,需要回溯看UDF,才理解。例如下面这个便是影子索引。
---非影子索引
SELECT *
FROM book
WHERE book ==> 'shakespeare';
---影子索引
SELECT *
FROM book
WHERE my_shadow_func(book) ==> 'shakespeare';
CREATE OR REPLACE FUNCTION my_shadow_func(anyelement)
RETURNS anyelement
IMMUTABLE STRICT
LANGUAGE c AS '$libdir/zombodb.so', 'shadow_wrapper';
使用过的所有事务id
一个完整的事务可能包括事务发起、事务中、事务提交、事务回滚等其中多个步骤。而Zombodb执行引擎里面使用了一个hashset存储之前已经提交的所有事务id,防止重复提交。对外的API如下:
事务开始:push_xid,将当前事务id放入hashset中,并发起Bulk事务正在进行的请求。
事务提交:commit,等待所有hashset中的事务id被提交,如果有失败,就会panic掉,提交后,释放执行引擎的所有成员占用的内存。
事务回滚:abort,给ES Bulk发起立刻终止请求并释放执行引擎的所有成员占用的内存。
所有查询的query_desc
这个就是前面提到的query_stack,有一个数组维护,里面存储了一个tuple(查询描述符,查询状态)。
事务回调是否注册标记
在PG/GP里面会有回调函数,用户可以进行注册,例如:两阶段提交中/后的处理回调,都可以自定义逻辑,例如:在Zombodb里面实现了PreCommit、Abort,这里便会调用前面的commit与abort。
设置完毕这些回调函数,需要设置注册标记为true,因为这些只需要注册一次就行(执行引擎对象是全局单例)。
3.查询状态
Zombodb中提供了两个比较特殊的函数:
zdb.scores
zdb.highlights
一个可以用来返回ES的score,并放到order by之后用来排序。
另一个是使用ES的文档高亮特性,对查询结果进行高亮显示。
为何这里会有一个查询状态呢?单独还放到了执行引擎里面,像其他的zdb查询缺没有放到执行引擎里面,例如:
zdb.terms
要回答这个问题,首先我们需要知道这两个函数的实现入参都有一个ctid,而根据Zombodb代码中的注释与调用入口,可以发现这两个放到执行引擎里面,是为了方便解决HOT问题。
更复杂的解释,等后面研究一下这里面的门道吧,还有不少东西探究。
本节完~