MVCC(1)--概述

引言

上一篇博客初步了解了行存储引擎的访存方式后,我将展开对行存储引擎的MVCC技术展开详细学习。

一、MVCC回顾

MVCC(多版本并发控制)是一种用于数据库并发控制的技术,它可以在不锁定整个表的情况下实现事务的隔离。在 存储引擎概述中有所提到。

二、MVCC相关名词概念介绍

快照

在了解MVCC技术之前,我们先来了解一个和他密切相关的概念——快照, 快照是数据库的一个只读副本,它记录了某一时刻数据库的状态。你可以把它想象成一个数据库的“照片”。快照可以用于备份,也可以用于在出现问题时恢复数据。

它具有如下好处:

  • 提供了一个静态的视图来为报表提供服务。
  • 可以利用数据库快照来恢复数据库,相比备份恢复来说,这个速度会大大提高。
  • 和数据库镜像结合使用,提供读写分离作为测试环境或数据变更前的备份。 当一个新的事务开始时,服务器会为这个事务创建一个新的快照。这个快照包含了一个事务在开始时数据库的一致性状态。这个快照被用来在事务中所有的数据修改被应用到数据库之前,保证所有读取操作能在这个一致性状态上进行。 以下是创建和使用快照的一些关键步骤:
  1. 在新的事务开始时,服务器会调用函数来设置新的快照。这个函数会创建一个新的快照并把它设置到事务的上下文中。
  2. 当一个长事务进行读取操作时,它会在开始读取前调用函数来设置读取的快照。这个函数会获取当前的快照并将它设置到事务的上下文中。
  3. 当一个长事务进行写入操作时,它会在开始写入前调用函数来设置写入的快照。这个函数会获取当前的快照并将它设置到事务的上下文中。在事务提交时,这个快照会被用来确定哪些读取操作需要被重试以便保持数据库的一致性。
  4. 在一个长事务结束时,它会在提交或回滚前调用函数来结束事务并清理资源。这个函数会结束当前的事务并释放相关的资源,包括删除或更新快照。 以上是快照在 OpenGauss 中的基本实现方式。具体的实现细节和优化会因为 OpenGauss 的版本和配置不同而有所差异。具体的实现代码可以在 OpenGauss 的源代码中找到,可以从 src/storage/access/transam/ 目录开始搜索相关函数和代码

闪回查询

闪回查询(Flashback Query)是一种特殊的查询,它可以查看过去某一时刻的数据。这是通过使用undo日志来实现的,undo日志保存了数据修改前的原始数据。当执行闪回查询时,系统会查找undo日志,并用其中的信息将数据恢复到指定时间点的状态。 闪回查询是一种查询技术,可以查询数据库中某个时间点之前的记录,也可以查询某个事务执行前的数据库状态。其应用位置如下图所示:

以下概述一下 OpenGauss 中闪回查询的实现原理和涉及的关键步骤。 OpenGauss 中闪回查询的实现主要依赖于WAL(Write-Ahead Logging)和MVCC(Multi-Version Concurrency Control)两个机制。

  • WAL 用于保证数据的持久性和一致性

  • MVCC 用于支持并发访问和事务隔离。 闪回查询的基本思路是利用 WAL 和 MVCC 的机制,将数据库状态“回滚”到特定的时间点,然后执行查询操作。具体步骤如下:

  • 确定要回滚的时间点:通过时间戳或特定的事务 ID 来确定要回滚到哪个时间点。

  • 查找闪回点:在 WAL 日志中查找与确定的时间点相对应的闪回点,通常是一个事务的结束点。

  • 回滚数据库:利用 WAL 日志中的信息,将数据库回滚到闪回点所处的状态。在此过程中,需要处理 MVCC 的并发访问和事务隔离问题,保证回滚操作的正确性和一致性。

  • 执行查询操作:在回滚后的数据库状态上执行查询操作,获取所需的数据。

闪回查询的实现涉及到的关键代码主要分布在 open gauss 的 src/storage/access/transam 目录中,具体包括 xlog.cpp、xlogutils.cpp、recovery.cpp、recovery_impl.cpp、heapam.cpp 等文件。

  • xlog.cpp 和 xlogutils.cpp 主要处理 WAL 日志的读写和解析
  • recovery.cpp 和 recovery_impl.cpp 主要处理数据库的恢复操作
  • heapam.cpp 则主要处理堆表的并发访问和事务隔离 需要注意的是,OpenGauss 的闪回查询功能通常需要在配置文件中开启相关的参数(例如 fsync、full_page_writes 等),并且需要使用特定的 SQL 语句(例如 SELECT * FROM table_name AS OF TIMESTAMP timestamp来执行闪回查询操作。因此,具体实现代码会因版本和配置不同而有所差异。

在数据库中,快照和闪回都是用于数据恢复和查询历史数据的技术。因此,快照和闪回查询都可以用于查看和恢复历史数据,但它们的工作方式和使用场景可能会有所不同。快照通常用于备份和恢复整个数据库,而闪回查询则更适合于查看和恢复单个表或行的历史数据。

三、MVCC详解

解决问题

MVCC解决的是读写并发冲突问题。更新数据的时候,原地更新,把老版本放到历史版本区页面里,同时维护新版本元组到老元组的指针。读元组的时候,根据快照Snapshot.CSN来判断应该读到哪个版本。

工作方式

MVCC通过为每个读取的行创建数据行的“快照”来工作,也就是说,读取将会看到开始时的数据,而不是最新的数据。

MVCC和快照读关系

快照读是MVCC下的具体读取操作。在read committed和repeatable read隔离级别下,innodb使用MVCC。但是它们对于快照数据的定义不同:在read committed隔离级别下,对于快照数据总是读取被锁定行的最新一份快照数据。而在repeatable read隔离级别下,对于快照数据总是读取事务开始时的行数据版本。

因此,MVCC和快照之间有着密切的关系。MVCC使用快照来实现并发控制,而快照则提供了一个机制来查看和访问数据的历史版本。 在 OpenGauss 中,快照是 MVCC 并发控制机制的一种实现方式。

其运行流程如下图所示:

简单地总结如下: 1.如果当前事务ID小于一行的xmin,那么就需要检索xmin对应的Clog,读取此事务状态,以此来判断此行数据是否对当前事务可见。 2.如果当前事务ID大于一行中的xmax,那么说明此行数据的更新/删除发生于本事务开始之前,此行数据对本事务一定不可见(但不排除此行数据的新版本对本事务可见,因为新旧版本是单独进行判断的)。 3.如果xid落在了xmin、xmax中间,就需要依据CSN来判断本事务的快照下对应数据是否应该被看到,需要检索CSN Log来进行对比判断。

简单来说,OPENGAUSS的MVCC实现包括了以下几个部分:
  • 事务管理器:它是事务系统的核心。它被实现为一个有限循环状态机。它接收来自外部系统的命令,并根据当前事务的状态确定事务的下一个执行过程。
  • 日志管理器:它记录了事务执行状态和数据更改过程,包括事务提交日志(Clogs),事务提交序列号日志(CSNlogs),和事务日志(Xlogs)。Clogs 只记录事务执行结果。CSNlogs 记录日志提交的顺序以确定可见性。Xlogs 是用于数据恢复和持久性的重做日志。
  • 线程管理机制:所有线程的事务信息都记录在一个内存区域。任何线程都可以访问这个区域以获取其他事务的状态信息。
  • 锁管理器:它控制系统的写并发性,并使用锁机制来确保事务写过程之间的隔离。
以下是一些涉及MVCC的主要部分和文件:

src/gausskernel/storage/lmgr:这个目录包含了与锁管理和事务管理相关的源代码,是MVCC的关键部分。在这里,你可以找到事务控制器、锁管理器以及与MVCC并发控制相关的文件。

src/gausskernel/storage/access/transam:这个目录包含了事务管理和数据访问层的核心代码。其中,有一些文件,如clog.c,包含了与MVCC相关的实现细节,例如可见性检查和事务日志记录。

src/gausskernel/storage/access:这个目录包含了与数据访问和索引相关的头文件,其中一些头文件定义了MVCC的数据结构和函数接口。

四、MVCC实现重点

我们需要关注: 1.元组版本号的实现:使用元组头部信息的字段来标示元组的版本号 重点关注元组的插入、删除、更新,该内容已在博客阶段小结中有详细介绍,此处仅对元组版本号信息记录位置做简单补充说明。 在src/include/access/htup.h中定义了元组的头部信息

 
  1. ypedef struct HeapTupleHeaderData {
  2. union {
  3. HeapTupleFields t_heap; /* 存储该元组的一些描述信息 */
  4. DatumTupleFields t_datum;
  5. } t_choice;
  6. ItemPointerData t_ctid; /* (块号,块内偏移) 存储用来记录当前元组或新元组的物理位置 */
  7. /* Fields below here must match MinimalTupleData! */
  8. uint16 t_infomask2;
  9. uint16 t_infomask; /* various flag bits, see below */
  10. uint8 t_hoff;
  11. /* ^ - 23 bytes - ^ */
  12. bits8 t_bits[FLEXIBLE_ARRAY_MEMBER];
  13. /* MORE DATA FOLLOWS AT END OF STRUCT */
  14. } HeapTupleHeaderData;
  15. typedef HeapTupleHeaderData* HeapTupleHeader

对与该结构体中的联合体的第一个成员t_heap,我们可以在如下代码中找到其成员变量所记录的信息

 
  1. typedef struct HeapTupleFields {
  2. ShortTransactionId t_xmin; /* 存放插入该 Tuple 时的 txid */
  3. ShortTransactionId t_xmax; /* 存放删除或者更新该 Tuple 时的 txid,如果还没更新或者删除,那么置 0,表示无效 */
  4. union {
  5. CommandId t_cid; /* 创建或更新/删除该 Tuple 的命令在该事务内执行的所有 SQL 命令中的编号 */
  6. ShortTransactionId t_xvac; /* old-style VACUUM FULL xact ID */
  7. } t_field3;
  8. } HeapTupleFields;

2.快照的实现:活跃事务数组方法和时间戳方法 在本文的概念介绍中有所提及。 3.判断数据有效性、可见性、可更新性的算法的实现:XidVisibleInSnapshot和HeapTupleSatisfiesMVCC 4.不同的隔离级别的实现:在一个事务中获取快照的次数

五、小结

综上,我们将围绕MVCC实现重点中尚未分析过的2、3、这两点点展开,逐一解析storage/lmgr,storage/access/transam,文件夹,以期望更好地理解MVCC的工作机制。 下一篇博客MVCC(2)-快照的实现

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值