數據庫鎖按照鎖的粒度劃分,可分爲表級鎖、行級鎖、頁級鎖;按照鎖級別劃分,可分爲共享鎖、排他鎖;按照加鎖方式劃分,可分爲自動鎖、顯式鎖;按照操作劃分,可分爲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。