PolarDB数据库的CSN机制
背景
对postgres数据库熟悉的同学会发现在高并发场景下在获取快照处易出现性能瓶颈,其原因在于PG使用全局数组在共享内存中保存所有事务的状态,在获取快照时需要加锁以保证数据一致性。获取快照时需要持有ProcArraryLock共享锁比遍历ProcArray数组中活跃事务,与此同时提交或回滚的事务需要申请ProcArray排他锁已清除本事务。可想而知,在高并发场景下对ProcArrayLock的申请会成为数据库的瓶颈。为克服上述问题,polardb引入CSN(COMMIT SEQUENCE NUM)事务快照机制避免对ProcarryLock的申请。
1 CSN 机制
1.1 CSN原理
PolarDB在事务层,通过CSN快照来代替PG原生快照
如图所示,每个非只读事务在运行过程中会被分配一个xid,在事务提交时推进CSN;同时会将单前的CSN与事务的XID的映射关系保存起来。
图中实心竖线标识获取快照时刻,会获取最新提交CSN的下一个值4。TX1、TX3、TX5均已提交,其对应的CSN为1、2、3。TX2、TX4、TX6正在运行,TX6、TX8是未来还未开启的事务。对于当前快照而言,严格小于CSN=4的事务的提交结果均可见;其余事务还未提交,不可见。
1.2 CSN的实现
CSN(Commit Sequence Number,提交顺序号)本身与XID(事务号)也会留存一个映射关系,以便将事务本身以及其对应的可见性进行关联,这个映射关系会留存在CSNLog中。事务ID 2048、2049、2050、2051、2052、2053对应的CSN号依次是5、4、7、10、6、8,也就是事务的提交顺序是2049、2048、2052、2050、2053、2051.
PolarDB与之对应为每个事务id分配8个字节uint64的CSN号,所以一个8kB页面能保存1k个事务的CSN号。CSNLOG达到一定大小后会分块,每个CSNLOG文件块的大小为256kB。同xid号类似,CSN号预留了几个特殊的号。CSNLOG定义代码如下:
2 CSN快照与可见性判断
2.1 CSN相关数据结构
polar_csn_mvcc_var_cache结构体维护了最老的活跃事务xid、下一个将要分配的CSN以及最新完成的事务xid。
当其他事务要获取该事务的CSN状态时,如果该事务处于正在提交阶段,那么其他事务通过获取CommitSeqNoLock锁的排他模式来等待其完成。
CSNLogControlLock用于写入csnlog文件时加锁保护。
2.2 CSN快照的获取
PolarDB中获取CSN快照函数为GetSnapshotDataCSN,实现流程如下:
1、获取polar_shmem_csn_mvcc_var_cache->polar_next_csn作为snapshot->polar_snapshot_csn值。
2、snapshot->xmin = polar_shmem_csn_mvcc_var_cache->polar_oldest_active_xid
3、snapshot->xmax=polar_shmem_csn_mvcc_var_cache->polar_latest_completed_xid+1
4、根据GUC参数old_snapshot_threshold,决定是否需要设置snapshot->lsn以及snapshot->whenTaken 。
5、最后根据GUC参数polar_csn_xid_snapshot表示是否从csn快照中生成xid快照。
tatic Snapshot
GetSnapshotDataCSN(Snapshot snapshot)
{TransactionId xmin;TransactionId xmax;CommitSeqNo snapshotcsn;Assert(snapshot != NULL);/** The ProcArrayLock is not needed here. We only set our xmin if* it's not already set. There are only a few functions that check* the xmin under exclusive ProcArrayLock:* 1) ProcArrayInstallRestored/ImportedXmin -- can only care about* our xmin long after it has been first set.* 2) ProcArrayEndTransaction is not called concurrently with* GetSnapshotData.*//* Anything older than oldestActiveXid is surely finished by now. */xmin = pg_atomic_read_u32(&polar_shmem_csn_mvcc_var_cache->polar_oldest_active_xid);/* If no performance issue, we try best to maintain RecentXmin for xid based snapshot */RecentXmin = xmin;/* Announce my xmin, to hold back GlobalXmin. */if (!TransactionIdIsValid(MyPgXact->xmin)){TransactionId oldest_active_xid;MyPgXact->xmin = xmin;TransactionXmin = xmin;/** Recheck, if oldestActiveXid advanced after we read it.** This protects against a race condition with GetRecentGlobalXmin().* If a transaction ends runs GetRecentGlobalXmin(), just after we fetch* polar_oldest_active_xid, but before we set MyPgXact->xmin, it's possible* that GetRecentGlobalXmin() computed a new GlobalXmin that doesn't* cover the xmin that we got. To fix that, check polar_oldest_active_xid* again, after setting xmin. Redoing it once is enough, we don't need* to loop, because the (stale) xmin that we set prevents the same* race condition from advancing RecentGlobalXmin again.** For a brief moment, we can have the situation that our xmin is* lower than RecentGlobalXmin, but it's OK because we don't use that xmin* until we've re-checked and corrected it if necessary.*//** memory barrier to make sure that setting the xmin in our PGPROC entry* is made visible to others, before the read below.*/pg_memory_barrier();oldest_active_xid = pg_atomic_read_u32(&polar_shmem_csn_mvcc_var_cache->polar_oldest_active_xid);if (oldest_active_xid != xmin){/*no cover begin*/xmin = oldest_active_xid;RecentXmin = xmin;MyPgXact->xmin = xmin;TransactionXmin = xmin;/*no cover end*/}}/** Get the current snapshot CSN. This* serializes us with any concurrent commits.*/snapshotcsn = pg_atomic_read_u64(&polar_shmem_csn_mvcc_var_cache->polar_next_csn);/** Also get xmax. It is always latestCompletedXid + 1.* Make sure to read it after CSN (see TransactionIdAsyncCommitTree())*/pg_read_barrier();xmax = pg_atomic_read_u32(&polar_shmem_csn_mvcc_var_cache->polar_latest_completed_xid);Assert(TransactionIdIsNormal(xmax));TransactionIdAdvance(xmax);snapshot->xmin = xmin;snapshot->xmax = xmax;snapshot->polar_snapshot_csn = snapshotcsn;snapshot->polar_csn_xid_snapshot = false;snapshot->xcnt = 0;snapshot->subxcnt = 0;snapshot->suboverflowed = false;snapshot->curcid = GetCurrentCommandId(false);/** This is a new snapshot, so set both refcounts are zero, and mark it as* not copied in persistent memory.*/snapshot->active_count = 0;snapshot->regd_count = 0;snapshot->copied = false;if (old_snapshot_threshold < 0){/** If not using "snapshot too old" feature, fill related fields with* dummy values that don't require any locking.*/snapshot->lsn = InvalidXLogRecPtr;snapshot->whenTaken = 0;}else{/** Capture the current time and WAL stream location in case this* snapshot becomes old enough to need to fall back on the special* "old snapshot" logic.*/snapshot->lsn = GetXLogInsertRecPtr();snapshot->whenTaken = GetSnapshotCurrentTimestamp();MaintainOldSnapshotTimeMapping(snapshot->whenTaken, xmin);}/* * We get RecentGlobalXmin/RecentGlobalDataXmin lazily in polar csn.* In master mode, we reset it when end transaction;* In hot standby mode, wal replayed by startup backend, we has to reset* it when get snapshot,* because RecentGlobalXmin/RecentGlobalDataXmin are backend variables.*/if (RecoveryInProgress())resetGlobalXminCacheCSN();/* * We need xid snapshot, should generate it from csn snapshot.* The logic is:* 1. Scan csnlog from xmin(inclusive) to xmax(exclusive)* 2. Add xids whose status are in_progress or committing or * committed csn >= snapshotcsn to xid array* Like hot standby, we don't know which xids are top-level and which are* subxacts. So we use subxip to store xids as more as possible. */if (polar_csn_xid_snapshot){if (TransactionIdPrecedes(xmin, xmax))polar_csnlog_get_running_xids(xmin, xmax, snapshotcsn, GetMaxSnapshotSubxidCount(),&snapshot->subxcnt, snapshot->subxip, &snapshot->suboverflowed);snapshot->polar_csn_xid_snapshot = true;}return snapshot;
}
2.3 MVCC可见性判断流程
结合行头的结构(其中的xmin、xmax)以及Clog、上述CSNLOG的映射机制,MVCC的大致判断流程如下所示,实现函数为HeapTupleSatisfiesMVCC,对于xid在CSN快照中的可见性判断函数为XidVisibleInSnapshotCSN,其流程图如下:
2.4 事务commit和abort如何更新CSN
CSN快照获取主要依据polar_shmem_csn_mvcc_var_cache变量中维护的成员变量,参考前面的CSN快照获取。
因此,这里主要关注事务在commit和abort时如何更新polar_shmem_csn_mvcc_var_cache的成员变量。
AdvanceOldestActiveXidCSN函数用于推进->polar_oldest_active_xid这个值:
进程退出、事务提交以及回滚之后、以及在备机上回放commit和abort时需要推进polar_shmem_csn_mvcc_var_cache->polar_oldest_active_xid,当事务的xid等于polar_shmem_csn_mvcc_var_cache->polar_oldest_active_xid时,才会推进polar_shmem_csn_mvcc_var_cache->polar_oldest_active_xid的值,否则直接返回。
polar_xact_abort_tree_csn在事务回滚时设置CSN的值(POLAR_CSN_ABORTED),并推进polar_shmem_csn_mvcc_var_cache->polar_latest_completed_xid值。
polar_xact_commit_tree_csn在事务提交时设置该事务CSN的值,并推进和polar_shmem_csn_mvcc_var_cache->polar_latest_completed_xid和polar_shmem_csn_mvcc_var_cache->polar_next_csn的值。
polar_shmem_csn_mvcc_var_cache->polar_next_csn只有事务提交才会推进,回滚事务不会推进该值。
对于开启CSN功能之后,PG中原来的维护xid分配的全局变量ShmemVariableCache中的数据成员只有ShmemVariableCache->nextXid会更新(用于分配xid)。而原来的ShmemVariableCache->latestCompletedXid等在已经被polar_shmem_csn_mvcc_var_cache->polar_latest_completed_xid所取代,因此事务状态变化时并不需要维护其值。
相关文章:

PolarDB数据库的CSN机制
背景 对postgres数据库熟悉的同学会发现在高并发场景下在获取快照处易出现性能瓶颈,其原因在于PG使用全局数组在共享内存中保存所有事务的状态,在获取快照时需要加锁以保证数据一致性。获取快照时需要持有ProcArraryLock共享锁比遍历ProcArray数组中活跃…...

使用kubeadm 部署kubernetes 1.26.1集群 Calico ToR配置
目录 机器信息 升级内核 系统配置 部署容器运行时Containerd 安装crictl客户端命令 配置服务器支持开启ipvs的前提条件 安装 kubeadm、kubelet 和 kubectl 初始化集群 (master) 安装CNI Calico 集群加入node节点 机器信息 主机名集群角色IP内…...

Servlet笔记(11):Servletcontext对象
1、什么是ServletContext ServletContext是一个全局储存空间,随服务器的生命周期变化, Cookie,Session,ServletContext的区别 Cookie: 存在于客户端的本地文本文件 Session: 存在于服务器的文本文件&#…...

EM算法是什么
EM算法是什么 EM算法(Expectation-Maximization Algorithm)是一种用于参数估计的迭代算法。它常被用于含有隐变量(latent variable)的概率模型中,例如高斯混合模型、隐马尔可夫模型等。 EM算法分为两个步骤ÿ…...

C++---线性dp---方格取数(每日一道算法2023.2.25)
注意事项: 本题属于"数字三角形"和"摘花生"两题的进阶版,建议优先看懂那两道,有助理解。 题目: 输入: 8 2 3 13 2 6 6 3 5 7 4 4 14 5 2 21 5 6 4 6 3 15 7 2 14 0 0 0输出: 67#include <cm…...

《第一行代码》 第八章:应用手机多媒体
一,使用通知 第一步,创建项目,书写布局 <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"android:orientation"vertical"android:layout_width"match_parent"android:layout_he…...

C++设计模式(20)——迭代器模式
亦称: Iterator 意图 迭代器模式是一种行为设计模式, 让你能在不暴露集合底层表现形式 (列表、 栈和树等) 的情况下遍历集合中所有的元素。 问题 集合是编程中最常使用的数据类型之一。 尽管如此, 集合只是一组对…...

戴尔Latitude 3410电脑 Hackintosh 黑苹果efi引导文件
原文来源于黑果魏叔官网,转载需注明出处。硬件型号驱动情况主板戴尔Latitude 3410处理器英特尔酷睿i7-10510U已驱动内存8GB已驱动硬盘SK hynix BC511 NVMe SSD已驱动显卡Intel UHD 620Nvidia GeForce MX230(屏蔽)无法驱动声卡Realtek ALC236已驱动网卡Realtek RTL81…...

一起Talk Android吧(第五百零四回:如何调整组件在约束布局中的位置)
文章目录 背景介绍调整方法一调整方法二经验分享各位看官们大家好,上一回中咱们说的例子是"解决retrofit被混淆后代码出错的问题",这一回中咱们说的例子是" 如何调整组件在约束布局中的位置"。闲话休提,言归正转, 让我们一起Talk Android吧! 背景介绍…...

ssh连不上实验室的物理机了
实验室的电脑,不能在校外用 ssh 连接了 192.168.1.33 是本地地址,掩码16位,图1。 192.168.1.14 是实验室的另一台可以ssh连接的物理机,掩码16。 192.168.0.1 是无线路由器地址。 192.168.0.2 是192.168.1.14上的虚拟机地址&#…...

selinux讲解
Selinux讲解 1、selinux的概述 Selinux的历史 Linux安全性与windows在不开启防御措施的时候是一样的;同样是C2级别的安全防护安全级别评定: D–>C1–>C2–>B1–>B2–>B3–>A1 D级,最低安全性C1级,主存取控制…...

【计算机网络】TCP底层设计交互原理
文章目录1.TCP底层三次握手详细流程2.TCP洪水攻击介绍和ss命令浅析3.Linux服务器TCP洪水攻击入侵案例4.TCP洪水攻击结果分析和解决方案5.TCP底层四次挥手详细流程1.TCP底层三次握手详细流程 TCP的可靠性传输机制:TCP三次我手的流程 一次握手:客户端发送一…...

Kotlin1.8新特性
Kotlin1.8.0新特性 新特性概述 JVM 的新实验性功能:递归复制或删除目录内容提升了 kotlin-reflect 性能新的 -Xdebug 编译器选项,提供更出色的调试体验kotlin-stdlib-jdk7 与 kotlin-stdlib-jdk8 合并为 kotlin-stdlib提升了 Objective-C/Swift 互操作…...

【Java8】
1、接口中默认方法修饰为普通方法 在jdk8之前,interface之中可以定义变量和方法,变量必须是public、static、final的,方法必须是public、abstract的,由于这些修饰符都是默认的。 接口定义方法: public抽象方法需要子类实现 接口定…...

阿里 Java 程序员面试经验分享,附带个人学习笔记、路线大纲
背景经历 当时我工作近5年,明显感觉到了瓶颈期。说句不好听的成了老油条,可以每天舒服的混日子(这也有好处,有时间准备面试)。这对于个人成长不利,长此以往可能面临大龄失业。所以我觉得需要痛下决心改变一…...

十大算法基础——上(共有20道例题,大多数为简单题)
一、枚举(Enumerate)算法 定义:就是一个个举例出来,然后看看符不符合条件。 举例:一个数组中的数互不相同,求其中和为0的数对的个数。 for (int i 0; i < n; i)for (int j 0; j < i; j)if (a[i] …...

【PAT甲级题解记录】1018 Public Bike Management (30 分)
【PAT甲级题解记录】1018 Public Bike Management (30 分) 前言 Problem:1018 Public Bike Management (30 分) Tags:dijkstra最短路径 DFS Difficulty:剧情模式 想流点汗 想流点血 死而无憾 Address:1018 Public Bike Managemen…...

SpringCloud————Eureka概述及单机注册中心搭建
Spring Cloud Eureka是Netflix开发的注册发现组件,本身是一个基于REST的服务。提供注册与发现,同时还提供了负载均衡、故障转移等能力。 Eureka组件的三个角色 服务中心服务提供者服务消费者 Eureka Server:服务器端。提供服务的注册和发现…...

原生django raw() 分页
def change_obj_to_dict(self,temp):dict {}dict["wxh_name"] temp.wxh_namedict["types"] temp.typesdict["subject"] temp.subjectdict["ids"] temp.ids# 虽然产品表里没有替代型号,但是通过sql语句的raw()查询可以…...

Android 9.0 Settings 搜索功能屏蔽某个app
1.概述 在9.0的系统rom产品定制化开发过程中,在系统Settings的开发功能中,最近产品需求要求去掉搜索中屏蔽某个app的搜索,就是根据包名,不让搜索出某个app., 在系统setting中,搜索功能中,根据包名过滤掉某个app的搜索功能,所以需要熟悉系统Settings中的搜索的相关功能,…...

SQL性能优化的47个小技巧,果断收藏!
1、先了解MySQL的执行过程 了解了MySQL的执行过程,我们才知道如何进行sql优化。 客户端发送一条查询语句到服务器; 服务器先查询缓存,如果命中缓存,则立即返回存储在缓存中的数据; 未命中缓存后,MySQL通…...

SE | 哇哦!让人不断感叹真香的数据格式!~
1写在前面 最近在用的包经常涉及到SummarizedExperiment格式的文件,不知道大家有没有遇到过。🤒 一开始觉得这种格式真麻烦,后面搞懂了之后发现真是香啊,爱不释手!~😜 2什么是SummarizedExperiment 这种cla…...

运行Qt后出现无法显示字库问题的解决方案
问题描述:运行后字体出现问题QFontDatabase: Cannot find font directory解决前提: 其实就是移植后字体库中是空的,字没办法进行显示本质就是我们只需要通过某种手段将QT界面中的字母所调用的库进行填充即可此处需要注意的是,必须…...

数据库浅谈之共识算法
数据库浅谈之共识算法 HELLO,各位博友好,我是阿呆 🙈🙈🙈 这里是数据库浅谈系列,收录在专栏 DATABASE 中 😜😜😜 本系列阿呆将记录一些数据库领域相关的知识 …...

代码随想录算法训练营 || 贪心算法 455 376 53
Day27贪心算法基础贪心的本质是选择每一阶段的局部最优,从而达到全局最优。刷题或者面试的时候,手动模拟一下感觉可以局部最优推出整体最优,而且想不到反例,那么就试一试贪心。做题的时候,只要想清楚 局部最优 是什么&…...

PMP考前冲刺2.25 | 2023新征程,一举拿证
题目1-2:1.项目经理正在进行挣值分析,计算出了当前的成本偏差和进度偏差。发起人想要知道基于当前的绩效水平,完成所有工作所需的成本。项目经理应该提供以下哪一项数据?A.完工预算(BAC)B.完工估算(EAC)C.完工尚需估算(ETC)D.完工偏差(VAC)2…...

【自然语言处理】Topic Coherence You Need to Know(主题连贯度详解)
Topic Coherence You Need to Know皮皮,京哥皮皮,京哥皮皮,京哥CommunicationUniversityofChinaCommunication\ University\ of\ ChinaCommunication University of China 在大多数关于主题建模的文章中,常用主题连贯度ÿ…...

C++入门:模板
模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。模板是创建泛型类或函数的蓝图或公式。库容器,比如迭代器和算法,都是泛型编程的例子,它们都使用了模板的概念。每个容器都有一个单一的定义,…...

【MySQL】索引常见面试题
文章目录索引常见面试题什么是索引索引的分类什么时候需要 / 不需要创建索引?有什么优化索引的方法?从数据页的角度看B 树InnoDB是如何存储数据的?B 树是如何进行查询的?为什么MySQL采用B 树作为索引?怎样的索引的数…...

【Web逆向】万方数据平台正文的逆向分析(上篇--加密发送请求)—— 逆向protobuf
【Web逆向】万方数据平台正文的逆向分析(上篇--加密发送请求)—— 逆向protobuf声明一、了解protobuf协议:二、前期准备:二、目标网站:三、开始分析:我们一句句分析:先for循环部分:后…...