鎖是計算機協調多個程序或純執行緒併發訪問某一資源的機制。在資料庫中,除傳統的計算資源(cpu、ram、i/o)的爭用以外,資料也是一種供許多使用者共享的資源。如何保證資料併發訪問的一致性、有效性是所在有資料庫必須解決的乙個問題,鎖衝突也是影響資料庫併發訪問效能的乙個重要因素。從這個角度來說,鎖對資料庫而言顯得尤其重要,也更加複雜。
鎖的分類
一、按操作劃分,可分為dml鎖、ddl鎖
二、按鎖的粒度劃分,可分為表級鎖、行級鎖、頁級鎖(mysql)
1、表級鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖衝突的概率最高,併發度最低。
表級鎖讓多執行緒可以同時從資料表中讀取資料,但是如果另乙個執行緒想要寫資料的話,就必須要先取得排他訪問(預設加排他表鎖);(共享讀鎖(table read lock))
更新資料時,必須要等到更新完成了,其他執行緒才能訪問(讀)這個表。(獨佔寫鎖(table write lock))
2、行級鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的概率最低,併發度也最高。
3、頁面鎖:開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表鎖和行鎖之間,併發度一般。
三、按鎖級別劃分,可分為共享鎖、排他鎖
共享鎖(s):允許乙個事務去讀一行,阻止其他事務獲得相同資料集的排他鎖。
排他鎖(x):允許獲得排他鎖的事務更新資料,阻止其他事務取得相同資料集的共享讀鎖和排他寫鎖。
四、按加鎖方式劃分,可分為自動鎖、顯示鎖
五、按使用方式劃分,可分為樂觀鎖、悲觀鎖
mysql各儲存引擎鎖的使用
myisam和memory儲存引擎採用的是表級鎖(table-level locking);
bdb儲存引擎採用的是頁面鎖(page-level locking),但也支援表級鎖;
innodb儲存引擎既支援行級鎖(row-level locking),也支援表級鎖,但預設情況下是採用行級鎖
為了允許行鎖和表鎖共存,實現多粒度鎖機制,innodb 還有兩種內部使用的意向鎖(intention locks),這兩種意向鎖都是表鎖:
意向共享鎖(is):事務打算給資料行加行共享鎖,事務在給乙個資料行加共享鎖前必須先取得該錶的 is 鎖。
意向排他鎖(ix):事務打算給資料行加行排他鎖,事務在給乙個資料行加排他鎖前必須先取得該錶的 ix 鎖。
innodb加鎖方法:
意向鎖是 innodb 自動加的, 不需使用者干預。
對於 update、 delete 和 insert 語句, innodb
會自動給涉及資料集加排他鎖(x);
對於普通 select 語句,innodb 不會加任何鎖;
事務可以通過以下語句顯式給記錄集加共享鎖或排他鎖:
共享鎖(s):select * from table_name where … lock in share mode。 其他 session 仍然可以查詢記錄,並也可以對該記錄加 share mode 的共享鎖。但是如果當前事務需要對該記錄進行更新操作,則很有可能造成死鎖。
排他鎖(x):select * from table_name where … for update。其他 session 可以查詢該記錄,但是不能對該記錄加共享鎖或排他鎖,而是等待獲得鎖
悲觀併發控制、樂觀併發控制和多版本併發控制,其中悲觀併發控制其實是最常見的併發控制機制,也就是鎖;而樂觀併發控制其實也有另乙個名字:樂觀鎖,樂觀鎖其實並不是一種真實存在的鎖,我們會在文章後面的部分中具體介紹;最後就是多版本併發控制(mvcc)了,與前兩者對立的命名不同,mvcc 可以與前兩者中的任意一種機制結合使用,以提高資料庫的讀效能。
控制不同的事務對同乙份資料的獲取是保證資料庫的一致性的最根本方法,如果我們能夠讓事務在同一時間對同一資源有著獨佔的能力,那麼就可以保證操作同一資源的不同事務不會相互影響。
最簡單的、應用最廣的方法就是使用鎖來解決,當事務需要對資源進行操作時需要先獲得資源對應的鎖,保證其他事務不會訪問該資源後,在對資源進行各種操作;在悲觀併發控制中,資料庫程式對於資料被修改持悲觀的態度,在資料處理的過程中都會被鎖定,以此來解決競爭的問題。
讀寫鎖
為了最大化資料庫事務的併發能力,資料庫中的鎖被設計為兩種模式,分別是共享鎖和互斥鎖。當乙個事務獲得共享鎖之後,它只可以進行讀操作,所以共享鎖也叫讀鎖;而當乙個事務獲得一行資料的互斥鎖時,就可以對該行資料進行讀和寫操作,所以互斥鎖也叫寫鎖。共享鎖和互斥鎖除了限制事務能夠執行的讀寫操作之外,它們之間還有『共享』和『互斥』的關係,也就是多個事務可以同時獲得某一行資料的共享鎖,但是互斥鎖與共享鎖和其他的互斥鎖並不相容,我們可以很自然地理解這麼設計的原因:多個事務同時寫入同一資料難免會發生各種詭異的問題。如果當前事務沒有辦法獲取該行資料對應的鎖時就會陷入等待的狀態,直到其他事務將當前資料對應的鎖釋放才可以獲得鎖並執行相應的操作。
除了悲觀併發控制機制 - 鎖之外,我們其實還有其他的併發控制機制,樂觀併發控制(optimistic concurrency control)。樂觀併發控制也叫樂觀鎖,但是它並不是真正的鎖,很多人都會誤以為樂觀鎖是一種真正的鎖,然而它只是一種併發控制的思想。
基於時間戳的協議
鎖協議按照不同事務對同一資料項請求的時間依次執行,因為後面執行的事務想要獲取的資料已將被前面的事務加鎖,只能等待鎖的釋放,所以基於鎖的協議執行事務的順序與獲得鎖的順序有關。在這裡想要介紹的基於時間戳的協議能夠在事務執行之前先決定事務的執行順序。
每乙個事務都會具有乙個全域性唯一的時間戳,它即可以使用系統的時鐘時間,也可以使用計數器,只要能夠保證所有的時間戳都是唯一並且是隨時間遞增的就可以。
基於時間戳的協議能夠保證事務並行執行的順序與事務按照時間戳序列執行的效果完全相同;每乙個資料項都有兩個時間戳,讀時間戳和寫時間戳,分別代表了當前成功執行對應操作的事務的時間戳。
該協議能夠保證所有衝突的讀寫操作都能按照時間戳的大小序列執行,在執行對應的操作時不需要關注其他的事務只需要關心資料項對應時間戳的值就可以了:無論是讀操作還是寫操作都會從左到右依次比較讀寫時間戳的值,如果小於當前值就會直接被拒絕然後回滾,資料庫系統會給回滾的事務新增乙個新的時間戳並重新執行這個事務。
基於驗證的協議
樂觀併發控制其實本質上就是基於驗證的協議,因為在多數的應用中唯讀的事務佔了絕大多數,事務之間因為寫操作造成衝突的可能非常小,也就是說大多數的事務在不需要併發控制機制也能執行的非常好,也可以保證資料庫的一致性;而併發控制機制其實向整個資料庫系統新增了很多的開銷,我們其實可以通過別的策略降低這部分開銷。
而驗證協議就是我們找到的解決辦法,它根據事務的唯讀或者更新將所有事務的執行分為兩到三個階段:
在讀階段,資料庫會執行事務中的全部讀操作和寫操作,並將所有寫後的值存入臨時變數中,並不會真正更新資料庫中的內容;在這時候會進入下乙個階段,資料庫程式會檢查當前的改動是否合法,也就是是否有其他事務在 raed phase 期間更新了資料,如果通過測試那麼直接就進入 write phase 將所有存在臨時變數中的改動全部寫入資料庫,沒有通過測試的事務會直接被終止。
為了保證樂觀併發控制能夠正常執行,我們需要知道乙個事務不同階段的發生時間,包括事務開始時間、驗證階段的開始時間以及寫階段的結束時間;通過這三個時間戳,我們可以保證任意衝突的事務不會同時寫入資料庫,一旦由乙個事務完成了驗證階段就會立即寫入,其他讀取了相同資料的事務就會回滾重新執行。
作為樂觀的併發控制機制,它會假定所有的事務在最終都會通過驗證階段並且執行成功,而鎖機制和基於時間戳排序的協議是悲觀的,因為它們會在發生衝突時強制事務進行等待或者回滾,哪怕有不需要鎖也能夠保證事務之間不會衝突的可能。
mysql 中實現的多版本兩階段鎖協議(multiversion 2pl)將 mvcc 和 2pl 的優點結合了起來,每乙個版本的資料行都具有乙個唯一的時間戳,當有讀事務請求時,資料庫程式會直接從多個版本的資料項中具有最大時間戳的返回。更新操作就稍微有些複雜了,事務會先讀取最新版本的資料計算出資料更新後的結果,然後建立乙個新版本的資料,新資料的時間戳是目前資料行的最大版本 +1:資料版本的刪除也是根據時間戳來選擇的,mysql 會將版本最低的資料定時從資料庫中清除以保證不會出現大量的遺留內容。
參考文章
**mysql的中事務與鎖
mysql鎖總結
資料庫 併發控制與鎖機制
事務隔離級別 資料庫中的事務是併發操作的,併發操作可以提高系統的工作效率,節省資源。在事務併發操作時,會出現多個事務對某一資源的爭用,多個事務對資源進行不同的操作,若不加以控制,會出現資料不一致的問題。因此,在dbms中需要進行併發控制管理。若不對併發事務進行控制,會出現資料不一致的問題,如髒讀 不...
mysql併發控制的機制
事務的三個問題 1.1 髒讀 事務a檢視資料,事務b修改資料,導致事務a看到了事務b修改的髒資料,這種現象稱為髒讀。1.2 不可重複讀 側重與資料行的修改 事務a檢視資料,事務b修改資料並提交了事務,事務a發現資料被修改了,造成了資料的前後不一致的問題。1.3 幻讀 側重與資料行的增加 事務a發現一...
高併發Mysql樂觀鎖機制
為什麼80 的碼農都做不了架構師?高併發mysql樂觀鎖機使用舉例 以mysql innodb為例,使用version版本號方式 1,假設商品goods表中有乙個欄位status,status為1代表商品未被下單,status為2代表商品已經被下單,那麼我們對某個商品下單時必須確保該商品status...