
資料庫鎖知識
不少人在開發的時候,應該很少會注意到這些鎖的問題,也很少會給程式加鎖(除了庫存這些對數量準確性要求極高的情況下),即使我們不會這些鎖知識,我們的程式在一般情況下還是可以跑得好好的,因為這些鎖資料庫隱式幫我們加了,只會在某些特定的場景下才需要手動加鎖,
對于UPDATE、DELETE、INSERT陳述句,InnoDB會自動給涉及資料集加排他鎖(X) MyISAM在執行查詢陳述句SELECT前,會自動給涉及的所有表加讀鎖,在執行增、刪、改操作前,會自動給涉及的表加寫鎖,這個程序并不需要用戶干預
表鎖
首先,從鎖的粒度,我們可以分成兩大類:
表鎖 :開銷小,加鎖快;不會出現死鎖;鎖定力度大,發生鎖沖突概率高,并發度最低
行鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度小,發生鎖沖突的概率低,并發度高 不同的存盤引擎支持的鎖粒度是不一樣的==:InnoDB行鎖和表鎖都支持、MyISAM只支持表鎖!InnoDB只有通過索引條件檢索資料才使用行級鎖==,否則,InnoDB使用表鎖也就是說,InnoDB的行鎖是基于索引的!
表鎖下又分為兩種模式: 表讀鎖(Table Read Lock)&& 表寫鎖(Table Write Lock)
從下圖可以清晰看到,在表讀鎖和表寫鎖的環境下:讀讀不阻塞,讀寫阻塞,寫寫阻塞!讀讀不阻塞:當前用戶在讀資料,其他的用戶也在讀資料,不會加鎖 讀寫阻塞:當前用戶在讀資料,其他的用戶不能修改當前用戶讀的資料,會加鎖!寫寫阻塞:當前用戶在修改資料,其他的用戶不能修改當前用戶正在修改的資料,會加鎖!

從上面已經看到了:讀鎖和寫鎖是互斥的,讀寫操作是串行,
-
如果某個行程想要獲取讀鎖,同時另外一個行程想要獲取寫鎖,在mysql中,寫鎖是優先于讀鎖的!
-
寫鎖和讀鎖優先級的問題是可以通過引數調節的:max_write_lock_count和low-priority-updates
注:

行鎖
InnoDB和MyISAM有兩個本質的區別:InnoDB支持行鎖、InnoDB支持事務
InnoDB實作了以下兩種型別的行鎖:
-
共享鎖(S鎖、讀鎖):允許一個事務去讀一行,阻止其他事務獲得相同資料集的排他鎖,即多個客戶可以同時讀取同一個資源,但不允許其他客戶修改,
-
排他鎖(X鎖、寫鎖):允許獲得排他鎖的事務更新資料,阻止其他事務取得相同資料集的讀鎖和寫鎖,寫鎖是排他的,寫鎖會阻塞其他的寫鎖和讀鎖,
另外,為了允許行鎖和表鎖共存,實作多粒度鎖機制,InnoDB還有兩種內部使用的意向鎖(Intention Locks),這兩種意向鎖都是表鎖:
-
意向共享鎖(IS):事務打算給資料行加行共享鎖,事務在給一個資料行加共享鎖前必須先取得該表的IS鎖,
-
意向排他鎖(IX):事務打算給資料行加行排他鎖,事務在給一個資料行加排他鎖前必須先取得該表的IX鎖,
-
意向鎖也是資料庫隱式幫我們做了,不需要程式員關心!
MVCC
MVCC(Multi-Version ConcurrencyControl)多版本并發控制,可以簡單地認為:MVCC就是行級鎖的一個變種(升級版),在表鎖中我們讀寫是阻塞的,基于提升并發性能的考慮,MVCC一般讀寫是不阻塞的(很多情況下避免了加鎖的操作),
可以簡單的理解為:對資料庫的任何修改的提交都不會直接覆寫之前的資料,而是產生一個新的版本與老版本共存,使得讀取時可以完全不加鎖,
事務的隔離級別
事務的隔離級別就是通過鎖的機制來實作,鎖的應用最終導致不同事務的隔離級別,只不過隱藏了加鎖細節,事務的隔離級別有4種:
-
Read uncommitted:會出現臟讀,不可重復讀,幻讀
-
Read committed:會出現不可重復讀,幻讀
-
Repeatable read:會出現幻讀(Mysql默認的隔離級別,但是Repeatable read配合gap鎖不會出現幻讀!)
-
Serializable:串行,避免以上的情況
Read uncommitted:出現的現象--->臟讀:一個事務讀取到另外一個事務未提交的資料,例子:A向B轉賬,A執行了轉賬陳述句,但A還沒有提交事務,B讀取資料,發現自己賬戶錢變多了!B跟A說,我已經收到錢了,A回滾事務【rollback】,等B再查看賬戶的錢時,發現錢并沒有多...
Read committed:出現的現象--->不可重復讀:一個事務讀取到另外一個事務已經提交的資料,也就是說一個事務可以看到其他事務所做的修改,例如:A查詢資料庫得到資料,B去修改資料庫的資料,導致A多次查詢資料庫的結果都不一樣【危害:A每次查詢的結果都是受B的影響的,那么A查詢出來的資訊就沒有意思了】
Repeatable read:避免不可重復讀是事務級別的快照!每次讀取的都是當前事務的版本,即使被修改了,也只會讀取當前事務版本的資料

至于虛讀(幻讀):是指在一個事務內讀取到了別的事務插入的資料,導致前后讀取不一致,和不可重復讀類似,但虛讀(幻讀)會讀到其他事務的插入的資料,導致前后讀取不 一致,幻讀的重點在于新增或者洗掉(資料條數變化),不可重復讀的重點是修改,幻讀和不可重復的區別?
樂觀鎖和悲觀鎖
無論是Read committed還是Repeatable read隔離級別,都是為了解決讀寫沖突的問題,現在考慮一個問題:有一張資料庫表USER,只有id、name欄位,現在有2個請求同時操作表A,程序如下:(模擬更新丟失,雖然不是很恰當)
1. 操作1查詢出name="zhangsan"
2. 操作2也查詢出name="zhangsan"
3. 操作1把name欄位資料修改成lisi并提交
4. 操作2把name欄位資料修改為wangwu并提交
那么操作1的更新丟失啦,即一個事務的更新覆寫了其它事務的更新結果,解決上述更新丟失的方式有如下3種:
-
使用Serializable隔離級別,事務是串行執行的!
-
樂觀鎖
-
悲觀鎖
悲觀鎖
我們使用悲觀鎖的話其實很簡單(手動加行鎖就行了):select * from xxxx for update,在select 陳述句后邊加了for update相當于加了排它鎖(寫鎖),加了寫鎖以后,其他事務就不能對它修改了!需要等待當前事務修改完之后才可以修改.也就是說,如果操作1使用select ... for update,操作2就無法對該條記錄修改了,即可避免更新丟失,
樂觀鎖
樂觀鎖不是資料庫層面上的鎖,需要用戶手動去加的鎖,一般我們在資料庫表中添加一個版本欄位version來實作,例如操作1和操作2在更新User表的時,執行陳述句如下:
update A set Name=lisi,version=version+1 where ID=#{id} and version=#{version},
此時即可避免更新丟失,
間隙鎖GAP
當我們用范圍條件檢索資料而不是相等條件檢索資料,并請求共享或排他鎖時,InnoDB會給符合范圍條件的已有資料記錄的索引項加鎖;對于鍵值在條件范圍內但并不存在 的記錄,叫做“間隙(GAP)”,InnoDB也會對這個“間隙”加鎖,這種鎖機制就是所謂的間隙鎖,例子:假如emp表中只有101條記錄,其empid的值分別是1,2,...,100,101
Select * from emp where empid > 100 for update;
上面是一個范圍查詢,InnoDB不僅會對符合條件的empid值為101的記錄加鎖,也會對empid大于101(這些記錄并不存在)的“間隙”加鎖
InnoDB使用間隙鎖的目的有2個:
-
為了防止幻讀(上面也說了,Repeatable read隔離級別下再通過GAP鎖即可避免了幻讀)
-
滿足恢復和復制的需要:MySQL的恢復機制要求在一個事務未提交前,其他并發事務不能插入滿足其鎖定條件的任何記錄,也就是不允許出現幻讀
死鎖
并發的問題就少不了死鎖,在MySQL中同樣會存在死鎖的問題
鎖總結
表鎖其實我們程式員是很少關心它的:
-
在MyISAM存盤引擎中,當執行SQL陳述句的時候是自動加的,
-
在InnoDB存盤引擎中,如果沒有使用索引,表鎖也是自動加的,
現在我們大多數使用MySQL都是使用InnoDB,InnoDB支持行鎖:
-
共享鎖--讀鎖--S鎖
-
排它鎖--寫鎖--X鎖
在默認的情況下,select是不加任何行鎖的~事務可以通過以下陳述句顯示給記錄集加共享鎖或排他鎖,
-
共享鎖(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE
-
排他鎖(X):SELECT * FROM table_name WHERE ... FOR UPDATE
InnoDB基于行鎖還實作了MVCC多版本并發控制,MVCC在隔離級別下的Read committed和Repeatable read下作業,MVCC實作了讀寫不阻塞
以上是文章全部內容,有需要學習與經驗交流的友人請加入Swoole交流群學習與交流的咱們一起學習,有問題一起交流,一起進步!前提是你是學技術的,感謝閱讀!
點此加入該群
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/107481.html
標籤:PHP
上一篇:Excel匯入資料庫(php版)
