DDL_Synchronization

本文涉及的产品
云原生数据库 PolarDB PostgreSQL 版,标准版 2核4GB 50GB
云原生数据库 PolarDB MySQL 版,通用型 2核8GB 50GB
简介: 在共享存储一写多读的架构下,数据文件实际上只有一份。得益于多版本机制,不同节点的读写实际上并不会冲突。但是有一些数据操作不具有多版本机制,其中比较有代表性的就是文件操作。

概述

在共享存储一写多读的架构下,数据文件实际上只有一份。得益于多版本机制,不同节点的读写实际上并不会冲突。但是有一些数据操作不具有多版本机制,其中比较有代表性的就是文件操作。

多版本机制仅限于文件内的元组,但不包括文件本身。对文件进行创建、删除等操作实际上会对全集群立即可见,这会导致RO在读取文件时出现文件消失的情况,因此需要做一些同步操作,来防止此类情况。

对文件进行操作通常使用DDL,因此对于DDL操作,PolarDB提供了一种同步机制,来防止并发的文件操作的出现。除了同步机制外,DDL的其他逻辑和单机执行逻辑并无区别。

术语

  • LSN:Log Sequence Number,日志序列号。是WAL日志的唯一标识。LSN在全局是递增的。
  • 回放位点:Apply LSN,表示只读节点的回放位点。

同步DDL机制

DDL锁

同步DDL机制利用AccessExclusiveLock(后文简称DDL锁)来进行RW/RO的DDL操作同步。

image.png

图1 DDL锁和WAL日志的关系 DDL锁是数据库中最高级的表锁,对其他所有的锁级别都互斥,会伴随着WAL日志同步到RO节点上,并且可以获取到该锁在WAL日志的写入位点。当RO回放超过Lock LSN位点时,就可以认为在RO中已经获取了这把锁。DDL锁会伴随着事务的结束而释放。如图1所示,当回放到ApplyLSN1时,表示未获取到DDL锁;当回放到ApplyLSN2时,表示获取到了该锁;当回放到ApplyLSN3时,已经释放了DDL锁。 ![异步回放ddl锁.png](pic/46_DDL_2.png) 图2 DDL锁的获取条件 当所有RO都回放超过了Lock LSN这个位点时(如图2所示),可以认为RW的事务在集群级别获取到了这把锁。获取到这把锁就意味着RW/RO中没有其他的会话能够访问这张表,此时RW就可以对这张表做各种文件相关的操作。 > 说明:Standby有独立的文件存储,获取锁时不会出现上述情况。

image.png

图3 同步DDL流程图 图3所示流程说明如下:

  1. RO会话执行查询语句。
  2. RW会话执行DDL,在本地获取DDL锁并且写到WAL日志中,等待所有RO回放到该WAL日志。
  3. RO的回放进程尝试获取该锁,获取成功后将回放位点返回给RW。
  4. RW获知所有RO均获取到该锁。
  5. RO开始进行DDL操作。

如何保证数据正确性

DDL锁是PG数据库最高级别的锁,当对一个表进行DROP/ALTER/LOCK/VACUUM(FULL) table等操作时,需要先获取到DDL锁。RW是通过用户的主动操作来获取锁,获取锁成功时会写入到日志中,RO则通过回放日志获取锁。

  • 主备环境:热备存在只读查询,同时进行回放,回放到该锁时,如果该表正在被读取,回放就会被阻塞直到超时。
  • PolarDB环境:RW获取锁需要等待RO全部获取锁成功才算成功,因为需要确保主备都不再访问共享存储的数据才能进行DDL操作。

当以下操作的对象都是某张表,“<”表示时间先后顺序时,同步DDL的执行逻辑如下:

  1. 本地所有查询操作结束 < 本地获取DDL锁 < 本地释放DDL锁 < 本地新增查询操作
  2. RW本地获取DDL锁 < 各个RO获取本地DDL锁 < RW获取全局DDL锁
  3. RW获取全局DDL锁 < RW进行写数据操作 < RW释放全局DDL锁

结合以上执行逻辑可以得到以下操作的先后顺序:各个RW/RO查询操作结束 < RW获取全局DDL锁 < RW写数据 < RW释放全局DDL锁 < RW/RO新增查询操作

可以看到在写共享存储的数据时,RW/RO上都不会存在查询,因此不会造成正确性问题。在整个操作的过程中,都是遵循2PL协议的,因此对于多个表,也可以保证正确性。

RO锁回放优化

上述机制中存在一个问题,就是锁同步发生在主备同步的主路径中,当RO的锁同步被阻塞时,会造成RO的数据同步阻塞(如图1所示,回放进程的3、4阶段在等待本地查询会话结束后才能获取锁)。PolarDB默认设置的同步超时时间为30s,如果RW压力过大,有可能造成较大的数据延迟。 RO中回放的DDL锁还会出现叠加效果,例如RW在1s内写下了10个DDL锁日志,在RO却需要300s才能回放完毕。数据延迟对于PolarDB是十分危险的,它会造成RW无法及时刷脏、及时做检查点,如果此时发生崩溃,恢复系统会需要更长的时间,这会导致极大的稳定性风险。

异步DDL锁回放

针对此问题,PolarDB进行了RO锁回放优化。

image.png

图4 RO异步DDL锁回放 优化思路:设计一个异步进程来回放这些锁,从而不阻塞主回放进程的工作。 整体流程如图4所示,和图3不同的是,回放进程会将锁获取的操作卸载到锁回放进程中进行,并且立刻回到主回放流程中,从而不受锁回放阻塞的影响。 锁回放冲突并不是一个常见的情况,因此主回放进程并非将所有的锁都卸载到锁回放进程中进行,它会尝试获取锁,如果获取成功了,就不需要卸载到锁回放进程中进行,这样可以有效减少进程间的同步开销。 该功能在PolarDB中默认启用,能够有效的减少回放冲突造成的回放延迟,以及衍生出来的稳定性问题。在AWS Aurora中不具备该特性,当发生冲突时会严重增加延迟。

如何保证数据正确性

在异步回放的模式下,仅仅是获取锁的操作者变了,但是执行逻辑并未发生变化,依旧能够保证RW获取到全局DDL锁、写数据、释放全局DDL锁这期间不会存在任何查询,因此不会存在正确性问题。


相关文章
|
关系型数据库 MySQL 分布式数据库
PolarDB MySQL多场景评测
本次评测将围绕指定场景中的灵活弹性和无感秒切展开,对于自选场景中的安全和DB+AI也进行了简单体验。
852 3
PolarDB MySQL多场景评测
|
存储 缓存 监控
多级缓存有哪些级别?
【10月更文挑战第24天】多级缓存有哪些级别?
275 1
|
JSON JavaScript 前端开发
JS逆向 AST 抽象语法树解析与实践
JS逆向 AST 抽象语法树解析与实践
564 2
|
开发工具 git
【Mac 系统】解决git clone速度慢
本文提供了解决Mac系统上使用git clone时速度慢的问题的方法。
665 0
|
SQL 监控 关系型数据库
MySQL-长事务详解
MySQL-长事务详解
402 0
|
消息中间件 Serverless 网络性能优化
消息队列 MQ产品使用合集之客户端和服务器之间的保活心跳检测间隔是怎么设置的
消息队列(MQ)是一种用于异步通信和解耦的应用程序间消息传递的服务,广泛应用于分布式系统中。针对不同的MQ产品,如阿里云的RocketMQ、RabbitMQ等,它们在实现上述场景时可能会有不同的特性和优势,比如RocketMQ强调高吞吐量、低延迟和高可用性,适合大规模分布式系统;而RabbitMQ则以其灵活的路由规则和丰富的协议支持受到青睐。下面是一些常见的消息队列MQ产品的使用场景合集,这些场景涵盖了多种行业和业务需求。
|
SQL 缓存 关系型数据库
数据库连接池到底应该设多大?
数据库连接池到底应该设多大?
621 0
|
存储 消息中间件 监控
搭建分布式应用准备工作(从零到一)
搭建分布式应用准备工作(从零到一)
|
SQL 安全 数据库
如何防止删库跑路?使用NineData进行SQL变更审核!
在日常开发任务中,对核心库进行变更可能带来一系列风险,如删库跑路和数据泄露等。因此,在变更SQL之前,需要进行审核以确保变更合理、安全、规范。
952 1
|
Java 数据库连接 API
SpringBoot【问题 01】借助@PostConstruct解决使用@Component注解的类用@Resource注入Mapper接口为null的问题(原因解析+解决方法)
SpringBoot【问题 01】借助@PostConstruct解决使用@Component注解的类用@Resource注入Mapper接口为null的问题(原因解析+解决方法)
1377 0