postgresql 插入 时间戳_数据也玩躲猫猫?PostgreSQL中别人提交的数据,我为什么看不到?...
原創(chuàng): Aken DB印象
文章鏈接:https://mp.weixin.qq.com/s/OkJaWbzcXcJtzSCOFnqeXQ
文章作為DB的學(xué)習(xí)體會(huì),若有錯(cuò)誤歡迎指導(dǎo)。
一、環(huán)境介紹
操作系統(tǒng):CentOS Linux release 7.6.1810 (Core) DB版本:PostgreSQL -11.5 on x86_64-pc-linux-gnu
二、問(wèn)題描述
同一個(gè)實(shí)例運(yùn)行的3個(gè)session,在T2時(shí)刻session 1向表table01插入一行數(shù)據(jù)之后,session 2和session 3兩個(gè)會(huì)話執(zhí)行相同的SQL查詢的結(jié)果不一樣。如下:
上圖中,session 2查到的是2行記錄,session 3卻只有1條記錄。為什么session 2能看到session 1新插入的記錄,而session 3卻看不到呢?這種情況是在什么場(chǎng)景下發(fā)生的呢?
三、相關(guān)理論知識(shí)回顧
如果有熟悉事務(wù)隔離級(jí)別的朋友可能已經(jīng)想到大概的原因。關(guān)于事務(wù)的隔離級(jí)別的介紹,有興趣的可以查看上一篇文章。
PostgreSQL的事務(wù)隔離級(jí)別介紹及更改
在說(shuō)明原因之前,這里先介紹一下PostgreSQL中取名為“transaction snapshot”這個(gè)東西,即事務(wù)快照。
至于什么是事務(wù)快照,以及為什么需要事務(wù)快照,我在官方文檔中暫時(shí)沒(méi)有看到具體的描述。
下面是個(gè)人的理解,不代表官方:
平時(shí)我們執(zhí)行SQL數(shù)據(jù)讀取的時(shí)候,實(shí)際上讀取的是一種狀態(tài)數(shù)據(jù),transaction snapshot本義上指是某個(gè)時(shí)刻事務(wù)的快照,實(shí)質(zhì)代表的是具體時(shí)刻具體事務(wù)下數(shù)據(jù)的狀態(tài)。
既然是狀態(tài),那么可能就有當(dāng)前狀態(tài)、上一個(gè)狀態(tài)、下一個(gè)狀態(tài)一說(shuō)。數(shù)據(jù)庫(kù)中所說(shuō)的事務(wù)可看作是將數(shù)據(jù)從上一個(gè)狀態(tài)進(jìn)入到另一個(gè)狀態(tài)的單位。
這是數(shù)據(jù)庫(kù)中的“詞典”,理解起來(lái)比較干澀,我們可以對(duì)應(yīng)到人類(lèi)詞典中比較容易理解的三個(gè)階段:過(guò)去的、當(dāng)前的、未來(lái)的。
所以,我對(duì)事務(wù)快照的理解為三個(gè)階段:一個(gè)transaction snapshot將事務(wù)劃分為過(guò)去的、當(dāng)前的、未來(lái)的三個(gè)區(qū)域。
比較友好的是,PostgreSQL官方給我們提供了一個(gè)獲取事務(wù)快照的函數(shù):txid_current_snapshot。下面是官網(wǎng)對(duì)txid_current_snapshot函數(shù)輸出結(jié)果的原文解析:
Table 9.75. Snapshot Components for PostgreSQL-12
詳細(xì)介紹見(jiàn):https://www.postgresql.org/docs/current/functions-info.html
- xmin,當(dāng)前處于active狀態(tài)的最小事務(wù)編號(hào);
- xmax,未來(lái)產(chǎn)生的事務(wù)中,第一個(gè)將被分配的事務(wù)編號(hào);
- xip_list,當(dāng)前處于active 狀態(tài)的事務(wù)列表(包括in progress和future狀態(tài)的事務(wù)),其余為inactive。
如下,查看當(dāng)前時(shí)刻事務(wù)快照:
(postgres@[local]:5432)[akendb01]#select txid_current_snapshot();txid_current_snapshot-----------------------639:642:639,641 <<- 1.xmin=639,表示當(dāng)前時(shí)刻快照中最小的是639這個(gè)事務(wù)。小于該編號(hào)的事務(wù)都已經(jīng)終止(提交、回滾或異常終止),這些事務(wù)屬于“過(guò)去的”范圍區(qū)域。
- 2.xmax=642,表示將來(lái)新事務(wù)產(chǎn)生時(shí)分配到的第一個(gè)事務(wù)編號(hào)txid,大于等于642的事務(wù)未產(chǎn)生,屬于“將來(lái)的”范圍區(qū)域。
- 3.xip_list=(639,641),表示該快照時(shí)刻639和641這兩個(gè)事務(wù)正處于active狀態(tài),屬于“當(dāng)前的”范圍區(qū)域。
畫(huà)成圖就是下面這個(gè)樣子:
transaction snapshot examples
四、原因分析
在PostgreSQL中,提交讀(或者叫讀提交)read committed事務(wù)隔離級(jí)別下,session中同一事務(wù)的每條SQL執(zhí)行的時(shí)候都會(huì)自動(dòng)去讀取當(dāng)前時(shí)刻的事務(wù)快照;而在repeatable read級(jí)別下,session中同一事務(wù)只會(huì)在事務(wù)開(kāi)始的第一個(gè)SQL獲取一次事務(wù)快照。
因?yàn)閞ead committed級(jí)別下,同一事務(wù)中不同時(shí)刻的SQL獲取的快照可能不一樣,因此讀到的數(shù)據(jù)可能會(huì)不一樣。
而repeatable read在整個(gè)事務(wù)周期只獲取一次事務(wù)快照,所以同一事務(wù)內(nèi)所有SQL使用的快照都是一致的,因此可以實(shí)現(xiàn)重復(fù)讀,規(guī)避了幻讀的產(chǎn)生。
pg默認(rèn)的事務(wù)隔離級(jí)別transaction isolation為read committed。這是上面文章開(kāi)頭session 2中read committed事務(wù)級(jí)別下產(chǎn)生幻讀的原因,也是session 3中repeatable read可以實(shí)現(xiàn)重復(fù)讀的原因。
請(qǐng)?jiān)徫以谖恼麻_(kāi)頭故意將會(huì)話的事務(wù)隔離級(jí)別忽略,目的是為了引導(dǎo)大家可以一起思考。
說(shuō)到這里,MySQL的朋友可能覺(jué)得PostgreSQL中transaction snapshot和MySQL中的一致性視圖Read view有點(diǎn)像。
所以,對(duì)于文章開(kāi)頭的問(wèn)題:
- 1.對(duì)于session 2和session 3的結(jié)果來(lái)說(shuō),上述的問(wèn)題并非因?yàn)閿?shù)據(jù)的不一致,而是因?yàn)椴煌氖聞?wù)隔離級(jí)別讀取的結(jié)果有所區(qū)別。
- 2.對(duì)于session 2來(lái)說(shuō),在同一個(gè)事務(wù)里面執(zhí)行相同的查詢語(yǔ)句前后得到的結(jié)果不一致,這種情況叫幻讀。
什么是幻讀? 下面是官方的原文解析:
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.
大概意思指:
在一個(gè)事務(wù)中相同的SQL查詢條件前后讀取到的結(jié)果不一致,原因是后者讀取到了其他事務(wù)中新提交的數(shù)據(jù)。
這個(gè)問(wèn)題其實(shí)在PostgreSQL-12官方文檔中有所提示,pg中repeatable read隔離級(jí)別下是不會(huì)出現(xiàn)幻讀的。如下圖標(biāo)紅處所示:
PostgreSQL-12事務(wù)隔離級(jí)別
為什么在PostgreSQL中的repeatable read下是Allowed,but not in PG呢?
這正是因?yàn)槭聞?wù)快照的作用。下面將文章開(kāi)始時(shí)的例子進(jìn)行充分的演示。
五、場(chǎng)景演示:提交讀、可重復(fù)讀事務(wù)快照對(duì)比
下面針對(duì)read committed和repeatable read兩種事務(wù)隔離模式下的事務(wù)快照進(jìn)行對(duì)比測(cè)試,例子如下:
1.T0時(shí)間段:
session 1在默認(rèn)情況下開(kāi)啟事務(wù),txid=666。
session 2在read committed隔離模式下開(kāi)啟事務(wù),txid=674;
session 3在可重復(fù)讀repeatable read隔離模式下開(kāi)啟事務(wù),txid=675;
session 4開(kāi)啟事務(wù)txid=676(略)。
1)事務(wù)開(kāi)始前table01中只有一行記錄:tuple 1
(postgres@[local]:5432)[akendb01]#select * from table01; id | name----+-------- 1 | aken01(1 row)(postgres@[local]:5432)[akendb01]#2)session 1在默認(rèn)提交讀模式下開(kāi)啟事務(wù),事務(wù)編號(hào)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:在提交讀隔離級(jí)別下開(kāi)啟事務(wù),事務(wù)編號(hào)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:在可重復(fù)讀隔離級(jí)別下開(kāi)啟事務(wù),事務(wù)編號(hào)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:分配一個(gè)事務(wù)txid=676
(postgres@[local]:5432)[akendb01]#select txid_current(); txid_current-------------- 676(1 row)2.T1時(shí)刻,session 1、2、3獲取當(dāng)前事務(wù)快照,并讀取table01的記錄。
1)session 1:讀取到的事務(wù)快照為'666:676:674,675',讀取表的記錄數(shù)為1行。
(postgres@[local]:5432)[akendb01]#select txid_current_snapshot(); txid_current_snapshot-----------------------666:676:674,675 <<< 實(shí)際上txid=676在session 4已經(jīng)分配,這個(gè)和官網(wǎng)將xmax解析為將來(lái)產(chǎn)生的第一個(gè)事務(wù)有矛盾,pg獲取事務(wù)快照時(shí)最后一個(gè)txid是否會(huì)滯后?(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:讀取到的事務(wù)快照為'666:676:666,675',讀取表的記錄數(shù)為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:讀取到的事務(wù)快照為'666:676:666,674',讀取表的記錄數(shù)為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時(shí)刻,session 1往table01插入一行記錄并commit提交,session 1、2、3讀取table01的記錄。
1)session 1在事務(wù)txid=666中獲取的事務(wù)快照為'674:676:674,675',查看結(jié)果中可以看到自己新插入的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 <<< 事務(wù)666已提交,session 1事務(wù)快照改變,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在事務(wù)txid=674中獲取到的快照為'674:676:675'和T1時(shí)刻不同,能看到事務(wù)txid=666新插入的tuple 2,產(chǎn)生幻讀。
(postgres@[local]:5432)[akendb01]#select txid_current_snapshot();txid_current_snapshot-----------------------674:676:675 <<< session 1的事務(wù)6663)session 3:
session 3在事務(wù)txid=675中獲取的事務(wù)快照依舊為'666:676:666,674',和T1時(shí)刻的保持一致,看不到事務(wù)txid=666新插入的tuple 2,無(wú)幻讀產(chǎn)生。
(postgres@[local]:5432)[akendb01]#select txid_current_snapshot();txid_current_snapshot-----------------------666:676:666,674 <<4.T3時(shí)間段
session 2、session 3事務(wù)結(jié)束,session 1、2、3讀取到的事務(wù)快照都為“676:676:”,且查詢結(jié)果相同。
(postgres@[local]:5432)[akendb01]#select txid_current_snapshot();txid_current_snapshot-----------------------676:676: <<總結(jié)
以上是生活随笔為你收集整理的postgresql 插入 时间戳_数据也玩躲猫猫?PostgreSQL中别人提交的数据,我为什么看不到?...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: oracle 查询时间点数据_oracl
- 下一篇: mysql连接idea详细教程_idea