PolarDB-MySQL 新特性 - Partial Result Cache

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 背景查询缓存(Query Cache)是数据库执行层的一个加速查询的特性,用来缓存一条查询语句的结果集,如果后续再有相同的查询,直接从结果集缓存中读取结果,而不用再重新执行而极大提升查询性能。但Query Cache在实际业务使用中存在较多的局限性,首先能够命中Query Cache的规则非常严格,必须是完全相同的SQL语句,并且被查询的表的数据不能有任何的修改,有任意规则不符合要求都会造成cac

背景

查询缓存(Query Cache)是数据库执行层的一个加速查询的特性,用来缓存一条查询语句的结果集,如果后续再有相同的查询,直接从结果集缓存中读取结果,而不用再重新执行而极大提升查询性能。

但Query Cache在实际业务使用中存在较多的局限性,首先能够命中Query Cache的规则非常严格,必须是完全相同的SQL语句,并且被查询的表的数据不能有任何的修改,有任意规则不符合要求都会造成cache失效。其次,Query Cache必须缓存完整的查询结果,如果查询结果集比较大,缓存需要占据较大的内存或者写入磁盘。如果用户业务场景是数据更新不频繁,又有不少重复的SQL语句,那提升效果会非常的显著,但显然,这样的用户场景并不多,大部分线上业务数据都有实时的写入或更新,Query Cache的命中率很难得到保证。

但在一条查询语句中内部,同样存在一些算子会被反复计算的情况(比如Correlated subquery,NestLoopJoin),即便是驱动算子执行的相关参数项有完全重复的情况,这些算子也依然需要被执行多次,而这些算子一般执行代价都较高,重复计算造成了大量资源的浪费。如果能将这些需要重复计算的算子的结果集缓存起来,优先查看缓存中是否已经存在,来尽量避免算子的重复计算,就可以极大提升查询效率。

鉴于结果集缓存能够带来非常可观的加速效果,我们在PolarDB-MySQL引入了一个适用范围更广的算子级别结果集缓存的新特性Partial Result Cache,简称PTRC。

什么是PTRC

Partial result Cache,是针对一个查询内部算子级别粒度的结果集缓存特性,比如缓存Correlated subquery或NestLoopJoin等算子执行的临时结果,后续再次执行该算子时,如果结果集缓存中已存在,则无需再重复执行该算子。

PTRC中的Partial有两层含义:

  1. PTRC缓存的不是整个查询的结果集,而是查询内部某个或某几个算子执行的中间结果的缓存。
  2. 并不一定缓存算子所有的中间结果,而是根据内存限制可能只缓存部分中间结果。

相较于传统的Query Cache,PTRC的缓存粒度更小,针对的是查询内部某些算子进行加速,并且可以看出PTRC的生命周期也是跟随查询一起开始和结束的。基于上述原理可以看出,PTRC的适用范围会更广,因为只针对查询内部算子的优化,不存在跨查询的数据一致性的问题,只要一个算子符合要求(即算子执行时依赖的参数不变,执行结果也不会变)就可以使用PTRC,当然我们在选择PTRC时不仅靠规则,还会基于代价决策是否使用PTRC。

PTRC的工作原理

PTRC的核心思想是利用中间结果缓存避免某些算子的重复执行,那哪些算子可以被PTRC加速呢?可以被PTRC加速的算子需要满足如下条件:

  1. 算子执行是有相关性参数(Correlated Parameters)的依赖,并且会被反复多次执行,比如NestLoopJoin、相关子查询;
  2. 算子的相关性参数保持不变,无论执行多少次,算子执行的结果是稳定不变的;比如算子中不能有random, now, udf等影响算子重复执行结果稳定性的函数,不然缓存会影响结果正确性。

解释下算子执行的Correlated Parameters,本质就是算子执行所依赖的外部参数,例如 t1 join t2 on t1.a = t2.a,t1表作为驱动表,t1表的每一行都要和t2表完成一次join操作,而 t1.a 则被认为是该NestLoopJoin的Correlated Parameters。如果t1.a 在t1表中存在较多的重复值,那PTRC将会减少这部分的重复计算。

再比如correlated subquery,子查询的每一次执行,都依赖于父查询的一次扫描的结果的基础上作为驱动参数,我们以TPCH-Q17示例说明下PTRC的基本工作原理。

TPCH-Q17:

select
        sum(l_extendedprice) / 7.0 as avg_yearly
from
        lineitem,
        part
where
        p_partkey = l_partkey
        and p_brand = 'Brand#34'
        and p_container = 'MED BOX'
        and l_quantity < (
                select
                        0.2 * avg(l_quantity)
                from
                        lineitem
                where
                        l_partkey = p_partkey
        );

PTRC中会以算子的Correlated Parameters作为key,算子的执行结果作为value存储在缓存中,故Q17中PTRC的缓存存储格式为:key= p_partkey, value = [true/false] 。

Q17中相关子查询的PTRC的主要执行流程如下图所示,当每次要对相关子查询求值时,根据Correlated Parameters (p_partkey)的值在PTRC中查找是否命中,如果未命中,需要真正执行子查询进行求值,并将新求值的结果记录到PTRC的缓存中;如果命中缓存,直接将结果返回,则避免了一次子查询的重复执行。因为Q17是part表join lineitem表后再执行子查询,join之后的结果中p_partkey重复项非常多,而p_partkey又是子查询的correlated parameters,所以Q17的PTRC命中率会很高,性能提升会非常显著。

使用explain可以查看计划发生的变化,在子查询真正执行前多了一个Partial Result Cache算子,则说明该子查询正式引入了PTRC,如下图所示:

基于代价选择PTRC

从PTRC执行流程可以了解到,引入PTRC也并不是多多益善,关键是取决于PTRC的命中率,如果命中率不高,引入PTRC也会带来额外的性能开销,比如每一次都要Check Cache,以及额外的内存占用开销等。

为避免不必要的开销,优化器会基于代价的选择是否使用PTRC,主要考虑两个维度:

  1. 当查询整体cost高于partial_result_cache_cost_threshold时,才考虑引入PTRC;
  2. 评估算子使用PTRC的命中率,命中率高于partial_result_cache_low_hit_rate时,才考虑引入PTRC;

优化器会先检查partial_result_cache_cost_threshold,当查询的整体cost低于该阈值时,我们认为该查询的执行代价比较低,本身应该就是一个短查询,即便引入PTRC后提升也会比较有限,且额外的Check Cache等操作可能还会对高并发下的短事务类查询的latency造成负面影响,所以提供了一个总的代价阈值控制是否完全忽略PTRC。而且当该阈值不满足时,优化器阶段枚举所有表达式寻找是否使用PTRC的开销也省掉了。

当查询cost满足partial_result_cache_cost_threshold时,优化器会遍历检查所有可能满足PTRC规则的算子,并对满足规则的算计估算它的PTRC 命中率,计算公式为 hit_rate = (fanout - ndv)/ fanout

命中率估算相关概念:

  1. fanout, 一个算子需要被重复执行的总次数。
  2. ndv,PTRC的key的唯一值的个数,也就是Correlated Parameters所有参数组合值的唯一值个数。

当评估出的hit_rate低于partial_result_cache_low_hit_rate时,这个算子就不再考虑使用PTRC。但在Mysql已有代价模型中,它的统计信息非常依赖于表的索引或直方图,如果Correlated Parameters这些相关的列上没有索引或直方图,很难评估出非常准的ndv,在这种情况下我们会尽量引入PTRC,然后交给PTRC执行阶段的动态反馈机制来动态检查是否有必要继续使用PTRC。

PTRC的动态反馈机制

在PTRC执行阶段,每次Cache是否命中都会记录到统计信息中,动态反馈机制负责对PTRC的真实命中率进行计算,如果发现实际命中率低于partial_result_cache_low_hit_rate时,则会直接在执行阶段禁掉PTRC,恢复到没有引入PTRC前的执行状态,用来避免低效的PTRC带来的额外开销。

partial_result_cache_check_frequency 是控制PTRC动态检测命中率频率的参数,它的含义是累计出现cache miss的次数,比如默认值为200,每当cache miss的次数累计达到200次后,就会触发一次动态反馈检测。

因为结果集是缓存在内存中,当PTRC的内存使用量达到上限时,同样也会触发动态反馈机制,但此时的动态反馈不仅要检查命中率是否过低,还会做数据淘汰或者是否转储到Disk的决策。

当内存达到上限时的动态反馈策略为:

  1. 当hit_rate低于partial_result_cache_low_hit_rate时,认为命中率过低,直接禁用PTRC;
  2. 当hit_rate高于partial_result_cache_high_hit_rate时,命中率大于高水位,会将内存中缓存数据转存至Disk存储,认为即便是转为Disk存储,依然会有可预期的性能提升;
  3. 当hit_rate处于低水位和高水位之间时,则会触发LRU数据淘汰,将命中率不符合预期的数据清理掉,后续重新缓存新的数据;若新缓存数据后再次触发内存不足,重复上述几个步骤的检测;

partial_result_cache_max_mem_size 是限制单个查询PTRC最大使用内存的配置参数,因为单个查询内部可能有多个算子使用PTRC,这些PTRC需要共享累计所占用的全部内存空间,一旦查询内总的内存使用超过限制,所有的PTRC都将会触发上述的动态反馈机制。

PTRC的参数配置

下面详细介绍一下PTRC相关的几个配置参数:

变量名

级别

说明

partial_result_cache_enabled

Global/Session

Partial Result Cache的开关。

partial_result_cache_cost_threshold

Global/Session

优化器是否考虑引入PTRC的代价阈值,单个查询的整体cost超过该阈值,才开始考虑是否可以使用PTRC。

partial_result_cache_check_frequency

Global/Session

触发PTRC的动态反馈检测的频率,频率是每累计cache miss的次数达到该值时触发一次检测。

partial_result_cache_low_hit_rate

Global/Session

PTRC命中率的低水位阈值。当优化器估算的命中率高于此值时才考虑使用PTRC;如已使用PTRC,动态反馈机制中发现真实命中率低于该值时,将直接放弃继续使用PTRC。

partial_result_cache_high_hit_rate

Global/Session

PTRC命中率的高水位阈值。当内存使用达到上限并且命中率高于此值时,内存缓存变更为文件存储缓存,已缓存的数据也会转存至文件中。

partial_result_cache_max_mem_size

单个查询中PTRC最大允许的内存使用量,一个查询内部可能有多个PTRC,多个PTRC累计使用的内存不能超过这个限制大小。

PTRC性能表现

通过前述基于代价选择PTRC的描述,可以看出影响PTRC的加速效果的主要有如下两个因素:

  1. 使用PTRC加速的算子的执行代价要足够大,如果算子本身执行代价不高,缓存加速的提升空间就有限;
  2. 缓存命中率,PTRC的缓存命中率越高,加速效果越明显;

我们再以TPCH-Q17测试为例:

相关子查询引入PTRC后的查询计划后:

经过测试统计信息计算,TPCH-Q17相关子查询引入PTRC的缓存命中率达96%,缓存加速效果非常明显,测试数据如下图:

总结

PTRC是一种缓存优化,针对的是单个查询内部的具有相关性参数依赖的复杂算子,通过缓存算子执行的中间结果集来避免这些复杂算子的重复计算,只要命中率足够,就可以获得非常可观的加速效果。目前查询中Correlated Subquery, Nested Loop Join (包含Inner join, Outer join, Semi join, Anti join) 等多种算子均可以使用PTRC进行加速,PTRC新特性将在PolarDB for MySQL 8.0.2.2.10版本开始支持,欢迎大家使用。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
7天前
|
存储 关系型数据库 MySQL
MySQL 8.0特性-自增变量的持久化
【11月更文挑战第8天】在 MySQL 8.0 之前,自增变量(`AUTO_INCREMENT`)的行为在服务器重启后可能会发生变化,导致意外结果。MySQL 8.0 引入了自增变量的持久化特性,将其信息存储在数据字典中,确保重启后的一致性。这提高了开发和管理的稳定性,减少了主键冲突和数据不一致的风险。默认情况下,MySQL 8.0 启用了这一特性,但在升级时需注意行为变化。
|
2月前
|
JSON 关系型数据库 MySQL
MySQL 8.0 新特性
MySQL 8.0 新特性
147 10
MySQL 8.0 新特性
|
2月前
|
存储 Oracle 关系型数据库
Oracle和MySQL有哪些区别?从基本特性、技术选型、字段类型、事务、语句等角度详细对比Oracle和MySQL
从基本特性、技术选型、字段类型、事务提交方式、SQL语句、分页方法等方面对比Oracle和MySQL的区别。
498 18
Oracle和MySQL有哪些区别?从基本特性、技术选型、字段类型、事务、语句等角度详细对比Oracle和MySQL
|
1月前
|
SQL 安全 关系型数据库
MySQL8.2有哪些新特性?
【10月更文挑战第3天】MySQL8.2有哪些新特性?
35 2
|
3月前
|
算法 关系型数据库 MySQL
一天五道Java面试题----第七天(mysql索引结构,各自的优劣--------->事务的基本特性和隔离级别)
这篇文章是关于MySQL的面试题总结,包括索引结构的优劣、索引设计原则、MySQL锁的类型、执行计划的解读以及事务的基本特性和隔离级别。
|
3月前
|
SQL 算法 关系型数据库
(二十)MySQL特性篇:2022年的我们,必须要懂的那些数据库新技术!
 MySQL数据库从1995年诞生至今,已经过去了二十多个年头了,到2022.04.26日为止,MySQL8.0.29正式发行了GA版本,在此之前版本也发生了多次迭代,发行了大大小小N多个版本,其中每个版本中都有各自的新特性,所有版本的特性加起来,用一本书的篇幅也无法完全阐述清楚,因此本章主要会挑重点特性来讲,具体各版本的特性可参考MySQL官网的开发手册。
148 1
|
5月前
|
关系型数据库 MySQL 测试技术
深入探索MySQL 8:隐藏索引与降序索引的新特性
深入探索MySQL 8:隐藏索引与降序索引的新特性
|
5月前
|
SQL JSON 关系型数据库
MySQL 8.0新特性?
【6月更文挑战第14天】MySQL 8.0新特性?
50 1
|
5月前
|
SQL 关系型数据库 MySQL
|
5月前
|
存储 关系型数据库 MySQL
mysql的InnoDB引擎实现ACID特性的原理
mysql的InnoDB引擎实现ACID特性的原理