您好,登錄后才能下訂單哦!
MySQL中的鎖和事務隔離級別是什么?可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。
鎖
數據庫的鎖機制是區別于文件系統的一個關鍵特性。用于管理對共享資源的并發訪問。InnoDB會在很多地方使用鎖機制,比如操作緩沖池中的數據表、LRU頁列表、數據行,為了保證一致性和完整性,需要有鎖的機制。
對于不同數據庫,鎖機制的設計和實現完全不同:
● MyISAM引擎: 表鎖設計,并發讀沒有問題,并發寫性能差。
● Microsoft SQL Server: 支持樂觀并發和悲觀并發,樂觀并發下支持行級鎖,維持鎖的開銷大,在行鎖數量超過閾值后會升級為表鎖。
● InnoDB引擎: 支持行鎖,提供一致性的非鎖定讀。行鎖沒有額外開銷,性能不會下降。
● Oracle:和InnoDB引擎非常類似。
兩類鎖:lock和latch
數據庫中lock和latch都可以稱為鎖,但是有很大的區別。
latch一般稱為閂鎖,用于保證并發線程操作臨界資源的正確性,作用對象是內存數據結構,要求鎖定時間非常短,不會檢測死鎖。在InnoDB引擎中又分為mutex(互斥量)和rwlock(讀寫鎖)。
lock是用來鎖定數據庫中的對象,如表、頁、行,作用對象是事務,在commit/rollback后釋放,會檢測死鎖。分為行鎖、表鎖、意向鎖。
我們下面的鎖指的都是lock類鎖。
四種鎖類型
InnoDB支持四種鎖:
● 共享鎖(S Lock):允許事務讀一行數據
● 排他鎖(X Lock):允許事務刪除或更新一行數據
● 意向共享鎖(Intention S Lock):事務想要獲得一張表中某幾行的共享鎖
● 意向排他鎖(Intention X Lock):事務想要獲得一張表中某幾行的排他鎖
當事務T1獲取了行r的共享鎖,由于讀取不會改變行數據,因此事務T2也可以直接獲得行r的共享鎖,此時稱為鎖兼容(Lock Compatible)。
而當事務T3想要獲取行r的排他鎖進行修改數據時,就需要等待T1/T2釋放行共享鎖,此時稱為鎖不兼容。
S鎖和X鎖都是行鎖,而IS鎖和IX鎖都為意向鎖,屬于表鎖。意向鎖的設計是為了在一個事務中揭示下一行將被請求的鎖類型,即在表鎖的更細粒度進行鎖定。由于InnoDB支持表鎖,因此意向鎖不會阻塞除全表掃描外的任何請求。
鎖的兼容性:
IS | IX | S | X | |
IS | 兼容 | 兼容 | 兼容 | 不兼容 |
IX | 兼容 | 兼容 | 不兼容 | 不兼容 |
S | 兼容 | 不兼容 | 兼容 | 不兼容 |
X | 不兼容 | 不兼容 | 不兼容 | 不兼容 |
存儲事務和鎖信息的三張表
我們可以通過show engine innodb status
命令在事務部分查看當前鎖請求的信息。
從InnoDB1.0開始,在INFORMATION_SCHEMA架構下添加了INNODB_TRX(transaction事務表)、INNODB_LOCKS(鎖表)、INNODB_LOCK_WAITS(鎖等待表),通過這三張表,可以讓我們實時監控當前事務并分析可能存在的表問題。
三個表的定義分別為:
INNODB_TRX | |
---|---|
trx_id | InnoDB存儲引擎內部唯一的事務ID |
trx_state | 當前事務的狀態 |
trx_started | 事務的開始時間 |
trx_requested_lock_id | 等待事務的鎖IDC,當狀態不為LOCK WAIT時為NULL |
trx_wait_started | 事務等待開始的時間 |
trx_weight | 事務的權重,反映一個事務修改和鎖定的行數。當需要回滾時,選擇該值最小的事務進行回滾 |
trx_mysql_thread_id | MySQL的線程ID,show processlist顯示的結果 |
trx_query | 事務運行的SQL語句 |
INNODB_LOCKS | |
---|---|
lock_id | 鎖ID |
lock_trx_id | 事務ID |
lock_mode | 鎖的模式 |
lock_type | 鎖的類型,表鎖或行鎖 |
lock_table | 要加鎖的表 |
lock_index | 鎖住的索引 |
lock_space | 鎖對象的space id |
lock_page | 事務鎖定頁的數量,表鎖時為NULL |
lock_rec | 事務鎖定行的數量,表鎖時為NULL |
lock_data | 事務鎖定記錄的主鍵值,表鎖時為NULL |
INNODB_LOCK_WAITS | |
---|---|
requesting_trx_id | 申請鎖資源的事務ID |
requesting_lock_id | 申請的鎖的ID |
blocking_trx_id | 阻塞的事務ID |
blocking_lock_id | 阻塞的鎖的ID |
通過INNODB_TRX
我們可以看到所有的事務,以及事務是否被阻塞,阻塞的鎖ID是什么。
之后,通過INNODB_LOCKS
查看所有的鎖信息。
之后,通過INNODB_LOCK_WAITS
可以查看到鎖的等待信息以及阻塞關系。
通過這三種表能夠較為清晰的查看事務和鎖的情況,也可以聯合查詢,在下面的一些場景下我們會來展示這三個表的內容。
隔離級別
首先我們來說下數據庫的四種事務隔離級別:
● READ UNCOMMITTED(0): 瀏覽訪問級別,存在臟讀、不可重復讀、幻讀
● READ COMMITTED(1): 游標穩定級別,存在不可重復度、幻讀
● REPEATABLE READ(2): 存在幻讀
● SERIALIZABLE(3): 隔離級別,保證事務安全,但完全串行,性能低
這四種事務隔離級別是指定的SQL標準,InnoDB默認的隔離級別是REAPEATABLE READ,但與其他數據庫不同的時,它同時使用了Next-Key-Lock鎖的算法,能夠避免幻讀的產生,因此能夠完全滿足事務的隔離性要求,即達到SERIALIZABLE隔離級別。
隔離級別越低,事務請求的鎖越少或持鎖時間越短,因此大部分數據庫的默認隔離級別為READ COMMITED。但是有相關的分析也指出,隔離級別的性能開銷幾乎一樣,因此用戶無須通過調整隔離級別來提高性能。
查看和修改事務隔離級別的命令:
mysql> select @@session.tx_isolation; +------------------------+ | @@session.tx_isolation | +------------------------+ | REPEATABLE-READ | +------------------------+ 1 row in set (0.00 sec) mysql> set session transaction isolation level SERIALIZABLE; Query OK, 0 rows affected (0.00 sec)
示例中修改了本次會話的事務隔離級別,如果需要修改全局參數,可以替換session為global。如果想要永久修改,需要修改配置文件:
[mysqld] transaction-isolation = READ-COMMITED
在SERIALIZABLE的事務隔離級別,InnoDB會對每個SELECT語句后自動加上LOCK IN SHARE MODE,來對讀操作加上一個共享鎖,因此不再支持一致性的非鎖定讀。
由于InnoDB在REPEATABLE READ隔離級別就可以達到SERIALIZABLE,因此一般不用使用最高隔離級別。
一致性非鎖定讀和多版本并發控制
一致性非鎖定讀(consistent nonlocking read)是指InnoDB通過行多版本控制(Multi Version Concurrency Control, MVCC)的方法來讀取當前執行時間數據庫中行的數據。
即如果讀取的行正在執行變更操作,這時讀取不會等待行鎖的釋放,而是會讀取行的一個快照數據。快照是指該行的一個歷史數據,通過undo操作來完成。這種方式極大提高了數據庫的并發性,這也是InnoDB的默認設置。
快照是當前行的一個歷史版本,但可能存在多個版本,行數據存在多個快照數據,這種技術成為行多版本技術,由此帶來的并發控制,稱為多版本并發控制(MVCC)。InnoDB在READ COMMITED 和 REPEATABLE READ隔離級別時,會使用非鎖定的一致性讀,但是在這兩種隔離級別使用的快找數據定義卻不同:
● READ COMMITED: 總是讀取最新一份快照
● REPEATABLE READ: 總是讀取事務開始時的行數據版本
我們執行一個示例:
一致性非鎖定讀 | ||
---|---|---|
時間 | 會話A | 會話B |
1 | BEGIN | |
2 | select * from z where a = 3; | |
3 | BEGIN | |
4 | update z set b=2 where a=3; | |
5 | select * from z where a = 3; | |
6 | COMMIT; | |
7 | select * from z where a = 3; | |
8 | COMMIT; |
在這個例子中我們可以清晰的看到0、1、2三種隔離級別的區別:
#在事務開始前我們可以分別調整為0、1、2三種隔離級別,來查看不同的輸出 mysql> set session transaction isolation level READ UNCOMMITTED; Query OK, 0 rows affected (0.00 sec) mysql> select @@tx_isolation; +------------------+ | @@tx_isolation | +------------------+ | READ-UNCOMMITTED | +------------------+ 1 row in set (0.00 sec) # A會話:T1事務 mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> select * from z where a = 3; +---+------+ | a | b | +---+------+ | 3 | 1 | +---+------+ 1 row in set (0.00 sec) # B會話:T2事務 mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> update z set b=2 where a=3; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 # A會話:T1事務,如果此時隔離級別是READ-UNCOMMITTED,因為此刻事務2可能會回滾,所以出現了臟讀 mysql> select * from z where a=3; +---+------+ | a | b | +---+------+ | 3 | 2 | +---+------+ 1 row in set (0.00 sec) # A會話:T1事務,如果此時隔離級別是大于READ-UNCOMMITTED的更高級別 mysql> select * from z where a=3; +---+------+ | a | b | +---+------+ | 3 | 1 | +---+------+ 1 row in set (0.00 sec) # B會話:T2事務 mysql> commit; Query OK, 0 rows affected (0.00 sec) # A會話:T1事務,如果此時隔離級別是READ-COMMITTED,因為數據和事務開始時讀取的出現了不一致,因此稱為不可重復讀,能夠讀到其他事務的結果,違反了事務的隔離性 mysql> select * from z where a=3; +---+------+ | a | b | +---+------+ | 3 | 2 | +---+------+ 1 row in set (0.00 sec) # A會話:T1事務,如果此時隔離級別是大于READ-COMMITTED的更高級別 mysql> select * from z where a=3; +---+------+ | a | b | +---+------+ | 3 | 1 | +---+------+ 1 row in set (0.00 sec) # A會話:T1事務 mysql> commit; Query OK, 0 rows affected (0.00 sec)
一致性鎖定讀和SERIALIZABLE隔離
在默認的REPEATABLE READ隔離級別時,InnoDB使用的是一致性非鎖定讀。但有時我們也需要顯示的指定使用一致性鎖定讀來保證讀取操作時對數據進行加鎖達到一致性。這要求數據庫支持鎖定讀加鎖語句:
● select ... for update: 讀取時對行記錄加X鎖
● select ... lock in share mode:讀取時對行記錄加一個S鎖
這兩種鎖必須在一個事務中,當事務提交后鎖也就釋放了,因此務必加上BEGIN, START TRANSACTION或者SET AUTOCOMMIT=0。
我們在前面隔離級別時也說過SERIALIZABLE隔離級別會對讀操作自動加上LOCK IN SHARE MODE指令來加上一個共享鎖,因此不再支持一致性的非鎖定讀。這也是隔離級別3的一大特性。
總結
由于鎖的概念非常重要,這里先講了鎖的概念、鎖的類型、鎖的信息查看、事務的隔離級別和區別,后面我們會繼續說鎖的算法、鎖的三種問題和幻讀、死鎖和鎖升級。
看完上述內容,你們對MySQL中的鎖和事務隔離級別有進一步的了解嗎?如果還想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。