原创: Aken DB印象
文章链接:https://mp.weixin.qq.com/s/OkJaWbzcXcJtzSCOFnqeXQ

文章作为DB的学习体会,若有错误欢迎指导。

一、环境介绍

操作系统:CentOS Linux release 7.6.1810 (Core) DB版本:PostgreSQL -11.5 on x86_64-pc-linux-gnu

二、问题描述

同一个实例运行的3个session,在T2时刻session 1向表table01插入一行数据之后,session 2和session 3两个会话执行相同的SQL查询的结果不一样。如下:

上图中,session 2查到的是2行记录,session 3却只有1条记录。为什么session 2能看到session 1新插入的记录,而session 3却看不到呢?这种情况是在什么场景下发生的呢?

三、相关理论知识回顾

如果有熟悉事务隔离级别的朋友可能已经想到大概的原因。关于事务的隔离级别的介绍,有兴趣的可以查看上一篇文章。

PostgreSQL的事务隔离级别介绍及更改

在说明原因之前,这里先介绍一下PostgreSQL中取名为“transaction snapshot”这个东西,即事务快照。

至于什么是事务快照,以及为什么需要事务快照,我在官方文档中暂时没有看到具体的描述。

下面是个人的理解,不代表官方:

平时我们执行SQL数据读取的时候,实际上读取的是一种状态数据,transaction snapshot本义上指是某个时刻事务的快照,实质代表的是具体时刻具体事务下数据的状态。

既然是状态,那么可能就有当前状态、上一个状态、下一个状态一说。数据库中所说的事务可看作是将数据从上一个状态进入到另一个状态的单位。

这是数据库中的“词典”,理解起来比较干涩,我们可以对应到人类词典中比较容易理解的三个阶段:过去的、当前的、未来的。

所以,我对事务快照的理解为三个阶段:一个transaction snapshot将事务划分为过去的、当前的、未来的三个区域。

比较友好的是,PostgreSQL官方给我们提供了一个获取事务快照的函数:txid_current_snapshot。下面是官网对txid_current_snapshot函数输出结果的原文解析:

Table 9.75. Snapshot Components for PostgreSQL-12

详细介绍见:https://www.postgresql.org/docs/current/functions-info.html

  • xmin,当前处于active状态的最小事务编号;
  • xmax,未来产生的事务中,第一个将被分配的事务编号;
  • xip_list,当前处于active 状态的事务列表(包括in progress和future状态的事务),其余为inactive。

如下,查看当前时刻事务快照:

(postgres@[local]:5432)[akendb01]#select txid_current_snapshot();txid_current_snapshot-----------------------639:642:639,641 <<
  • 1.xmin=639,表示当前时刻快照中最小的是639这个事务。小于该编号的事务都已经终止(提交、回滚或异常终止),这些事务属于“过去的”范围区域。
  • 2.xmax=642,表示将来新事务产生时分配到的第一个事务编号txid,大于等于642的事务未产生,属于“将来的”范围区域。
  • 3.xip_list=(639,641),表示该快照时刻639和641这两个事务正处于active状态,属于“当前的”范围区域。

画成图就是下面这个样子:

transaction snapshot examples

四、原因分析

在PostgreSQL中,提交读(或者叫读提交)read committed事务隔离级别下,session中同一事务的每条SQL执行的时候都会自动去读取当前时刻的事务快照;而在repeatable read级别下,session中同一事务只会在事务开始的第一个SQL获取一次事务快照。

因为read committed级别下,同一事务中不同时刻的SQL获取的快照可能不一样,因此读到的数据可能会不一样。

而repeatable read在整个事务周期只获取一次事务快照,所以同一事务内所有SQL使用的快照都是一致的,因此可以实现重复读,规避了幻读的产生。

pg默认的事务隔离级别transaction isolation为read committed。这是上面文章开头session 2中read committed事务级别下产生幻读的原因,也是session 3中repeatable read可以实现重复读的原因。

请原谅我在文章开头故意将会话的事务隔离级别忽略,目的是为了引导大家可以一起思考。

说到这里,MySQL的朋友可能觉得PostgreSQL中transaction snapshot和MySQL中的一致性视图Read view有点像。

所以,对于文章开头的问题:

  • 1.对于session 2和session 3的结果来说,上述的问题并非因为数据的不一致,而是因为不同的事务隔离级别读取的结果有所区别。
  • 2.对于session 2来说,在同一个事务里面执行相同的查询语句前后得到的结果不一致,这种情况叫幻读。

什么是幻读? 下面是官方的原文解析:

phantom read

A transaction re-executes a query returning a set of rows that satisfy a search condition and finds that the set of rows satisfying the condition has changed due to another recently-committed transaction.

大概意思指:

在一个事务中相同的SQL查询条件前后读取到的结果不一致,原因是后者读取到了其他事务中新提交的数据。

这个问题其实在PostgreSQL-12官方文档中有所提示,pg中repeatable read隔离级别下是不会出现幻读的。如下图标红处所示:

PostgreSQL-12事务隔离级别

为什么在PostgreSQL中的repeatable read下是Allowed,but not in PG呢?

这正是因为事务快照的作用。下面将文章开始时的例子进行充分的演示。

五、场景演示:提交读、可重复读事务快照对比

下面针对read committed和repeatable read两种事务隔离模式下的事务快照进行对比测试,例子如下:

1.T0时间段:

session 1在默认情况下开启事务,txid=666。

session 2在read committed隔离模式下开启事务,txid=674;

session 3在可重复读repeatable read隔离模式下开启事务,txid=675;

session 4开启事务txid=676(略)。

1)事务开始前table01中只有一行记录:tuple 1

(postgres@[local]:5432)[akendb01]#select * from table01; id | name----+-------- 1 | aken01(1 row)(postgres@[local]:5432)[akendb01]#

2)session 1在默认提交读模式下开启事务,事务编号txid=666。

(postgres@[local]:5432)[akendb01]#begin;BEGIN(postgres@[local]:5432)[akendb01]#show default_transaction_isolation; default_transaction_isolation------------------------------- read committed(1 row)(postgres@[local]:5432)[akendb01]#(postgres@[local]:5432)[akendb01]#select txid_current(); txid_current-------------- 666(1 row)(postgres@[local]:5432)[akendb01]#

3)session 2:在提交读隔离级别下开启事务,事务编号txid=674。

(postgres@[local]:5432)[akendb01]#start transaction isolation level read committed;START TRANSACTION(postgres@[local]:5432)[akendb01]#select txid_current(); txid_current-------------- 674(1 row)

4)session 3:在可重复读隔离级别下开启事务,事务编号txid=675

(postgres@[local]:5432)[akendb01]#start transaction isolation level repeatable read;START TRANSACTION(postgres@[local]:5432)[akendb01]#select txid_current(); txid_current-------------- 675(1 row)

5)session 4:分配一个事务txid=676

(postgres@[local]:5432)[akendb01]#select txid_current(); txid_current-------------- 676(1 row)

2.T1时刻,session 1、2、3获取当前事务快照,并读取table01的记录。

1)session 1:读取到的事务快照为'666:676:674,675',读取表的记录数为1行。

(postgres@[local]:5432)[akendb01]#select txid_current_snapshot(); txid_current_snapshot-----------------------666:676:674,675   <<< 实际上txid=676在session 4已经分配,这个和官网将xmax解析为将来产生的第一个事务有矛盾,pg获取事务快照时最后一个txid是否会滞后?(1 row)(postgres@[local]:5432)[akendb01]#(postgres@[local]:5432)[akendb01]#select * from table01;id | name----+--------1 | aken01(1 rows)(postgres@[local]:5432)[akendb01]#

2)session 2:读取到的事务快照为'666:676:666,675',读取表的记录数为1行。

(postgres@[local]:5432)[akendb01]#select txid_current_snapshot(); txid_current_snapshot----------------------- 666:676:666,675(1 row)(postgres@[local]:5432)[akendb01]#(postgres@[local]:5432)[akendb01]#select * from table01;id | name----+--------1 | aken01(1 rows)(postgres@[local]:5432)[akendb01]#

3)session 3:读取到的事务快照为'666:676:666,674',读取表的记录数为1行。

(postgres@[local]:5432)[akendb01]#select txid_current_snapshot(); txid_current_snapshot----------------------- 666:676:666,674(1 row)(postgres@[local]:5432)[akendb01]#(postgres@[local]:5432)[akendb01]#select * from table01;id | name----+--------1 | aken01(1 rows)(postgres@[local]:5432)[akendb01]#

3.T2时刻,session 1往table01插入一行记录并commit提交,session 1、2、3读取table01的记录。

1)session 1在事务txid=666中获取的事务快照为'674:676:674,675',查看结果中可以看到自己新插入的tuple 2。

(postgres@[local]:5432)[akendb01]#insert into table01 values(2,'aken02');INSERT 0 1(postgres@[local]:5432)[akendb01]#commit;COMMITTED(postgres@[local]:5432)[akendb01]#select txid_current_snapshot();txid_current_snapshot-----------------------674:676:674,675 <<< 事务666已提交,session 1事务快照改变,xmin=674(1 row)(postgres@[local]:5432)[akendb01]#select * from table01;id | name----+--------1 | aken012 | aken02(2 rows)(postgres@[local]:5432)[akendb01]#

2)session 2:

session 2在事务txid=674中获取到的快照为'674:676:675'和T1时刻不同,能看到事务txid=666新插入的tuple 2,产生幻读。

(postgres@[local]:5432)[akendb01]#select txid_current_snapshot();txid_current_snapshot-----------------------674:676:675  <<< session 1的事务666

3)session 3:

session 3在事务txid=675中获取的事务快照依旧为'666:676:666,674',和T1时刻的保持一致,看不到事务txid=666新插入的tuple 2,无幻读产生。

(postgres@[local]:5432)[akendb01]#select txid_current_snapshot();txid_current_snapshot-----------------------666:676:666,674  <<

4.T3时间段

session 2、session 3事务结束,session 1、2、3读取到的事务快照都为“676:676:”,且查询结果相同。

(postgres@[local]:5432)[akendb01]#select txid_current_snapshot();txid_current_snapshot-----------------------676:676: <<

postgresql 插入 时间戳_数据也玩躲猫猫?PostgreSQL中别人提交的数据,我为什么看不到?...相关推荐

  1. csgo社区服务器维护怎么玩躲猫猫,csgo国服怎么玩躲猫猫 国服躲猫猫快速进入方法...

    CSGO的躲猫猫模式得到许多玩家的欢迎.在CSGO中当然少不了诸如僵尸.躲猫猫这些地图制作者制作的娱乐模式,竞技之余享受一下躲猫猫的.下面我们就为大家整理了一些有关csgo国服怎么玩躲猫猫的方法.一起 ...

  2. 小妞妞爱玩躲猫猫-增强版

    现在玩躲猫猫时,小妞妞会躲到厚窗帘里面,把自己一层层卷起来,让你找. 我假装找不到,在她附近说:"咦,妞妞躲到哪儿去了?"她会小声回答:"在这儿呢."我循着声音 ...

  3. R语言使用ggplot2同时可视化dataframe的多个数据列实战:多个数据列可视化在同一个图中、多个数据列可视化在多个图中(纵向多个子图)

    R语言使用ggplot2同时可视化dataframe的多个数据列实战:多个数据列可视化在同一个图中.多个数据列可视化在多个图中(纵向多个子图) 目录

  4. 猫咪藏在哪个房间python_盘点:猫咪玩“躲猫猫”喜欢藏的几个地方,这下再也不愁找不到了...

    一觉醒来,家里的猫不在身边,将被子掀起来自己找,还是没有,床底下?依旧没有,然后找遍整个房间--不知道诸位铲屎官有没有这样的经历,猫咪"人间蒸发",然后又神不知鬼不觉地出现在身边, ...

  5. java向mysql写入数据慢_通过java代码往mysql数据库中写入日期相关数据少13个小时...

    通过show variables like '%time_zone%'; 查看时区: CST 时区 名为 CST 的时区是一个很混乱的时区,有四种含义: 美国中部时间 Central Standard ...

  6. python3读取excel某一列_怎样用python,读取excel中的一列数据!python读取excel某一列数据...

    Python 如何循环读取csv或者excel中的一列数据,写入到中搜索 是可以 a.csv复制到 b.csv中 import csv def foo(): with open('a.csv', 'r ...

  7. 2021的科技卦象·雷·到元宇宙玩“躲猫猫”

    科幻小说的脑洞经常被挪用到技术中,但没有一个词能像"元宇宙"这样声势浩大.势无可挡,毕竟大家都没听说过"流浪地球元年"或者"原力宇宙"之类的 ...

  8. oracle找回删除过的数据吗,oracle数据库中找回删除且已提交的数据

    有时会误删除数据库中的数据,心里很慌,要完蛋了,删库跑路!?等等 我们可以用如下的方法解决: 分为两种方法:scn和时间戳两种方法恢复. 一.通过scn恢复删除且已提交的数据 1.获得当前数据库的sc ...

  9. matlab把某一列作为x轴,excel表格怎么把某一列数据作为x轴-EXCEL表中的两列数据怎样作为X轴和Y轴放在图表里...

    excel表格制作坐标图,如何设定某列(数据直接非线... 只能邦你一半.x轴调整我会,生成图表--图表上方右键--选择数据--水平(分类)标签编辑,点进去,选择你要作为X轴的数据区域,即可. 使用e ...

最新文章

  1. SqlServer索引的原理与应用
  2. 【DM8168学习笔记5】EZSDK目录结构
  3. 敏捷水手——单体法到微服务之旅
  4. 11.6 ConfigParser模块
  5. python零基础能学吗-初学者必知:零基础学习Python真的能学会吗?
  6. 程序员常见的健康问题
  7. 《微观经济学》学习笔记
  8. python 安装talib模块
  9. 监狱数字化集成管理平台
  10. 玩转小米盒子1:选购指南及应用推荐
  11. 计算机类公务员提升空间,本人在公务员省考裸考申论61分,在之后还有多大的提升空间?...
  12. 国内各大企业邮箱,选择看重哪几个方面?
  13. 全面理解ERP和APS:用饭局的例子说明,MRP 还是APS
  14. Visual Studio 2010 Power Tool
  15. Python爬取10529条《三十而已》热评,看看大家都说了些啥
  16. 必读干货丨这项技能玩不转,职场终生当菜鸟
  17. 成功解决:UI验收模板,附:WEB/APP测试的通用用例
  18. java 异步写文件_异步读取文件实例
  19. Python3 实现 KMP 算法核心 PMT
  20. video/audio 音频/视频 标签详解

热门文章

  1. 《软件项目管理(第二版)》第 6 章——项目质量管理 重点部分总结
  2. python中以表示语块_scikitlearn:将数据拟合成块与将其全部拟合到on
  3. 蓝桥杯 历届试题 九宫重排
  4. linux boost教程,Linux上安装使用Boost入门指导
  5. r语言regexpr函数_R语言学习笔记-文本挖掘之字符处理(1)
  6. 页面错误!请稍后再试_微信内嵌H5页面授权和分享
  7. html 百分比正方形,css实现未知宽度的正方形需求
  8. linux的sonar安装,Linux安装sonar
  9. php system 255,GitHub - dwg255/OA-SYS: OA办公系统开源项目
  10. 路由器mysql密码重置密码_【验证】mysql root密码恢复