Mysql併發控制

最近在看Mysql的併發控制,事務處理等知識,做些整理。

併發控制目的是當多個連接對數據庫進行修改時保證數據的一致性。現在mysql的InnoDB在update,delete時使用行級鎖,對於select會結合MVCC保證一致性。

1、 併發控制
MySQL提供兩個級別的併發控制:服務器級(the server level)和存儲引擎級(the storage engine level)。加鎖是實現併發控制的基本方法,MySQL中鎖的粒度:
(1)    表級鎖:MySQL獨立於存儲引擎提供表鎖,例如,對於ALTER TABLE語句,服務器提供表鎖(table-level lock)。
(2)    行級鎖:InnoDB和Falcon存儲引擎提供行級鎖,此外,BDB支持頁級鎖。InnoDB的併發控制機制,下節詳細討論。
另外,值得一提的是,MySQL的一些存儲引擎(如InnoDB、BDB)除了使用封鎖機制外,還同時結合MVCC機制,即多版本兩階段封鎖協議MVCC(Multiversion two-phrase locking protocal),來實現事務的併發控制,從而使得只讀事務不用等待鎖,提高了事務的併發性。

數據庫的事務處理的原則是保證ACID的正確性。

2、事務處理

2.1 事務的ACID特性
事務是由一組SQL語句組成的邏輯處理單元,事務具有以下4個屬性:
(1)原子性(Atomicity):事務是一個原子操作單元,其對數據的修改,要麼全都執行,要麼全都不執行。
(2)一致性(Consistent):在事務開始和完成時,數據都必須保持一致狀態。這意味着所有相關的數據規則都必須應用於事務的修改,以保持數據的完整性;事務結束時,所有的內部數據結構(如B樹索引或雙向鏈表)也都必須是正確的。
(3)隔離性(Isolation):數據庫系統提供一定的隔離機制,保證事務在不受外部併發操作影響的“獨立”環境執行。這意味着事務處理過程中的中間狀態對外部是不可見的,反之亦然。
(4)持久性(Durable):事務完成之後,它對於數據的修改是永久性的,即使出現系統故障也能夠保持。

2.2、事務處理帶來的相關問題
由於事務的併發執行,帶來以下一些著名的問題:
(1)更新丟失(Lost Update):當兩個或多個事務選擇同一行,然後基於最初選定的值更新該行時,由於每個事務都不知道其他事務的存在,就會發生丟失更新問題--最後的更新覆蓋了由其他事務所做的更新。
(2)髒讀(Dirty Reads):一個事務正在對一條記錄做修改,在這個事務完成並提交前,這條記錄的數據就處於不一致狀態;這時,另一個事務也來讀取同一條記錄,如果不加控制,第二個事務讀取了這些“髒”數據,並據此做進一步的處理,就會產生未提交的數據依賴關係。這種現象被形象地叫做"髒讀"。
(3)不可重複讀(Non-Repeatable Reads):一個事務在讀取某些數據後的某個時間,再次讀取以前讀過的數據,卻發現其讀出的數據已經發生了改變、或某些記錄已經被刪除了!這種現象就叫做“不可重複讀”。
(4)幻讀(Phantom Reads):一個事務按相同的查詢條件重新讀取以前檢索過的數據,卻發現其他事務插入了滿足其查詢條件的新數據,這種現象就稱爲“幻讀”。


3. Mysql隔離級別:

READ UNCOMMITTED :事務可以看到其他事務沒有被提交的數據(髒數據)
READ COMMITTED :事務可以看到其他事務已經提交的數據
REPEATABLE READ :事務中兩次查詢的結果相同。(Innodb默認級別)
SERIALIZABLE :所有事務順序執行,對所有read操作加鎖。保證一致性。


4. 注意點:

(1)InnoDb通過MVCC防止了幻讀(Phantom Read),即兩次select之間有另一個事務插入了數據並且commit,兩次select也是保證一致性的。但是如果事務B的insert/update/delete語句執行時間和commit時間在本事務A的第一個select之前,則事務A的第一個select是可以讀到事務B的更新數據的。


(2)如果一個事務A,先進行了update/insert/delete操作,在進行第一次select操作,那麼select是可以讀到更新的時間,這樣造成的結果是select可能讀到根本不存在的數據(http://dev.mysql.com/doc/refman/5.1/en/innodb-consistent-read.html


(3)如果一個事務A進行了select操作,那麼在這個select開始後的其他事務B進行了對數據的更新操作(update/insert/delete),事務A的select將不會看到事務B操作的結果。事務Aselect在遍歷過程中會遇到兩種情況,一種是當前遍歷的行時正在被更新(update/insert/delete),那麼這一行會被加上write lock鎖。另一種是,當前遍歷的行沒有被加鎖。


對於第一種請,Innodb處理方式是select過程中出逐行遍歷數據,如果某一行被write lock了,那麼會去undo段裏面找這一行的前一個snapshot數據,這個snapshot數據是由write操作引起的,每次write操作都將保存當前事務時刻的原始數據,用於write失敗進行回滾,所以是放在undo數據段裏面。


對於第二種情況:可以通過事務的編號進行過濾,innodb的每個事務都有個事務編號保存在每一行數據的隱藏字段裏,如果當前事務編號比數據記錄裏面的隱藏字段的事務編號小,那麼表示當前數據是被當前事務後面的事務更新的,那麼不取當前數據,而是去undo段裏取snapshot數據。

(Mysql文檔的原文是:Suppose that you are running in the default REPEATABLE READ isolation level. When you issue a consistent read (that is, an ordinary SELECT statement), InnoDB gives your transaction a timepoint according to which your query sees the database. If another transaction deletes a row and commits after your timepoint was assigned, you do not see the row as having been deleted. Inserts and updates are treated similarly.)

發佈了81 篇原創文章 · 獲贊 22 · 訪問量 36萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章