GaussDB解读:Partial Result Cachev缓存中间结果对算子进行加速

GaussDB解读:Partial Result Cachev缓存中间结果对算子进行加速
文章图片
为了加速查询性能 , 传统的关系型数据库 , 比如Oracle、DB2 , 都有结果集缓存的特性 , 用来缓存一条查询语句的结果集 。 如果后续同样的语句被查询 , 数据库将直接从结果集缓存中获取结果 , 而不用再重新执行该查询 。 MySQL在4.0版本中也引入了结果集缓存Querycache , 但是在设计上有局限性 , 具体如下:
这也导致了该特性在MySQL8.0版本被移除 。
鉴于结果集缓存对查询性能的增益 , 我们在GaussDB(forMySQL)引入Partialresultcache这一新特性 , 简称PTRC 。 顾名思义 , 这也是一个结果集缓存特性 。 不同于传统的结果集缓存 , PTRC是用来辅助单个查询的内部算子的执行 。 也就是说PTRC粒度更小 , 是对查询内部的某个算子的中间结果进行缓存 , 从而起到算子加速的作用 。
这里的Partial有两层概念:
从这两点可以看出 , PTRC是与单个查询相关的 , 生命周期从查询开始到查询结束 , 自动终止 。 由于它是对算子进行加速 , 所以一个查询内部可以有多个PTRC 。 只要优化器根据代价计算 , 认为该算子适合PTRC , 那么优化器就会为该算子引入PTRC 。
PTRC如何确定对算子并加速?
这里我们引入一个新概念:参数化的重复扫描 , 指的是扫描算子根据参数的不同进行算子扫描 。 比如NestedLoopJoin , 对于外表扫描的每一条数据 , 内表会根据JOIN条件进行扫描 , 那么对于内表来说就是一次“参数化的重复扫描” 。 再比如correlatedsubquery , 对于父查询的每一次扫描都会根据父查询的结果集调用子查询执行 , 然后返回子查询的结果集 。
PTRC是如何工作的?
如前所述 , PTRC是缓存算子的中间结果集 , 那么也和其他cache一样 , 将数据以key , value的方式缓存到cache中 , 通过key来命中 , 得到value 。 那么PTRC的相关key和value是如何获取的?
下面我们以Correlatedsubquery为例做简单分析 , 查询语句如下:
SELECT*FROMt1WHEREt1.aIN(SELECTaFROMt2,t3WHEREt2.b=t1.bANDt2.c>t3.d);
GaussDB解读:Partial Result Cachev缓存中间结果对算子进行加速
文章图片
上图是子查询使用EXISTS策略执行的流程图 。 可以看出:对于数据表t1中的每一条数据 , 都会驱动子查询执行 , 直到数据表t1中的所有记录都循环结束 。 对于数据表t1中的每一条记录对应的t1.a , 都需要根据该列值重新扫描子查询 , 进而判断子查询的返回值 。
我们通过EXPLAIN来对比引入PTRC前后执行计划的差异:
EXPLAINformat=tree
SELECT*FROMt1WHEREt1.aIN(SELECTaFROMt2,t3WHEREt2.b=t1.bANDt2.c>t3.d);
->Filter:(t1.a,(select#2))(cost=0.35rows=1)
->Tablescanont1(cost=0.35rows=1)
->Select#2(subqueryincondition;dependent)
->Resultcache:cachekeys(t1.a,t1.b)
->Limit:1row(s)(cost=0.80rows=1)
->Filter:(t2.c>t3.d)(cost=0.80rows=1)
->Innerhashjoin(nocondition)(cost=0.80rows=1)
->Tablescanont3(cost=0.35rows=2)
->Hash
->Filter:((t2.b=t1.b)and((t1.a)=t2.a))(cost=0.35rows=1)
->Tablescanont2(cost=0.35rows=1)
可以看出引入PTRC后 , 多了一个算子Resultcache(标红部分) , 表明该算子当前的子查询引入了PTRC , 引入后的执行流程变更为:
GaussDB解读:Partial Result Cachev缓存中间结果对算子进行加速
文章图片
引入PTRC后 , 对于数据表t1中的每一条数据对应的t1.a列值 , 优先查看PTRC , 如果命中 , 直接从PTRC中获取结果集 , 而不需要执行子查询 。 如果未命中 , 需要按原来的方式继续执行子查询 , 子查询执行的结果会储存到PTRC中 。 如果下一次同样的列值来驱动执行子查询 , 可以直接从PTRC获取 。