海山数据库(He3DB)源码详解:CommitTransaction函数源码详解

server/2024/9/23 14:26:29/

文章目录

  • 海山数据库(He3DB)源码详解:CommitTransaction函数
    • 1. 执行条件
    • 2. 执行过程
      • 2.1 获取当前节点状态:
      • 2.2 检查当前状态:
      • 2.3 预提交处理:
      • 2.4 提交处理:
      • 2.5 释放资源:
      • 2.6 提交事务:
    • 作者介绍

海山数据库(He3DB)源码详解:CommitTransaction函数

本文介绍了事务提交过程中,具体执行提交任务的CommitTransaction函数详细执行流程。

1. 执行条件

  1. 当事务处于TRANS_INPROGRESS状态且事务块处于TBLOCK_STRATED或者TBLOCK_END状态下,由CommitTransactionCommand函数调用CommitTransaction函数完成提交事务。
  2. 在ParallelWorkerMain函数中由EndParallelWorkerTransaction调用。

2. 执行过程

2.1 获取当前节点状态:

获得当前节点变量值CurrentTransactionState,

并创建两个临时变量latestXid和is_parallel_worker。

    TransactionState s = CurrentTransactionState;TransactionId latestXid;bool		is_parallel_worker;
  1. CurrentTransactionState主要保存了当前事务状态相关的参数,主要是事务状态、事务块状态、XID、保存点、父节点等级等一系列参数。
  2. latestXid用来传递保存XID值。
  3. is_parallel_worker主要用于判断当前事务是否使用并行执行(并行执行指的是数据库将某些查询操作分布到多个工作进程中执行,以提高性能)。

2.2 检查当前状态:

检查并行执行模式,检查当前事务状态并判断父节点状态。

    /* Enforce parallel mode restrictions during parallel worker commit. */if (is_parallel_worker)EnterParallelMode();ShowTransactionState("CommitTransaction");/** check the current transaction state*/if (s->state != TRANS_INPROGRESS)elog(WARNING, "CommitTransaction while in %s state",TransStateAsString(s->state));Assert(s->parent == NULL);
  1. 如果执行并行执行模式,调用EnterParallelMode函数执行++s->parallelModeLevel。
  2. 检查当前事务状态,如果不是TRANS_INPROGRESS,则执行elog函数保存并返回主线程PostgreMain,执行Abort过程。
  3. 断言检查当前父节点状态,出错直接退出(调试用的)。

2.3 预提交处理:

第一、通过死循环处理用户定义的一些延迟触发器和一些portal操作。

    for (;;){/** Fire all currently pending deferred triggers.*/AfterTriggerFireDeferred();/** Close open portals (converting holdable ones into static portals)* If there weren't any, we are done. otherwise loop back to check* if they queued deferred triggers.  Lather, rinse, repeat.*/if (!PreCommit_Portals(false))break;}

这里的死循环行为是:因为,事务(增删改)操作完成之后会触发定义的延迟触发器,进行一些其他的行为,而这些其他行为中可能会包括游标的增删改查,导致了一个套娃操作。所以,这里的AfterTriggerFireDeferred函数处理延迟触发器,PreCommit_Portals处理游标,直到触发器处理完且没有游标需要处理,跳出函数。

  1. 不断调用AfterTriggerFireDeferred函数,在当前事务提交之前调用,执行并处理掉所有待处理的延迟触发器。
  2. 通过判断PreCommit_Portals函数的执行结果,来选择是否跳出死循环。PreCommit_Portals函数的作用,首先是将本次事务过程中的持久化游标物质化,并关闭游标对应需要的执行器,释放持有的锁;然后关闭本次事务过程中的一些非持久门户(Non-holdable portals),其他事务留存下来的持久性门户(Portals)不做改变。如果游标没有做任何操作,即该事务没有其他游标操作,返回false,否则返回true。

持久化游标物质化是指将持久化游标的结果集存储到磁盘或内存中,确保之后的游标查询结果不受后续事务操作对数据的影响。

第二、通过回调函数关闭参数对应的事务内部服务。

    CallXactCallbacks(is_parallel_worker ? XACT_EVENT_PARALLEL_PRE_COMMIT: XACT_EVENT_PRE_COMMIT);

第三、清理并行执行的资源,清理所有的触发器。

    /* If we might have parallel workers, clean them up now. */if (IsInParallelMode())AtEOXact_Parallel(true);/* Shut down the deferred-trigger manager */AfterTriggerEndXact(true);

第四、在关闭游标后调用PreCommit_on_commit_actions函数。

执行ON COMMIT子句定义的行为,包括对临时表的处理和游标的处理等,确保之前的游标都被正确的关闭,不会造成悬空引用(类似于悬空指针)。

    /** Let ON COMMIT management do its thing (must happen after closing* cursors, to avoid dangling-reference problems)*/PreCommit_on_commit_actions();

第五、调用smgrDoPendingSyncs函数和AtEOXact_LargeObject函数。

    /** Synchronize files that are created and not WAL-logged during this* transaction. This must happen before AtEOXact_RelationMap(), * so that we don't see committed-but-broken files after a crash.*/smgrDoPendingSyncs(true, is_parallel_worker);/* close large objects before lower-level cleanup */AtEOXact_LargeObject(true);
  1. smgrDoPendingSyncs函数会在当前事务期间,对那些被创建且其更改没有记录在 WAL(Write-Ahead Logging)日志中的文件进行同步。这是为了确保如果发生崩溃,这些文件的状态是一致的,并且不会留下已提交但损坏的文件。

  2. AtEOXact_LargeObject函数主要是清理一些超出常规字段所能存储限制的“大对象”,关闭与大型对象关联的文件描述符(LO fds),并清空 cookie 数组。

    这样做的结果是,这些文件描述符将不再有效,无法再被用来访问或操作大型对象。无论事务是提交还是中止,持有这些 LO fds 的内存上下文和资源所有者在事务结束时都将被销毁。这意味着这些资源在事务结束时无论如何都会被清理。

    在事务提交的情况下,需要关闭这些 LO fds,以避免在提交时出现关于资源泄露的警告。这是因为在提交时,系统会检查所有资源是否已经被正确管理,包括打开的文件描述符。

    在事务中止的情况下,可以跳过关闭 LO fds 的步骤。这是因为在中止事务时,系统会回滚所有更改,并且通常不需要执行与提交相同的清理操作。

第六、调用PreCommit_Notify函数处理事务提交前与NOTIFY命令相关的操作。

它确保了NOTIFY命令能够在事务提交时正确地通知其他会话或应用程序。

    /** Insert notifications sent by NOTIFY commands into the queue.This* should be late in the pre-commit sequence to minimize time spent* holding the notify-insertion lock.However, this could result in* creating a snapshot,so we must do it before serializable cleanup.*/PreCommit_Notify();

第七、判断当前是否为并行执行模式。

False,执行PreCommit_CheckForSerializationFailure 函数,在事务准备提交时,检查当前事务是否可能与其他事务发生了冲突。如果检测到冲突,函数会抛出一个错误,导致当前事务回滚。(这里主要发生在并发执行情况下或者多个表的复杂查询事务)
通过这种方式,PostgreSQL 确保事务的串行化执行,避免数据不一致的问题。

    /** Mark serializable transaction as complete for predicate locking* purposes.This should be done as late as we can put it and still allow* errors to be raised for failure patterns found at commit.This is not* appropriate in a parallel worker however,because we aren't committing* the leader's transaction and its serializable state will live on.*/if (!is_parallel_worker)PreCommit_CheckForSerializationFailure();
  1. 串行化是事务隔离级别中最高的级别,它确保事务的执行就像它们是串行执行的一样,避免了所有并发问题。
  2. 可序列化事务(Serializable Transaction):可序列化事务是数据库事务的最高隔离级别,它通过完全串行化事务的执行来避免所有类型的并发问题,如脏读、不可重复读和幻读。
  3. 谓词锁定(Predicate Locking):谓词锁定是一种数据库锁定技术,用于锁定满足特定条件的数据行,而不是锁定整个表或数据页。这允许更细粒度的并发控制。

第八、关闭中断机制,清理重置映射,正式开始提交事务清理资源。

    /* Prevent cancel/die interrupt while cleaning up */HOLD_INTERRUPTS();/* Commit updates to the relation map --- do this as late as possible */AtEOXact_RelationMap(true, is_parallel_worker);

2.4 提交处理:

第一、修改事务状态,开始事务提交

    /** set the current transaction state information appropriately during* commit processing*/s->state = TRANS_COMMIT;s->parallelModeLevel = 0;

第二、根据并行事务状态,处理日志。

检查并行执行状态。不是并行事务,调用RecordTransactionCommit函数记录当前XID的日志。是并行事务,则调用ParallelWorkerReportLastRecEnd函数处理WAL日志,将当前XID置为InvalidTransactionId。

    if (!is_parallel_worker){/** We need to mark our XIDs as committed in pg_xact.This is where we* durably commit.*/latestXid = RecordTransactionCommit();}else{/** We must not mark our XID committed; the parallel leader is* responsible for that. */latestXid = InvalidTransactionId;/** Make sure the leader will know about any WAL we wrote before it* commits. */ParallelWorkerReportLastRecEnd(XactLastRecEnd);}

ParallelWorkerReportLastRecEnd函数的目的是确保领导者进程在我们提交事务之前知道工作者进程已经写入的所有 WAL 日志。这是必要的,因为在并行事务处理中,领导者进程需要等待所有工作者进程完成它们的任务,并且记录了它们所有的更改,然后才能安全地提交整个事务。

第三、通知系统当前进程(MyProc)已经结束了一个事务。

ProcArrayEndTransaction 函数用于通知系统当前进程(MyProc)已经结束了一个事务,并且提供了要结束的事务 XID(latestXid)。ProcArrayEndTransaction 调用的顺序要求:

  1. 必须在释放当前进程持有的锁之前执行。这是因为在锁释放之前,其他进程需要知道当前进程已经结束了事务,以避免潜在的死锁或资源争用问题。
  2. 同时,它也必须在 RecordTransactionCommit 调用之后执行。RecordTransactionCommit 函数用于记录事务提交到 WAL(Write-Ahead Logging)日志,确保事务的持久性。
    ProcArrayEndTransaction(MyProc, latestXid);

ProcArrayEndTransaction函数标记了一个事务不在运行。从这一步以后,即便出错也来不及阻止事务提交了,后续都是资源的释放和参数重置过程。

2.5 释放资源:

第一、释放TopTransactionResourceOwner所拥有的资源。

  1. AtEOXact_Buffers函数清理缓冲区。
  2. AtEOXact_RelationCache函数清理关系缓存。
  3. AtEOXact_Inval函数在主事务结束时处理排队中的无效信息。
  4. AtEOXact_MultiXact函数结束多事务。
    ResourceOwnerRelease(TopTransactionResourceOwner,RESOURCE_RELEASE_BEFORE_LOCKS,true, true);/* Check we've released all buffer pins */AtEOXact_Buffers(true);/* Clean up the relation cache */AtEOXact_RelationCache(true);/** Make catalog changes visible to all backends.This has to happen after* relcache references are dropped (see comments for* AtEOXact_RelationCache), but before locks are released (if anyone is* waiting for lock on a relation we've modified, we want them to know* about the catalog change before they start using the relation). */AtEOXact_Inval(true);AtEOXact_MultiXact();ResourceOwnerRelease(TopTransactionResourceOwner,RESOURCE_RELEASE_LOCKS,true, true);ResourceOwnerRelease(TopTransactionResourceOwner,RESOURCE_RELEASE_AFTER_LOCKS,true, true);

ResourceOwnerRelease函数释放参数TopTransactionResourceOwner及其后代拥有的所有资源,但不删除“资源Owner”对象本身。

第二、处理删除操作并通知事务成功完成。

    smgrDoPendingDeletes(true);/** Send out notification signals to other backends (and do other* post-commit NOTIFY cleanup).  This must not happen until after our* transaction is fully done from the viewpoint of other backends.*/AtCommit_Notify();

第三、依次清理资源。

  1. AtEOXact_GUC(true, 1);
    处理:运行时配置参数(GUC)。
    作用:在事务结束时应用或撤销事务期间设置的配置更改。
  2. AtEOXact_SPI(true);
    处理:服务器过程接口(SPI)。
    作用:清理 SPI 在事务中使用的资源和状态。
  3. AtEOXact_Enum();
    处理:枚举类型。
    作用:管理枚举类型的元数据,确保事务结束后枚举状态是一致的。
  4. AtEOXact_on_commit_actions(true);
    处理:提交时执行的动作。
    作用:执行在事务提交时需要进行的特定动作或回调。
  5. AtEOXact_Namespace(true, is_parallel_worker);
    处理:命名空间。
    作用:清理命名空间相关的资源,可能与并行工作有关。
  6. AtEOXact_SMgr();
    处理:存储管理器。
    作用:管理文件和存储相关的资源,如释放锁或处理文件删除。
  7. AtEOXact_Files(true);
    处理:文件资源。
    作用:清理事务中使用的文件,如临时文件。
  8. AtEOXact_ComboCid();
    处理:组合命令 ID。
    作用:管理与组合命令 ID 相关的事务状态。
  9. AtEOXact_HashTables(true);
    处理:哈希表。
    作用:清理或刷新哈希表,确保数据结构的一致性。
  10. AtEOXact_PgStat(true, is_parallel_worker);
    处理:pg_stat 统计信息。
    作用:更新统计收集模块的状态,可能与并行工作有关。
  11. AtEOXact_Snapshot(true, false);
    处理:事务快照。
    作用:清理事务快照,确保 MVCC 状态下的数据可见性正确。
  12. AtEOXact_ApplyLauncher(true);
    处理:逻辑复制应用启动器。
    作用:管理逻辑复制相关的事务结束时的资源和状态。
  13. pgstat_report_xact_timestamp(0);
    处理:事务时间戳统计。
    作用:报告事务的时间戳信息给 pg_stat 模块,0 可能表示当前时间或其他特定值。
    /** Everything after this should be purely internal-to-this-backend* cleanup.*/AtEOXact_GUC(true, 1);AtEOXact_SPI(true);AtEOXact_Enum();AtEOXact_on_commit_actions(true);AtEOXact_Namespace(true, is_parallel_worker);AtEOXact_SMgr();AtEOXact_Files(true);AtEOXact_ComboCid();AtEOXact_HashTables(true);AtEOXact_PgStat(true, is_parallel_worker);AtEOXact_Snapshot(true, false);AtEOXact_ApplyLauncher(true);pgstat_report_xact_timestamp(0);

第四、重置全局变量和状态变量。

重置CurrentResourceOwner、XactTopFullTransactionId、CurTransactionResourceOwner、TopTransactionResourceOwner、nParallelCurrentXids和事务状态变量TransactionState s。ResourceOwnerDelete函数会删除TopTransactionResourceOwner参数的对象及其后代。

    CurrentResourceOwner = NULL;ResourceOwnerDelete(TopTransactionResourceOwner);s->curTransactionOwner = NULL;CurTransactionResourceOwner = NULL;TopTransactionResourceOwner = NULL;AtCommit_Memory();s->fullTransactionId = InvalidFullTransactionId;s->subTransactionId = InvalidSubTransactionId;s->nestingLevel = 0;s->gucNestLevel = 0;s->childXids = NULL;s->nChildXids = 0;s->maxChildXids = 0;XactTopFullTransactionId = InvalidFullTransactionId;nParallelCurrentXids = 0;

2.6 提交事务:

修改事务状态为TRANS_DEFAULT,放开中断机制,完成事务提交。

    s->state = TRANS_DEFAULT;RESUME_INTERRUPTS();

作者介绍

李超,移动云数据库工程师,负责云原生数据库He3DB的研发。


http://www.ppmy.cn/server/102616.html

相关文章

Servlet---axios框架 ▎路由守卫

前言 在现代Web应用中,前端和后端通常分离,前端使用框架(如Vue.js、React)与后端服务交互。Servlet是Java EE中处理HTTP请求的重要组成部分,能够生成动态Web内容。 Axios是一个基于Promise的HTTP客户端,简…

并查集(模板+例题)

文章目录 模板[1249. 亲戚 - AcWing题库](https://www.acwing.com/problem/content/description/1251/)思路代码 [237. 程序自动分析 - AcWing题库](https://www.acwing.com/file_system/file/content/whole/index/content/3788/)思路代码 [145. 超市 - AcWing题库](https://ww…

C++面向对象编程(上)

类与对象属于面向对象的程序设计思想(Object Oriented Programming),简称OOP。 面向对象基础理论 面向对象是一种对现实世界理解和抽象的方法,是计算机编程技术发展到一定阶段后的产物,是一种软件开发的方法 面向对象四大特性 1.抽象 忽…

1.微服务发展阶段

单体应用阶段 简介 系统业务量很小的时候我们把所有的代码都放在一个项目中,然后将这个项目部署在一台服务器上,整个项目所有的服务都由这台服务器去提供 优点 1.展现层、控制层、持久层全都在一个应用里面,调用方便、快速,单个请…

【Qt】Qt窗口 | QDialog 对话框

文章目录 一. 对话框二. 对话框的分类1. 非模态对话框2. 模态对话框3. 混合属性对话框 三. 自定义对话框1. 代码实现2. ui文件实现 四. 内置对话框1. QMessageBox 消息对话框2. QColorDialog 颜色对话框3. QFileDialog 文件对话框4. QFontDialog 字体对话框5. QInputDialog 输入…

直播App遭受抓包后的DDoS与CC攻击防御策略

随着直播应用的普及,越来越多的用户开始依赖这些平台进行娱乐和社交活动。然而,这也使得直播平台成为网络攻击的目标之一。其中,DDoS(分布式拒绝服务)攻击和CC(Challenge Collapsar,即HTTP慢速攻…

【AI 绘画】模型转换与快速生图(基于diffusers)

AI 绘画- 模型转换与快速生图(基于diffusers) 1. 本章介绍 本次主要展示一下不同框架内文生图模型转换,以及快速生成图片的方法。 SDXL文生图 2. sdxl_lightning基本原理 模型基本原理介绍如下 利用蒸馏方法获取小参数模型。首先&#x…

国内无法更新linux,无法使用sudo yum update

步骤 1: 添加国内镜像源 您可以选择国内的一些知名的镜像站点,如清华大学、阿里云等。这里以阿里云为例来说明如何操作: 备份原始的 CentOS 配置文件: sudo cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backu…