postgres 中可串行化事务隔离级别原理与实现总结

news/2024/7/9 21:34:34 标签: 数据库, postgresql, database

问题

先引入两个概念:读偏序、写偏序
读偏序:在一个读事务执行期间,另一个事务的写破坏了自己的读约束
写偏序:两个写事务并发期间,都满足了自己的约束,但两个事务提交的结果却破坏了彼此的约束
可串行化隔离级别(SSI)主要解的是写偏序问题,或者更通俗地讲,解的是两个事务串行执行结果与并行执行结果不一致问题。
举例:一个表有 1,2 两行数据,事务 t1 update 1 -> 2,并发事务 t2 update 2 -> 1,则串行执行有两种可能结果 :

  • t1 -> t2 表中数据为 2,2 (t1 读 t2 的写结果并做写操作)
  • t2 -> t1 表中数据为 1,1 (t)
    但并发执行的结果可能是 2,1。
    当发现这种串行化冲突时(两个事务串行执行结果与并行执行结果不一致),SSI 级别会检测这种冲突,并让其中一个事务回滚

解串行化问题的理论

三种读写依赖关系

在并发写的场景下存在以下三种依赖关系,其中 mvcc 保证了 ww 、wr 依赖不冲突,但保证不了 rw 不冲突:
写写依赖(ww):当存在同一个对象上 t1 -> t2 t1 先读写,t2 后读写时(t2 写写依赖 t1),不导致冲突
写读依赖(wr):当存在同一个对象上 t1- > t2 t1 先读写,t2 后读时(t2 写读依赖 t1),不导致冲突
读写依赖(rw):当存在同一个对象上 t1 -> t2 t1 先读,t2 后写时(t2 读写依赖 t1),可能导致冲突
第三种依赖导致冲突发生时,一定存在 t1-> t2 -> t3 这样的序列,比如最简单上面问题中的例子: t1 (rw)-> t2 (rw)-> t1 这种序列,t1 读到 1, t2 写 2->1,t1 再写 1->2 时未将 2->1 改为 2 -> 1 -> 2,这时 t0 提交,t1 再提交,就达不到 t1 把所有1 改为 2 的目的(同样 t2 读不到 t1 写的 2,它只将 2->1,没有1 ->2->1,达不到2全改为1的目的), t1 (rw)-> t2 (rw)-> t3 这种序列叫 危险结构a (rw)-> b 指的是 b 依赖 a,a 是读,b 是写,b 读写依赖 a)。

但出现 t0 读写依赖 t1 读写依赖 t2 时不一定会冲突,比如 t0 -> t1 -> t2,t0 只读不写,而在 t0 拿 snapshot 时 t2 还未提交,那么 t0 就应该读不到 t2 的结果,这时并不冲突。

读写依赖和危险结构的检测

对于读写依赖的检测,需要在读时和写时都做判断:
读时,检查有没有更新版本数据,如果有就存在 rw 依赖。
写时,检查有没有读操作做过的 SIREAD 标记,如果有就存在 rw 依赖。
为了让后续读写能检查到 rw 依赖,需要在事务上做个标记,t1 (rw)-> t2 时,在 t1 上标记 outConflict = true,t2 上标记 inConflict = true
在检查到 rw 依赖时,要判断是否存在危险结构,如果存在,就需要终止自己这个事务:

  • 如果读时发现 tself (rw)-> t2,且 t2 已经提交了,并且有 t3 读写依赖了 t2( t2 (rw)-> t3)则 abort 掉自己(因为 t2 已经 commit,不可能再 abort t2)
  • 如果写时发现 t1 (rw)-> tself(t1 上有 SIREAD标记),且 t1 已经提交了,并且 t1 读写依赖了 t0 (t0 (rw)->t1)则 abort 掉自己(因为 t1 已经 commit,不可能再 abort t1)

在事务提交时,如果发现 t1 (rw)->tself (rw)-> t2,则 tself 也要 abort

危险结构检查总结

读时检查 tself (rw)-> t1 (rw)-> t2
写时检查 t1 (rw)-> t2 (rw)-> tself
提交时检查 t1 (rw)-> tself (rw)-> t2

pg 的理论优化

1、当 t1 是只读事务时,t1->t2->t3 t3 已经提交后t1 才获取 snapshot,才可能成环(因为它是只读的,能成环一定是读到了已经提交事务的写入),t3 未提交不会成环
2、引入 BEGIN TRANSACTION READ ONLY, DEFERRABLE 命令,可以让这个只读事务拿快照锁定事务区间以后等所有并行事务都结束才真执行。如果这个区间中又出现了rw依赖,则要重拿快照,重新锁定区间,这样保证了没有并行事务。

pg 的 SSI 实现

pg 引入谓词锁概念,对应于理论中的 SIREAD 锁
谓词锁有三种级别 行级 -> 页级 -> 表级。
默认条件下,当一个页上有两个行级谓词锁时,就升级为页谓词锁,当表上有32个页锁或行锁时,就升级为表谓词锁。
谓词锁的事务状态存在 SERIALIZABLEXACT 状态体中,在事务提交后依然存在,当SLRU 满时,无法再添加事务状态时,会触发 summary,清理很老的 SERIALIZABLEXACT,老的已提交事务共用 OldCommittedSxact 这个结构体,下文中 tsummary 就表示这些已提交的事务。

读操作

对于读操作,对于安全快照,自己只读,t2 之后没有事务,或它们都在自己获取 snapshot 之后,则不会冲突。
当发现 t2 写在自己 tself 之后 (tself (rw)-> t2),则比较:

tself 回滚的情况

1、自己不是只读,且有 tself->t2->t3 则可能构成危险结构
2、自己是只读,且 t3集合 中最小的commit事务(或 tsummary)在自己获取快照前提交,则构成危险结构
3、无论自己是否只读,如果存在 t1(或 tsummary)->tself-> t2,则构成危险结构

t2 回滚的情况

1、tself->t2->tsummary,且 t2 未提交则标记 t2 DOOMED(t2 在 precommit 时会发现并回滚),如果t2 提交了,则回滚自己

写操作

tself 回滚的场景

1、t1-> tself -> t2 t2 已经提交,或属于 summary 事务
2、t2 在 prepare 提交阶段,t1 和 tself 要么没提交,要么提交在 t2 之后,这时如果 t1 是写则冲突,t1 是读但获取快照在 t2 提交之后也冲突
3、t0 -> t1 -> tself,t0 未提交或在 tself 之后提交,这时t0 是写事务,则冲突,t0 是读事务但获取快照比 tself 晚,则冲突

t2 回滚场景


http://www.niftyadmin.cn/n/457901.html

相关文章

C++【STL】之stack和queue学习

文章目录: 1. 容器适配器1.1 适配器的概念1.2 STL标准库中stack和queue的底层结构 2. 栈stack2.1 stack的使用2.2 stack模拟实现 3. 队列queue3.1 queue的使用3.2 queue模拟实现 1. 容器适配器 1.1 适配器的概念 适配器是一种设计模式(设计模式是一套被反复使用的…

C++ 指针和引用的区别

指针是C系语言的一大特色,也在很大程度上体现着C/C的精髓,一个数据对象的内存地址称为该数据对象的指针。本质上指针是一个变量,所以它有自己的类型,这个类型决定了它可以指向何种类型的数据对象;之所以使用数据对象这…

基于深度学习的人脸检测技术

用到环境 1、pycharm community edition 2022.3.2 2、Python 3.10 整篇内容都已上传至我的csdn资源中,想用的请移步。 流程 多任务级联卷积神经网络(Multi-task Cascaded Convolutional Networks, MTCNN)算法进行人脸检测 普通人脸检测 单人人脸检测 图1 单人人…

网络 - 你可知 Telnet 能通但是 Ping 不通百思不得其解

问题描述 以前本人以为 telnet 通 ping 一定也是通的,telnet 能通,表示两台计算机之间建立了连接通道。理论上是能 ping 通的。 但是今天万万没想到,并不是这样... 原因分析 如果不能 ping 通,可能的原因是对方主机关闭了 ping…

基於Hadoop HA 在kerberos中配置datax

概要 提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 概要 前言一、基於HADOOP HA 搭建datax二、基於HADOOP HA 配置好的datax去配置kerberos1.在datax的配置文件中進行配置2.在shell腳本中加入認證語句 总结 前言…

国产MCU有哪些?

国产MCU有哪些? 文章目录 国产MCU有哪些?1、家电和消费电子2、物联网3、智能表计/IC卡和安全4、计算机和网络通信5、工业控制6、汽车电子7、总结 MCU是微控制器的简称,是一种集成了CPU、RAM、ROM、I/O等功能的单片机,广泛应用于各…

新电脑机环境安装笔记

「Navicat_15.0.25_64bit_Setup.exe」 下载https://www.aliyundrive.com/s/b9xUw2JpuJb Navicat Keygen Patch v5.6.0 下载 https://www.aliyundrive.com/s/YYyE5BQMMuN 全程断网操作 patch 将安装目录选中 提示 check 64 mysql安装: https://baijiahao.baidu…

linux如何设置守护进程

第一步:创建执行sh脚本,设置执行权限 #!/bin/bash # 检测的应用程序名称 APP_NAME"clash-linux-amd64-v1.13.0" while true; do # 检测应用程序是否正在运行 if ps -ef | grep clash-linux-amd64-v1.13.0 |grep -v grep >/dev/nul…