写点什么

GaussDB(DWS) 中共享消息队列实现的三大功能

  • 2021 年 12 月 20 日
  • 本文字数:1975 字

    阅读完需:约 6 分钟

摘要:本文将详细介绍 GaussDB(DWS)中共享消息队列的实现。

 

本文分享自华为云社区《GaussDB(DWS)CBB组件之共享消息队列介绍》,作者:疯狂朔朔。

1)共享消息队列是什么?


在前文中,我们讲解了 SysCache 的实现原理,GaussDB(DWS)通过 SysCache 缓存表元数据,以加速查询,然而在并发查询过程中,不可避免地会出现需要同步元数据的情况,举个简单例子,假设存在以下语句执行流程:

a.     Create table abc(会话 1)

b.    Select * from abc(会话 1)

c.     Drop table abc(会话 2)

d.    Select * from abc(会话 1)


在会话 1 中,会连续两次执行 Select 表操作(b 和 d),在 b 语句执行后,会话 1 将对 abc 的元数据进行缓存,缓存到 SysCache 中,以备后续使用。然而,在 c 语句执行后,需要对会话 1 中的元数据进行失效,否则,会话 1 将在执行 d 语句过程中发生错误,读取已删除的数据。


那么,会话 2 如何“通知”会话 1 失效哪些数据呢?答案是共享消息队列。

2)共享消息队列存储结构


如图所示,为共享消息队列数据结构



​图示中主要包括两部分,下面部分为 ThreadLocal 结构,主要记录的是每个线程内部的数据,线程间数据是独立的,无法互相访问,上面部分为共享消息队列中的数据,共享消息队列存在于共享内存中,可同时被多个线程访问,共享消息队列的访问场景是典型的一写多读场景。


在共享消息队列中,核心变量有三个,nextMsgNum、minMsgNum、MaxMsgNum,其中 nextMsgNum 记录了每个线程消息读取到的位置,minMsgNum 记录了共享消息队列中最早消息的位置,maxMsgNum 记录了共享消息队列中最新消息的位置,对于每个线程而言,需要定期(在表加锁/事务开始/收到信号时触发)从共享消息队列中读取失效消息,利用失效消息(共享消息队列中的每个消息)更新线程内部数据,同时,若线程内部产生失效消息(通常 DDL 语句在事务提交时产生大量失效消息),则需要向共享消息队列中插入失效数据,供其他线程读取。另外,还有两个参数,hasMessages、resetState,其中 hasMessages 用于标记对应线程是否存在未读取的失效消息,resetState 用于标记对应线程是否需要失效全部消息。


NOTE:失效数据有哪些?失效消息一共有六类(源自 PG),有兴趣的同学可以研读 PG 源码,在此处我们不再展开,仅需知道线程需要从共享消息队列中读取/插入消息,以实现数据同步。

3)共享消息队列接口实现


共享消息队列本质上对于外部接口只需要提供三个功能:读取共享消息队列中消息、向共享消息队列中写入消息、清理共享消息队列。

3.1)读取共享消息队列



​如图所示,为失效消息读取过程。在线程同步失效消息过程中,有几个关键点:

  • 若共享内存中线程对应的 hasMessage 为 True,则表示有失效消息需要读取,否则直接返回,无新的失效消息。

  • 读取失效消息过程中,需要持有读共享锁,以保证读取的消息不会被清理掉。

  • 若读取失效消息过程中,发现 resetState 被置为 True,说明该线程已经无法使用共享消息队列中的消息进行追增,需要对缓存进行全失效。缓存全失效相当于追增全部数据,需要将 nextMsgNum 置为 maxMsgNum。缓存全失效将严重降低 SQL 执行性能,尽量减少缓存全失效的发生频率。

  • 在追增数据过程中,会推进线程自身的 nextMsgNum,以标记数据追增位置。


NOTE:在读取共享消息队列过程中,每个线程推进自己的 nextMsgNum 位置,以方便记录数据追增情况。

3.2)向共享消息队列中写入消息



​如图所示,为失效消息写入过程。有以下几个关键点:

  • 写入失效消息过程,需要调用清理共享消息队列接口,以保证有足够多的空位写入失效消息。

  • 在写入失效消息过程中,会更新 maxMsgNum。

  • 写入失效消息完毕以后,需要将其他线程的 hasMessage 标记为 True。

  • 写入失效消息过程,需要持排他写锁,阻塞其他线程写操作,但不阻塞读。


NOTE:写入失效消息过程实际上是推进 maxMsgNum 的过程,同时告知其他线程有新的失效消息需要读取。

3.3)清理共享消息队列



​如图所示,清理共享消息队列过程中,有以下几个关键点:

  • 清理共享消息队列需要持有排他读锁和排他写锁,阻塞读过程。其主要原因是,清理共享消息队列会推进 minMsgNum,若不持读锁,可能导致 nextMsgNum 读取过期数据。

  • 清理共享消息队列会推进 minMsgNum。

  • 清理共享消息队列过程,会将所有没有及时追增失效消息的线程执行全失效。

  • 在清理共享消息队列最后步骤,会对距离 minMsgNum 最近的线程,发送追增信号,以确保不会频繁发生全失效。该步骤主要考虑的情况是,若线程长时间处于 idle 状态,需要外部信号触发其及时追增消息。


NOTE:清理共享消息队列过程,实际上是推进 minMsgNum 的过程,同时对所有没有及时追增失效消息的线程执行全失效。


根据以上共享消息队列接口可知,读取共享消息队列主要负责推进各个线程自身的 nextMsgNum;写入失效消息主要负责推进 maxMsgNum;清理共享消息队列主要负责推进 minMsgNum。通过共享消息队列,可有效实现各个线程之间的数据同步。


点击关注,第一时间了解华为云新鲜技术~

发布于: 47 分钟前阅读数: 4
用户头像

提供全面深入的云计算技术干货 2020.07.14 加入

华为云开发者社区,提供全面深入的云计算前景分析、丰富的技术干货、程序样例,分享华为云前沿资讯动态,方便开发者快速成长与发展,欢迎提问、互动,多方位了解云计算! 传送门:https://bbs.huaweicloud.com/

评论

发布
暂无评论
GaussDB(DWS)中共享消息队列实现的三大功能