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