數據庫操作遇到的問題
- 臟讀:一個事務讀取了另一個未提交事務寫入的數據;
- 不可重復讀:一個事務重新讀取前面讀取過的數據,發現該數據已經被另一個已經提交的事務修改;
- 幻讀:一個事務重新執行一個查詢,返回符合查詢條件的行的集合,發現滿足查詢條件的行的集合因為其它最近提交的事務而發生了改變。
事務特性
- 原子性(Atomicity):事務作為一個整體被執行,包含在其中的對數據庫的操作要么全部被執行,要么都不執行;
- 一致性(Consistency):事務應確保數據庫的狀態從一個一致狀態轉變為另一個一致狀態。一致狀態的含義是數據庫中的數據應滿足完整性約束;
- 隔離性(Isolation):多個事務并發執行時,一個事務的執行不應影響其他事務的執行;
- 持久性(Durability):一個事務一旦提交,他對數據庫的修改應該永久保存在數據庫中。
數據庫事務隔離級別
- 讀未提交(READ UNCOMMITTED)
- 讀已提交(READ COMMITTED)
- 可重復讀(REPEATABLE READS)
- 可序列化(SERIALIZABLE)
數據庫事務隔離級別&&解決問題
參考:http://www.postgres.cn/docs/9.5/transaction-iso.html
樂觀鎖與悲觀鎖
參考:http://www.hollischuang.com/archives/934
MVCC(multiversion concurrency control,多版本并發控制)
MVCC(多版本并發控制)與使用鎖的優缺點:
在MVCC里,對檢索(讀)數據的鎖請求與寫數據的鎖請求不沖突,所以讀不會阻塞寫,而寫也從不阻塞讀。甚至當通過創新的序列化快照隔離(SSI)級別提供事務隔離的嚴格等級時,PostgreSQL維持這樣的保證。
在PostgreSQL里也有表和行級別的鎖定機制,用于給那些無法輕松接受MVCC行為的應用。 不過,恰當地使用MVCC總會提供比鎖更好的性能。另外,由應用定義的咨詢鎖提供了一個獲得不依賴于單獨事務的鎖的機制。
問題:PG什么時候選擇MVCC什么時候選擇使用鎖?
在PostgreSQL里也有表和行級別的鎖功能,用于那些通常不需要完整事務隔離并且想要顯式管理特定沖突點的應用。不過,恰當地使用MVCC通常會提供比鎖更好的性能。MVCC的兩種實現方法
寫新數據時,把舊數據移到一個單獨的地方,如回滾段中,其他人讀數據時,從回滾段中把舊的數據讀出來;
2.寫數據時,舊數據不刪除,而是把新數據插入。PostgreSQL數據庫使用第二種方法,而Oracle數據庫和MySQL中的innodb引擎使用的是第一種方法;-
與oracle數據庫和MySQL中的innodb引擎相比較,PostgreSQL的MVCC實現方式的優缺點如下:
- 優點:
- 事務回滾可以立即完成,無論事務進行了多少操作;
- 數據可以進行很多更新,不必像Oracle和MySQL的Innodb引擎那樣需要經常保證回滾段不會被用完,也不會像oracle數據庫那樣經常遇到“ORA-1555”錯誤的困擾;
- 缺點:
- 舊版本數據需要清理。PostgreSQL清理舊版本的命令成為Vacuum;
- 舊版本的數據會導致查詢更慢一些,因為舊版本的數據存在于數據文件中,查詢時需要掃描更多的數據塊。
- 優點:
PG中delete和update語句機制
在PostgreSQL中,使用delete和update語句刪除或更新的數據行并沒有被實際刪除,而只是在舊版本數據行的物理地址上將該行的狀態置為已刪除或已過期。因此當數據表中的數據變化極為頻繁時,那么在一段時間之后該表所占用的空間將會變得很大,然而數據量卻可能變化不大。要解決該問題,需要定期對數據變化頻繁的數據表執行VACUUM操作
- 無VACUUM:只是將刪除的數據狀態置為已刪除,該空間不能記錄被重新使用.
- VACUUM:刪除的記錄位于末端,占用的空間會被物理釋放歸還操作系統,如果不是位于末端,會將刪除數據鎖占用的空間置為可用狀態.
- VACUUM FULL:不論被刪除的數據是否處于數據表末端,這些數據鎖占用的空間都將被物理釋放并歸還操作系統.
下面的blog詳細分析了vacuum和vacuum full的區別,以及如何恢復索引和delete后的磁盤空間:
http://www.cnblogs.com/stephen-liu74/archive/2011/12/27/2304155.html
PG的MVCC實現機制(以insert形式展開說明)
在Postgres中,每一個事務都會得到一個被稱作為 XID 的事務ID。這里說的事務不僅僅是被 BEGIN - COMMIT 包裹的一組語句,還包括單條的insert、update或者delete語句。當一個事務開始時,Postgrel遞增XID,然后把它賦給這個事務。Postgres還在系統里的每一行記錄上都存儲了事務相關的信息,這被用來判斷某一行記錄對于當前事務是否可見。
insert的MVCC說明:
- 當插入一行記錄時,PG會把當前事務的XID存儲在這行數據中的xmin;
- 當插入的數據未COMMIT之前,這行數據對其他數據是不可見的;
- 當插入的數據COMMIT后,這行數據對其他數據是可見的;
- 資料說只有xmin<XID條件,才能查看這條新記錄,但測試發現可能存在可重復讀;
- 事務1:
事務ID:x
查詢數據1;
阻塞;
查詢數據2; - 事務2:
事務ID:x+1
插入數據
上面2個示例,如果事務2在事務1阻塞的時候提交,在事務1中的查詢數據2是能查看到這條新數據的,因為PG默認的事務隔離級別是讀已提交.如果更改事務隔離級別為SERIALIZABLE后,則不會發生可重復讀的情況(即查詢1和查詢2兩次的結果一致).
自己測試查看語句:
--獲取行xmin,xmax值
select xmin, xmax,* from tablename;
--獲取當前事務的XID
select txid_current();
--改變事務隔離級別
BEGIN TRANSACTION ISOLATION LEVEl [READ COMMITTED/REPEATABLE READ/SERIALIZABLE]; --一次啟動事務并指定事務隔離級別
對于delete和update來說,機制也是類似的,當對于他們來說PG使用xmax來判斷數據的可見性.
自己測試思路可以參考:
http://www.zlovezl.cn/articles/postgresql-concurrency-with-mvcc/
原理也可參考:
說明:https://my.oschina.net/Kenyon/blog/108850
示例:https://my.oschina.net/Kenyon/blog/63668
PG中的事務隔離
- 兩個事務,更新的同一行,一次只有一個事務能更新.
- 兩個事務,更新不同行,能同時更新.
參考:
PG文檔事務隔離:http://www.postgres.cn/docs/9.5/transaction-iso.html
PG的表鎖
PG文檔表鎖機制:http://www.postgres.cn/docs/9.5/explicit-locking.html#LOCKING-TABLES
通過實例較好的說明了PG的鎖機制:http://www.oschina.net/translate/postgresql-locking-revealed
PG的行鎖
PG文檔行級鎖:http://www.postgres.cn/docs/9.5/explicit-locking.html#LOCKING-ROWS
pg行鎖解讀:http://blog.itpub.net/30088583/viewspace-1699315/
PG事務常用操作
- 開啟事務
BEGIN;
START TRANSACTION [ transaction_mode [, ...] ]
-這里的 transaction_mode是下列之一:
- ISOLATION LEVEL { SERIALIZABLE | REPEATABLE READ | READ COMMITTED | READ UNCOMMITTED }
- READ WRITE | READ ONLY
- [ NOT ] DEFERRABLE
- 設置事務模式
SET TRANSACTION transaction_mode [, ...]
SET TRANSACTION SNAPSHOT snapshot_idSET SESSION CHARACTERISTICS AS TRANSACTION transaction_mode [, ...]
-這里的 transaction_mode是下列之一:
- ISOLATION LEVEL { SERIALIZABLE | REPEATABLE READ | READ COMMITTED | READ UNCOMMITTED }
- READ WRITE | READ ONLY
- [ NOT ] DEFERRABLE
--事務隔離級別,定義多個事務時間的隔離級別
BEGIN TRANSACTION ISOLATION LEVEl [READ COMMITTED/REPEATABLE READ/SERIALIZABLE]; --一次啟動事務并指定事務隔離級別
BEGIN;
TRANSACTION ISOLATION LEVEl [READ COMMITTED/REPEATABLE READ/SERIALIZABLE]; --先啟動事務,再設置事務隔離級別
- 結束事務
commit
rollback
- 預備事務
--預備事務,使得事務分階段可以提交
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未被插入,但是第一個插入成功。
- 查看當前事務的事務id
select txid_current();
詳情參考:https://my.oschina.net/liuyuanyuangogo/blog/415395
其他參考
pg鎖機制理解:http://francs3.blog.163.com/blog/static/40576727201082134343604/
mysql鎖機制理解: http://hedengcheng.com/?p=771
pg中mvcc實現機制:http://www.zlovezl.cn/articles/postgresql-concurrency-with-mvcc/
http://blog.itpub.net/30088583/viewspace-1585695/
較好的闡釋了pg中mvcc的原理:https://my.oschina.net/Kenyon/blog/108850
pg事務級別查看與變更:http://blog.csdn.net/scugxl/article/details/51126433
mysql與pg對比:https://www.sdk.cn/news/4587
pg維護vacuum解析:http://www.cnblogs.com/stephen-liu74/archive/2011/12/27/2304155.html
Ubuntu安裝PG與使用:http://wenzhixin.net.cn/2014/01/12/hello_postgresql