一文讲述数仓组件 SysCache
摘要:SysCache 是 ThreadLocal 结构,每个线程都具有各自的 SysCache,其中存储的缓存信息由执行的业务决定。
本文分享自华为云社区《GaussDB(DWS)CBB组件之SysCache原理介绍》,作者:疯狂朔朔。
SysCache 是什么?我们为什么需要 SysCache?
在说明这个问题之前,我们需要简单说明一下 GaussDB(DWS)的基本工作原理。
在我们链接数据库后,数据库将在后台为我们分配一根单独的线程,该线程负责执行我们发送的请求,假设我们按顺序发送以下语句:
a. create table abc (a int, b int, c int);
b. insert into abc values (1, 2, 3);
c. insert into abc values (2, 3, 4);
d. insert into abc values (3, 4, 5);
在创建表 abc 后,连续向 abc 中插入了 3 条数据,每条插入语句均需要访问 abc 的元数据,包括但不仅限于:
i. pg_authid:读取用户相关信息,
ii. pg_resource_pool:读取资源池相关信息,
iii. pg_class:读取表相关元数据,
iv. pgxc_group:读取 nodegroup 相关信息,
v. pg_namespace:读取 schema 相关信息,
vi. pg_type:读取字段类型,
vii. pg_attribute:读取表列属性信息。
可见,虽然简单执行了一条 insert 语句,其中涉及到的解析、校验逻辑是异常复杂的,会关联大量系统表相关查询操作。为了加快系统表查询速率,GaussDB(DWS)中针对系统表查询操作构建了 SysCache 缓存,以加速系统表查询速率。在上述例子中,语句 b)在执行时,会访问相关系统表元数据,并通过 SysCache 进行缓存,之后在语句 c)和语句 d)执行时,直接从 SysCache 获取相应的缓存信息,以加速执行效率。
NOTE:实际上,GaussDB(DWS)中 SysCache 与 PG 的 SysCache 原理一致,不同之处在于,GaussDB(DWS)做了线程化改造,将 PG 多进程模型改为多线程模型。
NOTE:一句话解释 SysCache,SysCache 是 GaussDB(DWS)内核系统表 Tuple 高速缓存。
SysCache 存储结构
SysCache 是 ThreadLocal 结构,每个线程都具有各自的 SysCache,其中存储的缓存信息由执行的业务决定。下图展示了 SysCache 的大体结构:
图 1 SysCache 结构
SysCache 在内存中以指针数组的形式进行存储,数组中每个元素均为一个 CatCache 结构体,CatCache 中存储了系统表中的元数据信息。在 CatCache 中可能存在大量被缓存的数据,为加速查找,CatCache 中设计了哈希数组,哈希数组包含多个哈希桶,每个哈希桶中记录了元数据链表信息,最终元数据是以双向链表的形式存于哈希桶中的。
以一个例子进行说明:假设执行以下语句:select * from public.abc;
系统在执行该语句时,在 SysCache 中会经历以下三步:
1. 查找 pg_class 对应的 CatCache:该语句的输入参数有三个,从 pg_class 查询 Schema 名为 public,表名为 abc 的元数据信息。因此,执行该语句时,会直接查询 pg_class 对应的 CatCache。
2. 计算哈希值:通过输入参数 public 和 abc 计算其对应的哈希值,定位哈希桶的位置。
3. 遍历哈希桶:遍历哈希桶中元数据信息,若匹配到 public.abc 的元数据,则直接返回该数据,若未匹配到,则需要进一步扫描 pg_class,以读取元数据信息。
NOTE:一句话解释 SysCache 存储结构:SysCache 存储结构本质上是多个哈希桶,每个哈希桶中元数据是以双向链表的形式进行存储。
SysCache 对外接口
SysCache 对外接口主要包括两个:查询 SysCache 和释放 SysCache,这两个方法需要配对使用,在 SysCache 返回查询结果后,需要再次调用释放 SysCache 接口,以释放 SysCache 相关数据。
下图展示了查询 SysCache 的主要步骤:
图 2 查询 SysCache 流程图
在查询 SysCache 前,需要确保 CatCache 初始化,之后,需要依据查询的 key 信息计算哈希值,以定位哈希桶位置。对于 SysCache 而言,数据的 key 最多为 4 个,在上述例子中,key1=public,key2=abc,key3 和 key4 将以 0 进行补全。
计算哈希值之后,将扫描对应哈希桶中的双向链表,逐个进行匹配,若未匹配到,将进一步扫描该系统表的数据页。若数据页中也没有该数据,则说明不存在该数据,需要建立一个假的 CatCTup,并标记为 negative,以提升查询 miss 效率,若数据页中包含该数据,则将该条数据进行封装,建立 CatCTup,并插入到对应的哈希桶中。
在匹配到 CatCTup 后,需要将当前 CatCTup 的引用数量 refcount 加一,并进行返回,refcount 的作用是计数引用,以防止被错误释放。
对于释放 SysCache,其主要步骤是将引用数量 refcount 减一,并释放 refcount 为 0 的资源。
图 3 释放 SysCache 流程图
NOTE:在查询 SysCache 和释放 SysCache 的过程中,会将当前匹配的 tuple 与 CurrentResourceOwner 进行绑定,该操作的主要目的是在线程退出或者事务提交时进行资源泄漏的校验。
NOTE:SysCache 支持模糊查询,接口为 SearchCatCacheList,有兴趣的小伙伴可自行百度,本问只以最简单的情况进行介绍。
SysCache 失效机制
在解释 SysCache 失效之前,需要先解释一下 SysCache 为什么需要失效。我们说过,SysCache 为 ThreadLocal 结构,假设线程 1 在 SysCache 中缓存了表 abc 的元数据,而线程 2 删除了表 abc,那么线程 1 中 abc 的元数据需要进行失效处理,GaussDB(DWS)中 SysCache 失效机制主要通过失效消息队列实现。
图 4 SysCache 失效机制
我们以一个简单例子进行说明,上图为语句执行过程中,SysCache 失效消息发送流程图。
在事务开始时,本线程将主动接收并处理失效消息队列中的消息。在开启子事务时,会初始化失效消息存储结构,主要包括两部分结构,CurrnetCmdInvalidMsgs(当前失效消息),主要存储当前执行语句产生的失效消息,PriorCmdInvalidMsgs(历史失效消息),主要用于存储从事务开启到当前时间点,产生的失效消息,其中 CurrnetCmdInvalidMsgs 和 PriorCmdInvalidMsgs 均为 ThreadLocal 结构。
在每个语句执行前,会将前一个语句的失效消息从 CurrnetCmdInvalidMsgs 移动到 PriorCmdInvalidMsgs。之后在语句执行完毕以后,将失效消息加入到 CurrnetCmdInvalidMsgs。
在子事务提交时,分为两种情况,若事务 rollback,则需要失效本地所有的 PriorCmdInvalidMsgs。若事务提交,则需要将子事务的 PriorCmdInvalidMsgs 和 CurrnetCmdInvalidMsgs 加入到父事务的 PriorCmdInvalidMsgs 中,并将当前事务信息切换为父事务环境。
在父事务提交时,同样分为两种情况,若事务 rollback,则需要失效本地所有的 PriorCmdInvalidMsgs。若事务提交,则需要向失效消息队列提交该线程产生的全部失效消息。
NOTE:一句话解释 SysCache 失效机制,内核在事务提交成功时会主动将本线程产生的失效消息提交到失效消息队列,供其他线程同步。
NOTE:此处并未详细解释失效消息队列相关内容,大家可将失效消息队列当作黑盒,失效消息队列对外提供失效消息提交和读取接口,用于失效消息同步,后续文章会详细介绍失效消息队列相关内容。
想了解 GuassDB(DWS)更多信息,欢迎微信搜索“GaussDB DWS”关注微信公众号,和您分享最新最全的 PB 级数仓黑科技,后台还可获取众多学习资料~
版权声明: 本文为 InfoQ 作者【华为云开发者社区】的原创文章。
原文链接:【http://xie.infoq.cn/article/73bbf44ed43b4e6c6a8488a93】。文章转载请联系作者。
评论