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

073e1f52cbda9fe50ce1cb365e6908ef.gif

原创: 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查询的结果不一样。如下:

096bb9fd46409be0203ff4ca2bef83d4.png


上图中,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状态,属于“当前的”范围区域。

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

485e4ee9f2924373d6297f744ebb0c17.png

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隔离级别下是不会出现幻读的。如下图标红处所示:

5981df13ec2cb35cc9df2e689ef5822d.png

PostgreSQL-12事务隔离级别

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

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

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

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

67085f7bf0d0d283af80f4ed30271011.png

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: <<
71e54298b401251e51524326871d5d10.gif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/538329.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VMware打卡虚拟机提示“此虚拟机可能已被复制或移动”

“我已移动虚拟机” //表示打开后的虚拟的网卡的mac地址不变&#xff0c;如果复制本地的&#xff0c;同时开机在一个vmnet可能造成冲突。 “我已复制虚拟机” //表示打开后的虚拟机的网卡的物理地址是新生成的&#xff0c;建议不懂的就选这个。 “取消” …

埋坑一: vue中子组件调用兄弟组件方法

小计&#xff1a; 开发中遇到子组件需要调用兄弟组件中的方法&#xff0c;如下写个小demo记录下心得&#xff0c;如果你有好的方法&#xff0c;请到评论区域指教 父组件示例代码&#xff1a; 组件功能解析&#xff1a; 通过$emit获取子组件事件&#xff0c;通过$ref调用子组件中…

nioqrc oracle,程序停在 readnocancel () from -lib-tls-libpthread.so.0

程序停在 readnocancel () from -lib-tls-libpthread.so.0(2012-04-10 23:20:56)标签&#xff1a;程序杂谈程序停在 __read_nocancel () from /lib/tls/libpthread.so.0我在 IBMBladeCenter JS21机器 (计算机集群)上 利用 MPI C 编程&#xff0c; 但出现了一些奇怪的现象。那就…

synchronized 方法 导致插入数据插不进_synchronized 原理知多少

本文转载于SegmentFault社区作者&#xff1a;ytaosynchronized是 Java 编程中的一个重要的关键字&#xff0c;也是多线程编程中不可或缺的一员。本文就对它的使用和锁的一些重要概念进行分析。使用及原理synchronized 是一个重量级锁&#xff0c;它主要实现同步操作&#xff0c…

SpringMVC源码解析(四)——请求处理

2019独角兽企业重金招聘Python工程师标准>>> 前言 这一篇&#xff0c;将着手介绍一次请求的处理。用到了 HandlerMapping、HandlerAdapter 知识&#xff0c;如果遇到不是太了解&#xff0c;可以回顾下。 源码分析 其实 DispatcherServlet 也只是 Servlet 的一个实现…

oracle中where中使用函数,Oracle 尽量避免在 SQL语句的WHERE子句中使用函数

-- Start在 WHERE 子句中应该尽量避免在列上使用函数&#xff0c;因为这样做会使该列上的索引失效&#xff0c;影响SQL 语句的性能。即使该列上没有索引&#xff0c;也应该避免在列上使用函数。考虑下面的情况&#xff1a;CREATE TABLE EMPLOYEE(NAME VARCHAR2(20) NOT NULL,--…

求近似数最值_干货|初中数学《数的开方》知识点梳理

本章内容课标的要求● 1.了解平方根、算术平方根、立方根的概念&#xff0c;会用根号表示数的平方根、算术平方根、立方根。● 2.了解乘方与开方互为逆运算&#xff0c;会用平方运算求百以内整数的平方根&#xff0c;会用立方运算会求百以内整数(对应的负整数)的立方根&#xf…

第三章(续)

目录 第二章 灰度变换与空间滤波(续)直方图处理与函数绘图生成直方图直方图均衡直方图匹配空间滤波线性空间滤波非线性空间滤波图像处理工具箱的标准滤波器线性空间滤波器非线性空间滤波器第二章 灰度变换与空间滤波(续) 直方图处理与函数绘图 生成直方图 应用函数 imhist 语法…

Linux Mysql 安装方法

1、检查是否有安装 [rootJDDB mysql]# yum list installed | grep mysql mysql-community-client.x86_64 5.6.39-2.el7 mysql56-community mysql-community-common.x86_64 5.6.39-2.el7 mysql56-community mysql-community…

oracle 经纬度算距离,根据经纬度诀别用java和Oracle存储过程计算两点距离

根据经纬度分别用java和Oracle存储过程计算两点距离create or replace procedure SP_GET_DISTANCE(cx in number,cy in number,sx in number, sy in number,distance out varchar2)isd number;x number;y number;r number;pi number;begin--开始计算r:6371229;--地球半径pi:3.1…

Kafka集群安装--测试--关闭

一、前提 1、kafka安装包下载&#xff1a;http://kafka.apache.org/downloads 2、jdk已安装 3、scala已安装 4、zookeeper集群已安装并运行二、步骤 1、对kafka_2.9.2-0.8.1.tgz进行解压缩&#xff1a;tar -zxvf kafka_2.9.2-0.8.1.tgz。2、对kafka目录进行改名&#xff1a;mv …

Java中的工厂模式

设计模式遵循原则 开闭原则&#xff1a;对扩展开放&#xff0c;对修改关闭里氏代换原则&#xff1a;只有当衍生类可以替换掉基类&#xff0c;软件单位的功能不受到影响时&#xff0c;基类才能真正被覆用。而衍生类也能够在基类的基础上增加新的行为依赖倒转原则&#xff1a;开闭…

python的底层实现_Python底层封装实现方法详解

这篇文章主要介绍了Python底层封装实现方法详解,文中通过示例代码介绍的非常详细&#xff0c;对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下事实上&#xff0c;python封装特性的实现纯属“投机取巧”&#xff0c;之所以类对象无法直接调用私有方法和属性&a…

php 附近的距离,PHP查询附近的人及其距离的实现方法_PHP

本文实例讲述了PHP查询附近的人及其距离的实现方法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;array(lat>$lat $dlat,lng>$lng-$dlng),right-top>array(lat>$lat $dlat, lng>$lng $dlng),left-bottom>array(lat>$lat - $dlat, lng>$ln…

统计指定目录下的视频时长

package time;import java.io.File;import org.apache.log4j.Logger;import it.sauronsoftware.jave.Encoder; import it.sauronsoftware.jave.EncoderException; import it.sauronsoftware.jave.MultimediaInfo;public class Test2 {/* 支持的后缀 */private static final Str…

怎么在cmd中运行python脚本_cmd中运行python脚本智能使用流程

(此时的ScaleMode自动变Vbuser)更有趣的是用来计算字串高、宽的TextHeight/TextWidth也变成以座标0-100的方式来表现了On Error Resume NextSet outstreemWscript.stdoutIf (LCase(Right(Wscript.fullname,11))"Wscript.exe") ThenSet objShellWscript.CreateObject(…

世界时钟 软件_Clocker for Mac(世界时钟软件)

Clocker for Mac是一款Mac平台上免费的世界时钟工具&#xff0c;方便我们查看世界各地的时间&#xff0c;它是开源免费的&#xff0c;完全没有广告。包括数百个时区&#xff0c;支持24小时制或AM / PM&#xff0c;macz提供Clocker mac免费版&#xff0c;欢迎前来下载&#xff0…

Mac 设置 NDK

2019独角兽企业重金招聘Python工程师标准>>> 1、首先查看我自己的android studio &#xff0c;找到以下路径 如上图&#xff0c;打开一个 AS 项目&#xff0c;file - project structure 这是我的3 个路径 Ndk /Users/dhbm/Library/Android/sdk/ndk-bundle Sdk /User…

Workbench has not been created yet

原因是&#xff1a;加载的插件变更后需要清理 在启动参数最后加入 -clean

oracle必须声明标识符函数,引用变量时需要必须声明标识符

SQL> declare2 pname emp.ename%type;3 psal emp.sal%type;4 begin5 select enmae,sal into pname,psal from emp where empno7782;6 dbms_output.put_line(pname||xsis||psal);7 end;8 /pname emp.ename%type;*第 2 行出现错误:ORA-06550: 第 2 行, 第 7 列:PLS-002…