MySQL 技術內幕——鎖模塊

數據庫鎖按照鎖的粒度劃分,可分爲表級鎖、行級鎖、頁級鎖;按照鎖級別劃分,可分爲共享鎖、排他鎖;按照加鎖方式劃分,可分爲自動鎖、顯式鎖;按照操作劃分,可分爲DML 鎖、DDL 鎖;按照使用方式劃分,可分爲樂觀鎖、悲觀鎖。

1、MyISAM 與 InnoDB 關於鎖方面的區別是什麼?

MyISAM 默認用的是表級鎖,不支持行級鎖。

對於 insert、update、delete,MyISAM 會自動加一個表級別的寫鎖 (排他鎖);對於 select 會自動加一個表級別的讀鎖 (共享鎖)。

  • 讀鎖不會阻塞讀鎖;
  • 讀鎖會阻塞寫鎖,直到所有讀鎖被釋放爲止;
  • 寫鎖會阻塞寫鎖,直到寫鎖被釋放爲止;
  • 寫鎖會阻塞讀鎖,直到寫鎖被釋放爲止。

可以通過以下 SQL 顯式的加讀鎖或寫鎖:

LOCK TABLES person_info_myisam READ; // MyISAM 讀鎖
LOCK TABLES person_info_myisam WRITE; // MyISAM 寫鎖
UNLOCK TABLES;  // MyISAM 釋放讀鎖

// MyISAM SELECT 默認加讀鎖
SELECT ... FOR UPDATE; // MyISAM SELECT 加寫鎖

InnoDB 默認用的是行級鎖,也支持表級鎖。需要注意的是,InnoDB 在 SQL 沒有用到索引的時候,走的依然是表級鎖,只有 SQL 用到索引的時候走的是行級鎖。

在 SQL 用到索引的時候,對於 insert、update、delete,InnoDB 會自動給涉及的數據行加一個寫鎖 (排他鎖);對於 select 不會加任何鎖。

事務可以通過以下 SQL 顯式的加讀鎖或寫鎖:

SELECT ... LOCK IN SHARE MODE; // InnoDB SELECT 加讀鎖
SELECT ... FOR UPDATE; // InnoDB SELECT 加寫鎖

與 MyISAM 相比,由於 InnoDB 支持事務,所以 InnoDB 使用的是二段鎖,二段鎖即加鎖和解鎖是分爲兩個步驟來進行的,即先對同一個事務裏的一批操作分別進行加鎖,然後到 commit (提交) 的時候再對事務里加上的鎖進行統一的解鎖。

MyISAM 適合的場景:

  • 頻繁執行全表 count 語句的場景;
  • 對數據進行增刪改的頻率不高,查詢非常頻繁的場景;
  • 沒有事務的場景。

InnoDB 適合的場景:

  • 數據增刪改查都相當頻繁的場景;
  • 可靠性要求比較高,要求支持事務的場景。

爲什麼 MyISAM 查詢速度比 InnoDB 快?

  • InnoDB 要緩存數據塊,MyISAM 只緩存索引塊,這中間還有換進換出的減少;
  • InnoDB 尋址要映射到塊,再到行,MyISAM 記錄的直接是文件的 OFFSET,比 InnoDB 定位要快;
  • InnoDB 還需要維護 MVCC 一致,即使你的場景沒有,但他還是需要去檢查和維護 MVCC 多版本併發控制 。

2、使用 MySQL 實現樂觀鎖?

數據庫表裏可以增加一個 version 字段,查詢數據的時候將 version 也查詢出來,數據每更新一次,version + 1,當提交更新的時候,判斷當前 version 與第一次取出來的是否相等,如果相等則更新,否則認爲是過期數據。這就是樂觀鎖的一種實現方式。

3、數據庫事務的四大特性?

數據庫事務的四大特性即 ACID:

說明
原子性(Atomic) 事務包含的所有操作要麼全部執行,要麼全部失敗回滾。
一致性(Consistency) 事務應確保數據庫的狀態從一個一致狀態轉變爲另外一個一致狀態。
隔離性(Isolation) 多個事務併發執行時,一個事務的執行不應該影響其他事務的執行。
持久性(Durability) 一個事務一旦提交,它對數據庫的修改應該永久保存在數據庫中。

隔離性是重點。

4、併發訪問產生的問題以及事務隔離機制?

MySQL 會利用鎖機制創建出不同的事務隔離級別,下面將按照事務隔離級別從低到高的順序,看下由於併發訪問產生的問題:

問題 說明 解決
更新丟失 MySQL 所有事務隔離級別在數據庫層面上均可避免
髒讀 A 事務讀取 B 事務尚未提交的數據,此時如果 B 事務發生回滾,那麼 A 事務讀取到的數據就是髒數據 READ-COMMITTED 事務隔離級別以上可避免
不可重複讀 A 事務前後多次讀取同一條數據 。在 A 事務第一次讀取數據後,B 事務執行了更改操作,此時 A 事務第二次讀取到數據時,發現和之前的數據不一樣了,成爲不可重複讀 REPEATABLE-READ 事務隔離級別以上可避免
幻讀 A 事務執行讀取操作,需要兩次統計數據的總量,前一次查詢數據總量後,B 事務執行了新增數據的操作並完成提交,此時 A 事務再讀取數據總量,發現和之前統計的不一樣,成爲幻讀。 SERTALIZABLE 事務隔離級別可避免

不可重複讀和幻讀有什麼區別?

不可重複讀是讀取了其他事務更改的數據,針對 insert、update,解決:可以使用行級鎖鎖定該行,A 事務多次讀取操作完成後才釋放該鎖,這個時候才允許其他事務更改剛纔的數據。

幻讀是讀取了其他事務新增的數據,針對 insert、delete,解決:使用表級鎖,鎖定整張表,A 事務多次讀取數據總量之後才釋放該鎖,這個時候才允許其他事務新增數據。

事務隔離機制:

隔離級別\問題 更新丟失 髒讀 不可重複讀 幻讀
未提交讀 可避免 會發生 會發生 會發生
已提交讀(RC,READ-COMMITTED) 可避免 可避免 會發生 會發生
可重複讀(RR,REPEATABLE-READ) 可避免 可避免 可避免 會發生
串行化(SERTALIZABLE) 可避免 可避免 可避免 可避免

5、InnoDB 可重複讀 (RR) 隔離級別下如何避免幻讀?

可重複讀隔離級別理論上避免不了幻讀,而是通過一種巧妙的方式規避了幻讀。分爲表象和內在:

  • 表象:快照讀(非阻塞讀),僞 MVCC;
  • 內在:next-key 鎖(行鎖 + gap 鎖)。

當前讀:

select ... lock in share mode; # 讀鎖(共享鎖)
select ... for update; # 寫鎖(排他鎖)
update ...; # 寫鎖
delete ...; # 寫鎖
insert ...; # 寫鎖

快照讀:不加鎖的非阻塞讀,select。

可重複讀 (RR) 隔離級別下避免幻讀,根本原因是事務對數據加了 next-key 鎖,next-key 鎖由行鎖 + gap 鎖組成:

  • 行鎖:對單行記錄上的鎖;
  • gap 鎖:gap 就是索引樹中插入新記錄的空隙,gap 鎖即鎖定一個範圍,但不包括記錄本身,gap 鎖的目的是爲了防止同一事務的兩次當前讀出現幻讀的情況。

可重複讀 (RR) 隔離級別下 gap 鎖出現的場景:

不論 delete、update、select,當前讀若用到主鍵索引或者唯一索引會用到 Gap 鎖嗎?

  • 如果 where 條件全部命中,則不會用 gap 鎖,只會加行鎖;
  • 如果 where 條件部分命中或者全不命中,則會加 gap 鎖;

gap 鎖會用在非唯一索引或者不走索引的當前讀中。

6、已提交讀 (RC)、可重複讀 (RR) 級別下的 InnoDB 的非阻塞讀如何實現?

  • 數據行裏的 DB_TRX_ID、DB_ROLL_PTR、DB_ROW_ID 字段;
  • undo 日誌;
  • read view。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章