PostgreSQL併發控制(MVCC, 事務,事務隔離級別)

基於PostgreSQL9.4html

9.3中文文檔:http://58.58.27.50:8079/doc/html/9.3.1_zh/mvcc.htmlsql

9.4中文文檔:http://www.postgresql.org/docs/9.4/static/mvcc.html數據庫


  本文描述PostgreSQL數據庫系統在多個會話試圖同時訪問同一數據時的表現。併發控制的目標是爲全部會話提供高效的訪問,同時還要維護嚴格的數據完整性。每一個數據庫應用開發人員都應該熟悉本話題。
架構

PostgreSQL的MVCC與鎖

   PostgreSQL爲開發者提供了豐富的對數據併發訪問進行管理的工具。在內部,PostgreSQL利用多版本併發控制(MVCC)來維護數據的一致性。這就意味着當檢索數據時,每一個事務看到的都只是一小段時間以前的數據快照(一個數據庫版本),而不是數據的當前狀態。這樣,若是對每一個數據庫會話進行事務隔離,就能夠避免一個事務看到其它併發事務的更新而致使不一致的數據。MVCC經過避開傳統數據庫系統鎖定的方法,最大限度地減小鎖競爭以容許合理的多用戶環境中的性能。併發

   使用MVCC與使用鎖定模式相比較的優缺點:mvc

   在MVCC裏,對檢索(讀)數據的鎖請求與寫數據的鎖請求不衝突,因此讀不會阻塞寫,而寫也從不阻塞讀。甚至當經過創新的序列化快照隔離(SSI)級別提供事務隔離的嚴格等級時,PostgreSQL維持這樣的保證。
oracle

  在PostgreSQL裏也有表和行級別的鎖定機制,用於給那些沒法輕鬆接受MVCC行爲的應用。 不過,恰當地使用MVCC總會提供比鎖更好的性能。另外,由應用定義的諮詢鎖提供了一個得到不依賴於單獨事務的鎖的機制。函數

   MVCC的實現方法有兩種:工具

   1.寫新數據時,把舊數據移到一個單獨的地方,如回滾段中,其餘人讀數據時,從回滾段中把舊的數據讀出來;post

   2.寫數據時,舊數據不刪除,而是把新數據插入。

   PostgreSQL數據庫使用第二種方法,而Oracle數據庫和MySQL中的innodb引擎使用的是第一種方法。

   與racle數據庫和MySQL中的innodb引擎相比較,PostgreSQL的MVCC實現方式的優缺點以下。

   優勢:

   1.事務回滾能夠當即完成,不管事務進行了多少操做;

   2.數據能夠進行不少更新,沒必要像Oracle和MySQL的Innodb引擎那樣須要常常保證回滾段不會被用完,也不會像oracle數據庫那樣常常遇到「ORA-1555」錯誤的困擾;

   缺點:

   1.舊版本數據須要清理。PostgreSQL清理舊版本的命令成爲Vacuum;

   2.舊版本的數據會致使查詢更慢一些,由於舊版本的數據存在於數據文件中,查詢時須要掃描更多的數據塊。   

   (本段轉自《PostgreSQL修煉之道》)


1、事務與事務id

  事務是單個邏輯工做單元執行的一系列操做。必須具備:原子性、一致性、隔離性和持久性(ACID)。

   在Postgres中,每個事務都會獲得一個被稱做爲 XID 的事務ID。這裏說的事務不只僅是被BEGIN - COMMIT包裹的一組語句,還包括單條的insert、update或者delete語句。當一個事務開始時,PostgreSQL遞增XID,而後把它賦給這個事務。PostgreSQL還在系統裏的每一行記錄上都存儲了事務相關的信息,這被用來判斷某一行記錄對於當前事務是否可見。

       舉個例子,當你插入一行記錄時,PostgreSQL會把當前事務的XID存儲在這一行中並稱之爲 xmin 。只有那些‘已提交的並且 xmin’比當前事務的XID小的記錄對當前事務纔是可見的。這意味着,你能夠開始一個新事務而後插入一行記錄,直到你提交( COMMIT )以前,你插入的這行記錄對其餘事務永遠都是不可見的。等到提交之後,其餘後建立的新事務就能夠看到這行新記錄了,由於他們知足了 xmin < XID 條件,並且建立哪一行記錄的事務也已經完成。

     對於 DELETE 和 UPDATE 來講,機制也是相似的,但不一樣的是對於它們PostgreSQL使用叫作 xmax 的值來判斷數據的可見性。這幅圖展現了在兩個併發的插入/讀取數據的事務中,MVCC在事務隔離方面是怎麼起做用的。

    在下面的圖中,假設咱們先執行了這個建表語句:

CREATE TABLE numbers (value int);

http://www.zlovezl.cn/static/uploaded/2014/06/MVCC_1.jpg

雖然 xmin 和 xmax 的值在平常使用中都是被隱藏的,可是你能夠直接請求他們,Postgres會高興的把值給你:

SELECT *, xmin, xmax FROM numbers;

獲取當前事務的XID也很簡單:

SELECT txid_current();


2、事務的操做命令

  http://58.58.27.50:8079/doc/html/9.3.1_zh/sql-set-transaction.html

BEGIN;  --開啓事務

--事務隔離級別,定義多個事務時間的隔離級別
BEGIN TRANSACTION ISOLATION LEVEl [READ COMMITTED/REPEATABLE READ/SERIALIZABLE];  --一次啓動事務並指定事務隔離級別
BEGIN;
TRANSACTION ISOLATION LEVEl [READ COMMITTED/REPEATABLE READ/SERIALIZABLE];  --先啓動事務,再設置事務隔離級別


--預備事務,使得事務分階段能夠提交
PREPARE TRANSACTION 'foobar';
......
COMMIT PREPARE TRANSACTION 'foobar';
ROLLBACK PREPARE TRANSACTION 'foobar';


--保存點savepoint,能夠支持事務的部分回滾
insert into lyy values(1,'nn');
savepoint svp1;
insert into lyy values(2,'ff');
rollback to savepoint svp1;
--此時提交的話,第二個insert未被插入,可是第一個插入成功。


END; 或者 COMMIT;  --結束並提交事務
或者 ROLLBACK;  --結束並回滾事務


3、事務隔離級別

  SQL標準定義了四個級別的事務隔離。最嚴格的是串行化,它是經過標準來定義的,也就是說, 保證一組可序列化事務的併發執行以產生一樣順序依次運行它們的同一效果。 其餘三個層次是經過現象術語被定義,致使併發事務之間的相互做用,這不該該發生在每一個級別中。

  各個級別不但願發生的現象是:

  髒讀:一個事務讀取了另外一個並行的未提交事務寫入的數據。(包括insert,update,delete)

  不可重複讀:一個事務A從新讀取前面讀取過的數據,發現該數據已經被另外一個已提交事務B修改(這個事務B的提交是在事務A第一次讀以後發生的)。(同一行,update)

  幻讀:一個事務從新執行一個查詢,返回一套符合查詢條件的行,發現這些行由於其它最近提交的事務而發生了改變。(結果集,insert、delete)

  這四種隔離級別和對應的行爲在表Table1裏描述。

  Table1 標準SQL事務隔離級別

   隔離級別      髒讀     不可重複讀   幻讀

         讀未提交      可能       可能      可能

   讀已提交      不可能       可能      可能

         可重複讀      不可能      不可能    可能

         可串行化      不可能      不可能    不可能

  在PostgreSQL裏,你能夠請求四種可能的事務隔離級別中的任意一種。可是在內部, 實際上只有三種獨立的隔離級別,分別對應讀已提交,可重複讀和可串行化。若是你選擇了讀未提交的級別, 實際上你用的是讀已提交,在重複讀的PostgreSQL執行時,幻讀是不可能的, 因此實際的隔離級別可能比你選擇的更嚴格。這是 SQL 標準容許的:四種隔離級別只定義了哪種現像不能發生可是沒有定義那種現像必定發生。PostgreSQL只提供三種隔離級別的緣由是:這是把標準的隔離級別與多版本併發控制架構映射相關的惟一合理方法。可用的隔離級別的行爲在下面小節裏描述。

   要設置一個事務的隔離級別,使用SET TRANSACTION命令。

   特別注意: 一些PostgreSQL數據類型和函數關於事務行爲有特定的規則。 尤爲是,序列變化(所以列數經過serial聲明)對於全部其餘的事務是當即可見的, 若是事務改變終止,則不進行回退。參見Section 9.16Section 8.1.4


事務隔離級別詳解

讀已提交隔離級別( BEGIN TRANSACTION ISOLATION LEVEl READ COMMITTED; )

   是PostgreSQL裏的缺省隔離級別。當一個事務運行在這個隔離級別時: SELECT查詢(沒有FOR UPDATE/SHARE子句)只能看到查詢開始以前已提交的數據而沒法看到未提交的數據或者在查詢執行期間其它事務已提交的數據 (僅讀當時數據庫快照)。不過,SELECT看得見其自身所在事務中前面更新執行結果,即便它們還沒有提交。(注意:在同一個事務裏兩個相鄰的SELECT命令可能看到不一樣的快照,由於其它事務會在第一個SELECT執行期間提交。

     分析:要是同時有兩個事務修改同一行數據會怎麼樣?這就是事務隔離級別(transaction isolation levels)登場的時候了。Postgres支持兩個基本的模型來讓你控制應該怎麼處理這樣的狀況。默認狀況下使用讀已提交(READ COMMITTED),等待初始的事務完成後再讀取行記錄而後執行語句。若是在等待的過程當中記錄被修改了,它就從頭再來一遍。舉一個例子,當你執行一條帶有WHERE子句的UPDATE時,WHERE子句會在最初的事務被提交後返回命中的記錄結果,若是這時WHERE子句的條件仍然能獲得知足的話,UPDATE纔會被執行。在下面這個例子中,兩個事務同時修改同一行記錄,最初的UPDATE 語句致使第二個事務的WHERE不會返回任何記錄,所以第二個事務根本沒有修改到任何記錄:

可重複讀隔離級別(BEGIN TRANSACTION ISOLATION LEVEl REPEATABLE READ; )

  這個級別和讀已提交級別是不同的。重複讀事務中的查詢看到的只是事務開始時的快照, 而不是該事務內部當前查詢開始時的快照,這樣,同一個事務內部後面的SELECT命令老是看到一樣的數據等,它們沒有看到經過 自身事務開始以後說起的其餘事務作出的改變。

  使用這個級別的應用必須準備好重試事務,由於串行化失敗。


可串行化隔離級別:(BEGIN TRANSACTION ISOLATION LEVEl SERIALIZABLE; )

  可串行化級別提供最嚴格的事務隔離。這個級別爲全部已提交事務模擬串行的事務執行, 就好像事務將被一個接着一個那樣串行(而不是並行)的執行。不過,正如可重複讀隔離級別同樣, 使用這個級別的應用必須準備在串行化失敗的時候從新啓動事務。 事實上,該隔離級別和可重複讀但願的徹底同樣,它只是監視這些條件,以全部事務的可能的序列不一致的(一次一個)的方式執行並行的可序列化事務執行的行爲。 這種監測不引入任何阻止可重複讀出現的行爲,但有一些開銷的監測,檢測條件這可能會致使序列化異常 將觸發序列化失敗

    分析:若是你須要更好的「兩個事務同時修改同一行數據」控制這種行爲,你能夠把事務隔離級別設置爲 可串行化(SERIALIZABLE) 。在這個策略下,上面的場景會直接失敗,由於它遵循這樣的規則:「若是我正在修改的行被其餘事務修改過的話,就再也不嘗試」,同時 Postgres會返回這樣的錯誤信息:因爲併發修改致使沒法進行串行訪問 。捕獲這個錯誤而後重試就是你的應用須要去作的事情了,或者不重試直接放棄也行,若是那樣合理的話。


   特別注意:序列化事務隔離級別還沒有被添加到熱備複製目標中 (正如在Section 25.5中描述的)。 嚴格的隔離級別目前熱備方式上支持可重複讀。 當在主庫上執行全部永久數據庫寫入可序列化事務中將確保全部的措施將最終達成一致, 運行在備庫上的可重複讀事務會看到一個過渡狀態,與主庫上的任何串行執行的可序列化事務不一致。


4、顯式鎖定

       轉至另外一篇博客:http://my.oschina.net/liuyuanyuangogo/blog/499460#OSC_h2_2



參考文章:

MVCC參考:

中文:http://www.zlovezl.cn/articles/postgresql-concurrency-with-mvcc/

英文:https://devcenter.heroku.com/articles/postgresql-concurrency

PostgreSQL事務處理機制:

http://blog.chinaunix.net/uid-20726500-id-4040024.html

相關文章
相關標籤/搜索