7) SELECT/UPDATE/DELETE(计划阶段)
图4-50 SELECT/UPDATE/DELETE(计划阶段)时序图
用户可以在内存表中SELECT/UPDATE/DELETE行。每个操作分为两个阶段:计划阶段和执行阶段。图4-50主要关注计划阶段。 每个SELECT/UPDATE/DELETE的规划阶段包括选择最佳执行计划。为此,openGauss准备了几个可能的执行路径,并要求FDW估计每个此类路径的开销,以便openGauss可以选择最佳的执行路径。 正常事件流:openGauss调用GetForeignRelSize接口,FDW从MOT中检索相关表对象,并用启动成本和总成本估计初始化此查询的FDW状态。openGauss调用GetForeignPaths触发所有涉及索引对象的开销计算。最后,openGauss调用GetForeignPlan触发结束整个过程,包括查询子句对本地和远程排序,以及根据所选执行路径对FDW状态进行序列化。PREPARE语句的执行在此结束,其他语句待执行的部分将在执行阶段中描述。 P1:在此异常事件流中,由于资源限制(内存不足),无法执行操作。 #### 8) SELECT/UPDATE/DELETE(执行阶段)
图4-51 SELECT/UPDATE/DELETE(执行阶段)
如图4-51所示,计划阶段完成后,执行阶段开始。
正常事件流:openGauss调用BeginForeignScan,FDW检索相关表并初始化查询的FDW状态。在进行UPDATE/DELETE操作时,openGauss通过调用BeginForeignModify接口触发一个额外的初始化阶段,然后返回NULL。openGauss通过调用IterateForeignScan接口进行如下操作:①仅在需要时一次性初始化游标;②在当前事务对象中查找下一行;③将行数据从MOT格式转换为openGauss格式;④游标前进;⑤返回包含unpack行的槽位。重复此过程,直到游标中不再有行,并且返回NULL到openGauss。然后openGauss应用本地条件/查询子句等本地过滤器来决定是否继续处理该行。在进行SELECT操作时,该行将被添加到结果集中,并返回结果集给用户。进行UPDATE和DELETE操作需执行的其余部分将在后文中进行描述。
9)UPDATE(结束执行阶段)
图4-52 UPDATE(结束执行阶段)时序图
执行SELECT、UPDATE和DELETE语句的公共部分后,每个语句的剩余部分有所不同。图4-52描述UPDATE的剩余部分。
正常事件流:openGauss为特定的更新元组调用ExecForeignUpdate接口。FDW更新当前事务对象中最后一行的状态以进行并发控制,然后FDW将行数据从openGauss格式转换为MOT格式,并通过覆盖该行的方式完成变更字段的更新。该操作在重做日志中持久化,并返回openGauss。
P1:在此异常事件流中,由于并发控制事务对象的行状态更新失败。在这种情况下,父事务将以适当的错误代码中止。
10) DELETE行(结束执行阶段)
图4-53 DELETE行(结束执行阶段)
图4-53描述了DELETE操作执行的剩余部分。
正常事件流:openGauss为特定更新的元组调用ExecForeignDelete接口。FDW更新当前事务对象中最后一行的状态以进行并发控制,然后将操作持久化到重做日志中,并返回openGauss。
P1:在此异常事件流中,由于并发控制事务对象的行状态更新失败。在这种情况下,父事务将以适当的错误代码中止。
4.3.3 内存表的存储
Table类包含管理数据库中内存表所需的所有项。表由以下组件组成:列、主索引和可选的二级索引。关键成员变量说明如表4-36 Table类的关键成员变量所示。
表4-36 Table类的关键成员变量
成员变量 | 描述 |
tableCounter : std::atomic<uint32_t> | 原子表ID |
m_tupleSize : uint32_t | 原始元组大小(以字节为单位) |
m_tableExId : uint64_t | openGauss提供的外部表ID |
m_secondaryIndexes : SecondaryIndexMap | 按名称访问的二级索引映射 |
m_rwLock : pthread_rwlock_t | RW Lock,防止在检查点/真空期间删除 |
m_rowPool : ObjAllocInterface* | row_pool行分配器对象池 |
m_primaryIndex : MOT::Index* | 主索引 |
m_numIndexes : uint16_t | 正在使用的耳机索引数 |
m_indexes : MOT::Index** | 索引数组 |
m_fixedLengthRows : bool | 指定行是否具有固定长度 |
m_fieldCnt : uint32_t | 表schema中的字段个数 |
m_columns : Column** | 列数组 |
Row类包含管理表中的内存行所需的所有项,关键成员变量如表4-37所示。
表4-37 Row类的关键成员变量
成员变量 | 描述 |
m_data : uint8_t | 保存行数据的原始缓冲区,开始于类的结束位置 |
m_keyType : KeyType | 使用的键类型。* Internal –用于内部测试(64位) |
- Surrogate –用于无索引表
- External –需要从行生成
|
| m_pSentinel : Sentinel* | 指向主哨兵的指针 |
| m_rowHeader : RowHeader | OCC行的头部,包含OCC操作的所有相关信息 |
| m_rowId : uint64_t | 创建行时生成的唯一rowId |
| m_table : Table* | 指向内存管理表的指针 |
4.4.4 索引
MOT使用索引来高效地访问数据。MOT索引支持范围查询等所有基本操作。由于数据存储在Row类中,每个MOT索引都按顺序使用哨兵来访问数据。
IndexFactory类提供了创建新索引对象的能力。
作为Table类的一部分,Index抽象类提供了创建和访问数据索引的能力。索引是否满足唯一性决定了该索引是否允许插入重复键。如图4-54所示,描述了一个有三行和两个索引的MOT表T的结构,其中一个索引是非唯一索引,另一个索引是唯一索引。对于非唯一索引而言,MOT内部通过在插入时用唯一标识符填充每个键的方式将键视为唯一。在图4-54中,MOT将哨兵插入到带有键的唯一索引和带有键+后缀的非唯一索引中。使用哨兵方便了维护操作,因为在进行维护操作时,可以在不接触索引数据结构的情况下替换行。
图4-54 唯一、非唯一索引和哨兵
Sentinel类包含指向唯一索引情况下的行数据或非唯一索引情况下主哨兵的指针,还包含一些标志位和引用计数等支持跨事务并发的信息。每次向索引插入新键时都会创建哨兵。例如,对于具有3个索引的表,插入新键时将创建3个哨兵,每个索引对应一个哨兵。哨兵和行之间的关系如图0-所示。
图4-55 哨兵与行关系
MasstreePrimaryIndex类实现了索引接口。它基于Masstree K/V存储实现,同时封装了MOT内存分配池,根据对象分配任意大小内存。
IndexIterator抽象类提供了创建迭代器并根据提供的迭代器访问数据的能力。
4.3.5 事务
事务部分覆盖了从openGauss映射到MOT的所有支持的DDL/DML操作。
事务与并发控制机制紧密耦合,每个操作都必须通过并发控制管理,并完成相应的行为。
MOT基于乐观并发机制,几乎不使用锁,因此每个客户端都有自己的事务视图,并且不会阻塞DML,与磁盘表对每个非SELECT操作都加锁的使用方式有显著区别。
每个局部行都有一个初始状态,状态由txn_state_machine管理。txn_state_machine扩展了Silo,支持新操作写后读和读后写,类似于MESI缓存一致性协议。如图4-56所示,MOT将新操作(RD/WR)视为本地缓存中的缓存不命中,并将状态从无效提升为新状态。
图4-56 DML事务状态机
图注:状态说明: INV:错误状态(INVALID STATE);RD:查询状态(SELECT STATE);WR:更新状态(UPDATE STATE);DEL:删除状态(DELETE STATE);INS:插入状态(INSERT STATE)。 操作说明: INV:无效操作(INVALID);RD:读操作(READ);WR:写操作(WRITE);DEL:删除操作(DEL);INS:插入操作(INSERT)。
详细流程
SELECT具体流程如图4-57所示。
图4-57 SELECT时序图
(1) 当SELECT操作被发送到FDW,FDW就会打开一个游标并将正确的哨兵发送到事务管理器。
(2) 事务管理器检查哨兵,如果哨兵有效则在缓存中搜索,否则返回未找到该行。
(3) TxnAccess在内部查找哨兵,如果在高速缓存中找到该行则返回该行,并认为是高速缓存命中。
(4) TxnManager评估隔离级别和来自缓存的结果:如果TxnAccess返回了一行,直接将其返回给openGauss;否则以下下两种情况。
① 隔离级别为READ_COMMITED时生成行的副本并返回给FDW。
② 隔离级别为REPEATABLE_READ时映射缓存中的行,并将缓存的行返回给FDW。
UPDATE具体流程如图4 58所示。
图4-58 UPDATE时序图
(1) 当UPDATE操作被发送到FDW,FDW就会打开一个游标,并将正确的哨兵发送到事务管理器。
(2) 事务管理器检查哨兵,如果哨兵有效就在缓存中搜索,否则返回未找到该行。
(3) TxnAccess在内部查找哨兵,如果在高速缓存中找到该行则返回该行,并认为是高速缓存命中。
(4) TxnManager评估来自缓存的结果。
① 如果TxnAccess返回了一行,直接将其返回openGauss。
② 如果没有找到该行,则映射哨兵并返回缓存的行。
(5) openGauss计算返回的行,如果该行与筛选器匹配则openGauss向FDW发送带有更新数据的更新操作。
(6) TxnManager将行的状态提升为WR,并用从openGauss接收的新数据更新本地行。
DELETE具体流程如图4-59所示。
图4-59 DELETE时序图
(1) 当DELETE操作被发送到FDW,FDW就会打开一个游标并将正确的哨兵发送到事务管理器。
(2) 事务管理器检查哨兵,如果哨兵有效就在缓存中搜索,否则返回未找到该行。
(3) TxnAccess在内部查找哨兵,如果在高速缓存中找到该行则返回该行,并认为是高速缓存命中。
(4) TxnManager评估来自缓存的结果。
① 如果TxnAccess返回了一行,直接将其返回openGauss。
② 如果没有找到该行,则映射哨兵并返回缓存的行。
(5) openGauss计算返回的行,如果该行与筛选器匹配,则openGauss向FDW发送带有更新数据的删除操作。
(6) TxnManager将行的状态提升为DEL,并将本地行标记为已删除。
INSERT具体流程如图4-60所示。
图4-60 INSERT序列图
(1) 操作发送到FDW后,FDW使用表API准备插入的行,并将该行发送到事务管理器。
(2) 事务管理器执行以下算法。
对于表中的每个索引执行以下操作。
① 将哨兵插入索引。
② 如果已提交行–中止事务。
③ 如果成功插入行–映射并完成插入。
④ 如果行不存在,如下。
- 如果已映射-自己插入,则中止。
- 否则将它映射到本地缓存。
(3) TxnManager对于重复的key返回RC_OK或RC_ABORT。TxnDDLAccess用于缓存和访问事务性DDL更改。事务中执行的所有DDL都存储在TxnDDLAccess中,并在事务提交/回滚时应用回滚。假设openGauss负责DDL并发,并确保并发的DDL更改不会并行执行。TxnAccess类用于缓存和访问事务性DML更改的。在事务中执行的所有DML都存储在TxnAccess中,并在事务提交/回滚中应用回滚。Access类用于保存单行访问的数据。AccessParams用于保存当前访问的参数,为CC管理提供额外的信息。InsItem用于保存行插入请求的数据。
4.3.6 并发控制
MOT采用源自SILO的单版本并发控制(concurrency control,CC)算法,是一种OCC算法。并发控制模块满足内存引擎的所有事务性需求,其主要设计目标是为MOT内存引擎提供各种隔离级别的支持。当前支持如下隔离级别。
(1) 读已提交(READ-COMMITED)。
(2) 可重复读(REPEATABLE-READ)。
图4-61 MOT本地内存和全局内存
图4-61显示了MOT运行事务时的关键技术,包括如下内容。
(1) 私有事务内存用于无锁读写,仅在最终提交时使用锁,低争用。
(2) 低时延,NUMA感知的本地内存。
(3) 乐观并发控制:数据锁最小化,低争用。
(4) 无锁自动清理(Auto-Vacuum),无开销。
(5) 极致优化的Masstree实现。
1. SILO并发控制背景&算法
Silo来自Stephen Tu等人在计算机顶级会议SOSP13上发表的《Speedy Transactions in Multicore In-Memory Databases》,在现代众核服务器上实现了卓越的性能和可扩展性。Silo的设计完全是为了高效地使用系统内存和高速缓存。例如,它避免了所有集中的争用点,包括集中事务ID分配。Silo的关键贡献是一种基于乐观并发控制的提交协议,它支持序列化,同时避免对仅读取的记录进行共享内存写入。Silo可提供与其他可序列化数据库一样的保证,而不会出现不必要的可扩展性瓶颈或额外的延迟。
设计
MOT的设计原则是通过减少对共享内存的写入来消除不必要的争用。Silo按照固定时间间隔的epoch进行时间分段,因此Silo这种OCC的变体可以支持序列化,即在epoch边界形成自然序列化点。在恢复之后也能通过CSN或周期性更新的epoch实现序列化。Epoch还有助于提高垃圾回收效率并使能快照事务。其他一些设计,如事务ID的设计、记录覆盖和支持范围查询等,进一步加快了事务执行,同时非中心化的持久化子系统也避免了争用。
2. 事务ID
SILO的并发控制以事务ID(tansaction ID,TID)为中心,它标识事务并记录版本,也用作锁和检测数据冲突。每个记录都包含最近修改它的事务的TID。TID为64位整数。每个TID的高位包含一个CSN,CSN等于对应事务提交时间的全局序列号;低三位分别为:Bit 63:锁定标志位,Bit 62:最新版本标志位,Bit 61:不存在状态标志位。由于CSN有效长度为61bit,因此MOT忽略了事务ID回卷。另外,与许多系统不同,Silo以分散而非集中的方式分配TID。
3. 数据布局
Silo中的一条记录包含以下信息。
(1) 一个64位的TID(MOT使用CSN)。
(2) 记录数据。提交的事务通常就地修改记录数据,主要通过减少记录对象的内存分配开销来提升短写的性能。然而,读者必须使用版本验证协议以确保已读取每个记录数据的一致性版本。
4. 乐观并发控制的数据库操作
1) 读/写流程
(1)事务流程
①在索引中搜索行引用。
② 将数据免锁复制到基于类型的本地集,包括读写集(Read/Write Set, R/W set)。
③ 基于本地副本进行处理。
(2)校验流程
① 按主键顺序对写集(Write Set)进行排序。
② 锁定写集中的所有行。
③ 验证读写集的行。
④ 验证本地行CSN是否更改。
⑤ 验证该行是否为该键的最新版本(由于存在本地数据,可能并非最新)。
⑥ 验证该行未被其他事务锁定。
⑦ 如果以上任一项验证失败,则中止事务。
⑧ 否则将更新CSN后的所有写集中的行复制回去,然后释放这些行上的锁。
2) 插入流程
(1)事务流程
① 构造一个CSN=0且状态为不存在的新行r。
添加r到写集并视为常规更新。
生成唯一的键k。
② 在状态为不存在的情况下,向树/索引添加从k → r的映射。
如果k已经映射到一个状态为存在的记录,则插入失败。
否则在读阶段增大版本号。
(2)校验流程
① 锁定写集。
② 验证插入集(insert set)。
③ 若事务中止,则垃圾回收器记录状态为不存在的行。
3) 删除流程
(1)事务流程
① 在索引中搜索行引用。
② 将行映射到本地缓存。
③ 将本地副本标记为已删除。
(2)校验流程
① 验证行保持不变;已删除的行将被视为更新。
② 从索引中删除行,即将已删除的哨兵/行放入垃圾回收器中。
图4-62 MOT提交协议伪代码
MOT提交协议伪代码如图4 62所示。
5. 关键类和数据结构
并发控制的关键类和数据结构如表4-38所示。
表4-38 并发控制的关键类和数据结构简介
关键类和数据结构 | 描述 |
OccTransactionManager类 | 管理整个事务验证机制,与事务类紧耦合 |
RowHeader类 | 每一行的OCC元数据。头部为64位,包含状态位和CSN |
4.3.7 重做日志
MOT重做日志(Redo Log)使用预写式日志(write-ahead logging,WAL)技术来确保数据完整性。WAL的核心概念是,内存中的数据和索引的更改只有在记录下这些更改之后才会发生。因此写入重做日志是MOT提交协议的一部分。
如图4-63所示,MOT存储引擎的重做日志模块同样使用openGauss磁盘引擎的日志接口进行持久化和恢复。这意味着MOT重做数据被写入相同的XLOG文件,并使用相同的XLOG逻辑。使用与openGauss磁盘引擎相同的日志记录接口可确保跨引擎事务的一致性,并减少复制、日志恢复等模块的冗余实现。
图4-63 使用相同的XLOG (WAL)基础架构的openGauss磁盘库和MOT
1. 事务日志记录
与openGauss其他存储引擎不同,MOT内存引擎仅在事务实际提交时才会写入重做日志。因此,在事务期间或事务中止时,数据不会写入重做日志。这样可以减少写入的数据量,从而减少不必要的磁盘IO调用,因为这种磁盘IO调用很慢。例如,如果在事务期间多次更新同一行,则只将表示已提交行的最终状态写入日志。
由于设计MOT内存引擎时考虑了对接不同的数据库的可能性,因此如图4-64所示,MOT通过抽象的ILogger接口对接重做日志。
图4-64 ILogger接口
2. 日志类型
设计MOT内存引擎时同样考虑了支持不同的日志记录方式。如图4-65所示,MOT当前已实现同步日志(synchronous redo Log)和同步组日志(group synchronous redo log)。这是通过RedoLogHandler类实现的。RedoLogHandler封装了日志逻辑,在数据库启动时初始化。RedoLogHandler可以根据需要扩展实现新的日志记录方式。
图4-65 RedoLogHandler接口
每个事务管理器对象(TxnManager)都包含一个Redolog类,该类负责在提交时将事务数据序列化到缓冲区中。如图4-66所示,该缓冲区被传输到RedologHandler以进行日志记录。
图4-66 使用RedoLogHandler的事务日志记录
1) 同步日志记录
同步日志使用SynchronousRedoLogHandler。如图4-67所示,这是一个简单的RedoLogHandler实现,它只将序列化缓冲区委托给ILogger(XLOGLogger),以便将其写入XLOG。因为在写缓冲区时,事务被阻塞,所以称为同步。只有当所有事务数据被序列化并写入日志时,提交协议才会继续。
图4-67 SynchronousRedoLogHandler
2) 同步组提交日志记录
同步组提交日志由SegmentedGroupSyncRedoLogHandler类实现。它通过将几个事务分组到一个写块(write block)中并一起写入的方式优化日志记录。这种方法在一次调用中收集更多数据,可以最大限度地减少磁盘IO次数。除此之外, SegmentedGroupSyncRedoLogHandler将每个NUMA处理器(socket)的事务分组,以减少跨NUMA处理器的数据传输,因为跨NUMA处理器的数据访问比同一NUMA处理器本地内存访问慢。
当事务提交时,它将数据序列化到缓冲区中,这个缓冲区被传输到SegmentedGroupSyncRedoLogHandler,并放入一个提交组中。提交组(Commit Group)是一组序列化事务缓冲区的集合,这些事务缓冲区将被提交并写入磁盘。根据不同的配置参数,当一个组被填满或超过预先配置的时间时,MOT将关闭该组,并将该组内所有缓冲区一起写入日志。
图4-68描述了将多个事务分组一起写入的组提交逻辑。
图4-68 同步组提交对每个NUMA处理器的事务进行分组
3) 异步日志
MOT暂未开发专用的异步日志机制,异步日志是通过在conf配置文件中将synchronization_commit参数设置为“off”来实现的。
3. 关键类和数据结构
重做日志的关键类和数据结构如表4-39所示。
表4-39 重做日志的关键类和数据结构简介
关键类和数据结构 | 描述 |
---|---|
RedoLog类 | 负责事务数据序列化的主要类,它是TxnManager类的成员对象。RedoLog的commit方法由TxnManager在事务通过验证阶段并且所有更新的行均已锁定后在提交协议中调用。在将变更应用到存储之前,RedoLog将在加锁后序列化事务数据,持久化到日志,释放锁。RedoLog使用RedoLogHandler将数据写入日志。RedoLogBuffer类是序列化缓冲区的一个简单实现,RedoLog类通过RedoLogBuffer序列化事务操作。RedoLogBuffer的前4个字节预留给缓冲区大小,在序列化时写入。OperationCode枚举是支持的事务操作列表。RecoveryManager通过根据OperationCode确定如何解析数据并应用事务操作 |
RedoLogWriter | 用于将操作序列化到缓冲区中。RedoLogWriter是一个简单的helper类,获取数据和缓冲区,并将数据序列化到缓冲区中 |
EndSegmentBlock | 控制块,写在每个flushed缓冲区的末尾。包括事务、CSN、是否提交等信息 |
EndSegmentBlockSerializer | 一个简单的helper类,用于序列化和反序列化EndSegmentBlock |
Ilogger接口 | 写日志接口的抽象。MOT通过ILogger可以写入不同类型的日志 |
LoggerFactory | 一个工厂类,用于创建不同类型的ILogger。LoggerFactory通过MOT配置项确定要创建哪种logger |
XLOGLogger | 基于openGauss XLOG (WAL)的ILogger的一个实现,它简单地使用openGauss WAL接口将序列化的事务写入WAL中。MOT WAL日志项有自己的资源管理器,这可以使openGauss识别到该日志项是一个MOT WAL日志项,并将其转发到MOT处理。AddToLog为XLOGLogger子接口,XLOGLogger使用openGauss日志基础能力。因此,AddToLog是一个对openGauss XLOG接口的简单委托 |
图4-69所示代码为XLOGLogger::AddToLog接口的实际实现。
图4-69 XLOGger对openGauss XLOG的委托
RedoLogHandler是重做日志逻辑的抽象。RedoLogHandler的派生类可实现不同的日志方法。RedoLogHandler是一个单例模式,由MOT管理,为RedoLog所用。
RedoLogHandlerFactory用于创建RedoLogHandler。MOT根据配置项中配置的RedoLogHandlerType创建RedoLogHandler。
SynchronousRedoLogHandler简单地将RedoLogBuffers委托给ILogger,以便写入重做日志。请参阅前述的同步日志记录小节。
GroupSyncRedoLogHandler是最先进的无锁组提交RedoLogHandler。GroupSyncRedoLogHandler将几个事务的redo log缓冲区分组到一个组,并把他们写在一起,以便优化和最小化磁盘IO。请参阅前述同步组提交小节。CommitGroup表示将一组RedoLogBuffer一起记录。一个提交组有一个主线程,由该主线程创建该提交组,它负责将组内的所有RedoLogBuffer写入日志。主线程写日志时,所有其他线程都在等待。主线程完成写入后将发送信号来唤醒组内其他所有线程,一旦唤醒,事务就可以继续。SegmentedGroupSyncRedoLogHandler是配置了GroupCommit日志方法时的RedoLogHandler。它是RedoLogHandler的一个实现,每个socket都有GroupSyncRedoLogHandler。SegmentedGroupSyncRedoLogHandler的优点在于可以通过维护多个组提交处理程序实现更高的并发。SegmentedGroupSyncRedoLogHandler维护一个GroupSyncRedoLogHandler数组,并将线程绑定到Socket以将线程委托给正确的处理程序。
4.3.8 检查点
与openGauss磁盘存储引擎不同,MOT存储引擎不基于页面存储数据,因此MOT的检查点机制与磁盘引擎的检查点机制完全不同。MOT检查点机制基于CALC(checkpointing asynchronously using logical consistency,使用逻辑一致性异步检查点)算法,该算法出自耶鲁大学Kun Ren等人在数据库顶级会议SIGMOD 2016发表的《Low-Overhead Asynchronous Checkpointing in Main-Memory Database Systems》。
1. CALC算法
CALC算法的优点如下。
(1) 内存占用少:任意时刻每行最多2个副本。只有当检查点为活动状态时,更具体地说,仅在检查点的一个特定阶段,才会创建第二个副本,从而减少了内存占用。
(2) 开销小:CALC比其他异步检查点算法开销小。
(3) 使用虚拟一致性点:CALC不需要停止数据库就能实现物理一致性。虚拟一致性点是数据库的视图,它反映了在指定时间之前提交的所有修改,而不包含指定时间之后提交的修改,而且在不停止数据库系统的情况下就可以获得。实际上,可以通过部分多版本创建虚拟一致性点。
如图4-70所示,精确部分多版本算法(precise partial multi-versioning)的总体思想如下。
(1) 每行都与两个版本相关联,一个是活动版本,一个是稳定版本。通常,稳定版本为空,表明稳定版本与活动版本一致,检查点线程可以安全地记录实时版本。稳定版本仅在检查点的一个特定阶段创建,此时检查点线程将记录该稳定版本。
(2) 每行维护一个稳定状态位,指示稳定行的状态。
图4-70 检查点概述
MOT检查点算法在五个状态之间循环,如图4 71所示。
图4-71 检查点状态机
通常,在进入下一阶段之前,系统要等待所有上一阶段开始提交的事务完成。
1) REST阶段:初始阶段,不进行checkpoint。
(1) 在REST阶段,每行只存储一个活动版本。所有稳定版本都为空,稳定状态位始终为不可用(not available)。
(2) 在此阶段开始提交的任何事务将直接对行的活动版本进行操作,并且不会创建稳定版本。
2) PREPARE阶段:这是虚拟一致性点之前的阶段。当openGauss要求MOT创建快照时,系统从REST阶段移动到PREPARE阶段。
(1) 与REST阶段类似,每行只存储一个活动版本。所有稳定版本都为空,稳定状态位始终为不可用。
(2) 在此阶段开始提交的任何事务将直接对行的活动版本进行操作,不会创建稳定版本。
3) RESOLVE阶段:该阶段标识出虚拟一致性点。在此时间点之前提交的所有事务都将包含在此检查点中,而随后提交的事务将不包含在检查点中。一旦在REST阶段开始提交的事务完成,系统将自动从REST阶段变为RESOLVE阶段。
(1) 在此阶段不允许任何事务启动提交,以避免这些事务在openGauss占用检查点的重做点前写入重做日志。
(2) 一旦在REST阶段开始提交的事务完成,MOT将在此阶段获取要包含在此检查点中的任务列表。
4)CAPTURE阶段:在此阶段中,后台工作进程将数据刷入磁盘。RESOLVE阶段一直持续,直到在准备阶段已开始的所有事务完成并释放其所有锁为止。系统准备任务列表,然后进入CAPTURE阶段。
(1) 在CAPTURE阶段开始的事务已经在一致性点之后开始,因此他们肯定会在一致性点之后完成。因此,除非记录已经具有显式稳定版本,否则总是在更新前将活动版本复制为对应的稳定版本。
(2) 收到BEGIN_CHECKPOINT事件后,系统生成检查点工作进程,扫描所有记录,并将没有显式稳定版本的行,或活动版本的对应的稳定版本刷盘。在此过程中,显式稳定版本一旦刷盘就会被释放。
5) COMPLETE阶段:这是紧跟捕获阶段完成的阶段。检查点捕获完成后,系统进入COMPLETE阶段。事务写入行为恢复为与REST阶段相同的状态。
与REST阶段类似,每行只存储一个活动版本。所有稳定版本都为空,稳定状态位始终为不可用(not available)。
一旦在捕获阶段开始的所有事务都完成,系统将转换回REST阶段,并等待下一个触发检查点的信号。但是,在返回到REST阶段之前,调用函数SwapAvailableAndNotAvailable翻转稳定状态位。这允许MOT避免只能通过完全扫描来重置稳定状态位,因为在CAPTURE阶段之后,所有稳定状态位都可用,但在Rest阶段开始时,希望所有稳定状态位都不可用。
2. 详细流程
(1) 一旦触发了检查点,Checkpointer后台会触发MOT的CREATE_SNAPSHOT事件。
(2) 当检查点处于REST阶段时,CheckpointManager将等待在COMPLETE阶段启动的事务完成。
(3) CheckpointManager修改checkpoint阶段为PREPARE。如果没有在REST阶段启动提交的事务处于活动状态,则立即进入RESOLVE阶段,否则等待REST阶段启动提交的最后一个事务完成后进入RESOLVE阶段。
(4) RESOLVE阶段标记了虚拟一致性点。在此阶段不允许任何事务开始提交,以避免在openGauss采取检查点的重做点之前这些事务写入重做日志。CheckpointManager等待在PREPARE阶段启动的事务完成。
(5) CheckpointManager准备要flush的表的列表(任务列表)并读取这些表的锁状态。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数同学面临毕业设计项目选题时,很多人都会感到无从下手,尤其是对于计算机专业的学生来说,选择一个合适的题目尤为重要。因为毕业设计不仅是我们在大学四年学习的一个总结,更是展示自己能力的重要机会。
因此收集整理了一份《2024年计算机毕业设计项目大全》,初衷也很简单,就是希望能够帮助提高效率,同时减轻大家的负担。
既有Java、Web、PHP、也有C、小程序、Python等项目供你选择,真正体系化!
由于项目比较多,这里只是将部分目录截图出来,每个节点里面都包含素材文档、项目源码、讲解视频
如果你觉得这些内容对你有帮助,可以添加VX:vip1024c (备注项目大全获取)
8年进入阿里一直到现在。**
深知大多数同学面临毕业设计项目选题时,很多人都会感到无从下手,尤其是对于计算机专业的学生来说,选择一个合适的题目尤为重要。因为毕业设计不仅是我们在大学四年学习的一个总结,更是展示自己能力的重要机会。
因此收集整理了一份《2024年计算机毕业设计项目大全》,初衷也很简单,就是希望能够帮助提高效率,同时减轻大家的负担。
[外链图片转存中…(img-WN8PL21N-1712529693848)]
[外链图片转存中…(img-kicFyHdj-1712529693848)]
[外链图片转存中…(img-oBk9BHHA-1712529693849)]
既有Java、Web、PHP、也有C、小程序、Python等项目供你选择,真正体系化!
由于项目比较多,这里只是将部分目录截图出来,每个节点里面都包含素材文档、项目源码、讲解视频
如果你觉得这些内容对你有帮助,可以添加VX:vip1024c (备注项目大全获取)
[外链图片转存中…(img-cWHetYWC-1712529693849)]