MySQL 全局鎖、表級鎖、行級鎖,你搞清楚了嗎?( 二 )

需要注意的是,表鎖除了會限制別的線程的讀寫外,也會限制本線程接下來的讀寫操作 。
也就是說如果本線程對學生表加了「共享表鎖」,那么本線程接下來如果要對學生表執行寫操作的語句 , 是會被阻塞的,當然其他線程對學生表進行寫操作時也會被阻塞,直到鎖被釋放 。
要釋放表鎖,可以使用下面這條命令,會釋放當前會話的所有表鎖:
unlock tables另外,當會話退出后 , 也會釋放所有表鎖 。
不過盡量避免在使用 InnoDB 引擎的表使用表鎖,因為表鎖的顆粒度太大,會影響并發性能,InnoDB 牛逼的地方在于實現了顆粒度更細的行級鎖 。
元數據鎖再來說說元數據鎖(MDL) 。
我們不需要顯示的使用 MDL,因為當我們對數據庫表進行操作時,會自動給這個表加上 MDL:

  • 對一張表進行 CRUD 操作時,加的是 MDL 讀鎖;
  • 對一張表做結構變更操作的時候 , 加的是 MDL 寫鎖;
MDL 是為了保證當用戶對表執行 CRUD 操作時,防止其他線程對這個表結構做了變更 。
當有線程在執行 select 語句( 加 MDL 讀鎖)的期間,如果有其他線程要更改該表的結構( 申請 MDL 寫鎖),那么將會被阻塞,直到執行完 select 語句( 釋放 MDL 讀鎖) 。
反之,當有線程對表結構進行變更( 加 MDL 寫鎖)的期間,如果有其他線程執行了 CRUD 操作( 申請 MDL 讀鎖),那么就會被阻塞,直到表結構變更完成( 釋放 MDL 寫鎖) 。
MDL 不需要顯示調用 , 那它是在什么時候釋放的?
MDL 是在事務提交后才會釋放,這意味著事務執行期間,MDL 是一直持有的 。
那如果數據庫有一個長事務(所謂的長事務,就是開啟了事務,但是一直還沒提交),那在對表結構做變更操作的時候,可能會發生意想不到的事情,比如下面這個順序的場景:
  1. 首先,線程 A 先啟用了事務(但是一直不提交),然后執行一條 select 語句,此時就先對該表加上 MDL 讀鎖;
  2. 然后,線程 B 也執行了同樣的 select 語句,此時并不會阻塞,因為「讀讀」并不沖突;
  3. 接著,線程 C 修改了表字段 , 此時由于線程 A 的事務并沒有提交,也就是 MDL 讀鎖還在占用著,這時線程 C 就無法申請到 MDL 寫鎖,就會被阻塞 , 
那么在線程 C 阻塞后,后續有對該表的 select 語句,就都會被阻塞,如果此時有大量該表的 select 語句的請求到來,就會有大量的線程被阻塞??,震}筆菘獾南叱毯蕓煬突岜?。
為什么線程 C 因為申請不到 MDL 寫鎖 , 而導致后續的申請讀鎖的查詢操作也會被阻塞?
這是因為申請 MDL 鎖的操作會形成一個隊列,隊列中寫鎖獲取優先級高于讀鎖 , 一旦出現 MDL 寫鎖等待,會阻塞后續該表的所有 CRUD 操作 。
所以為了能安全的對表結構進行變更,在對表結構變更前 , 先要看看數據庫中的長事務,是否有事務已經對表加上了 MDL 讀鎖,如果可以考慮 kill 掉這個長事務,然后再做表結構的變更 。
意向鎖接著,說說意向鎖 。
  • 在使用 InnoDB 引擎的表里對某些記錄加上「共享鎖」之前 , 需要先在表級別加上一個「意向共享鎖」;
  • 在使用 InnoDB 引擎的表里對某些紀錄加上「獨占鎖」之前,需要先在表級別加上一個「意向獨占鎖」;
也就是,當執行插入、更新、刪除操作,需要先對表加上「意向獨占鎖」,然后對該記錄加獨占鎖 。
而普通的 select 是不會加行級鎖的,普通的 select 語句是利用 MVCC 實現一致性讀,是無鎖的 。
不過,select 也是可以對記錄加共享鎖和獨占鎖的,具體方式如下:
//先在表上加上意向共享鎖 , 然后對讀取的記錄加共享鎖select ... lock in share mode;//先表上加上意向獨占鎖 , 然后對讀取的記錄加獨占鎖select ... for update;意向共享鎖和意向獨占鎖是表級鎖,不會和行級的共享鎖和獨占鎖發生沖突,而且意向鎖之間也不會發生沖突 , 只會和共享表鎖(

推薦閱讀