美團一面:能不能通俗的解釋下為什么要有意向鎖這個東西?
眾所周知,InnoDB 中既有讀鎖也有寫鎖,也稱為共享鎖和排他鎖,這兩種鎖既可以加在整張表上,也可以加在行上。
MySQL 自身就提供了表鎖的能力:
- 讀鎖:LOCK TABLE table_name READ 用讀鎖鎖表,會阻塞其他事務的寫操作
- 寫鎖:LOCK TABLE table_name WRITE 用寫鎖鎖表,會阻塞其他事務的讀和寫操作
行鎖是 InnoDB 存儲引擎提供的,MySQL 本身并不提供行級鎖的能力:
- 讀鎖,如SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE 加行級讀鎖,會阻塞其他事務對該行記錄的寫操作
- 寫鎖,如SELECT * FROM table_name WHERE ... FOR UPDATE 加行級寫鎖,會阻塞其他事務對該行記錄的的讀和寫操作
又有表鎖又有行鎖,我們來考慮下這兩種類型的鎖共存的問題。看下面這個例子:
事務 A 加了行級讀鎖,鎖住了表中的一行,讓這一行只能讀,不能寫。
之后,事務 B 嘗試申請整個表的寫鎖。
如果事務 B 申請成功,那么理論上它就能修改表中的任意一行,這與 A 持有的行級讀鎖是沖突的。
數據庫需要避免這種沖突,就勢必要讓 B 的申請被阻塞,直到 A 釋放行級讀鎖。
那數據庫要怎么判斷這個沖突呢?
- 步驟 1:判斷表是否已被其他事務用表級鎖鎖住了整張表
- 步驟 2:判斷表中的每一行是否已被行級鎖鎖住
看起來沒有什么困難的,但請注意步驟 2,判斷表中的每一行,各位,如何判斷?
顯然,需要遍歷!遍歷表中的每一行。
小學生都能想到這樣的判斷方法效率實在太過于低下了。
于是就有了意向鎖!
我們先來看下意向鎖的解釋:
Intention locks are table-level locks that indicate which type of lock (shared or exclusive) a transaction requires later for a row in a table.
意向鎖是一個表級鎖,其作用就是指明接下來的事務將會用到哪種鎖。
有兩種意向鎖:
- 意向共享鎖/讀鎖(IS Lock):當事務想要獲得一張表中某幾行的讀鎖(行級讀鎖)時,InnoDB 存儲引擎會自動地先獲取該表的意向讀鎖(表級鎖)
- 意向排他鎖/寫鎖(IX Lock):當事務想要獲得一張表中某幾行的寫鎖(行級寫鎖)時,InnoDB 存儲引擎會自動地先獲取該表的意向寫鎖(表級鎖)
注意這里的自動:申請意向鎖的動作是數據庫完成的,就是說,事務 A 申請一行的行鎖的時候,數據庫會自動先開始申請表的意向鎖,不需要我們程序員使用代碼來申請。
在意向鎖存在的情況下,事務 A 如果想申請行級讀鎖,就必須先申請該表的意向讀鎖,申請成功后才能繼續申請某行記錄的行級讀鎖。
在意向鎖存在的情況下,上面的判斷可以改成:
- 步驟 1(不變):判斷表是否已被其他事務用表級鎖鎖住了整張表
- 步驟 2:發現表上有意向讀鎖(說明表中有些行被行級讀鎖鎖住了),意向讀鎖和表級寫鎖互斥,因此,事務 B 申請表的寫鎖會被阻塞。
也就是說原先步驟 2 的遍歷表中每一行的操作,簡化成了判斷下整張表上有無表級意向鎖就行了,效率大幅提升。
這就是為什么要有意向鎖了。