MySQL 中鎖的面試題總結


什么是鎖?MySQL 中提供了幾類鎖?

鎖是實現數據庫並發控制的重要手段,可以保證數據庫在多人同時操作時能夠正常運行。MySQL 提供了全局鎖、行級鎖、表級鎖。其中 InnoDB 支持表級鎖和行級鎖,MyISAM 只支持表級鎖。

什么是死鎖?

是指兩個或兩個以上的進程在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的過程稱為死鎖。

死鎖是指兩個或兩個以上的進程在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的過程稱為死鎖。

常見的死鎖案例有哪些?

  • 將投資的錢拆封幾份借給借款人,這時處理業務邏輯就要把若干個借款人一起鎖住 select * from xxx where id in (xx,xx,xx) for update。
  • 批量入庫,存在則更新,不存在則插入。解決方法 insert into tab(xx,xx) on duplicate key update xx='xx'。

如何處理死鎖?

對待死鎖常見的兩種策略:

  • 通過 innodblockwait_timeout 來設置超時時間,一直等待直到超時;
  • 發起死鎖檢測,發現死鎖之后,主動回滾死鎖中的某一個事務,讓其它事務繼續執行。

如何查看死鎖?

  • 使用命令 show engine innodb status 查看最近的一次死鎖。
  • InnoDB Lock Monitor 打開鎖監控,每 15s 輸出一次日志。使用完畢后建議關閉,否則會影響數據庫性能。

如何避免死鎖?

  • 為了在單個 InnoDB 表上執行多個並發寫入操作時避免死鎖,可以在事務開始時通過為預期要修改的每個元祖(行)使用 SELECT ... FOR UPDATE 語句來獲取必要的鎖,即使這些行的更改語句是在之后才執行的。
  • 在事務中,如果要更新記錄,應該直接申請足夠級別的鎖,即排他鎖,而不應先申請共享鎖、更新時再申請排他鎖,因為這時候當用戶再申請排他鎖時,其他事務可能又已經獲得了相同記錄的共享鎖,從而造成鎖沖突,甚至死鎖
  • 如果事務需要修改或鎖定多個表,則應在每個事務中以相同的順序使用加鎖語句。在應用中,如果不同的程序會並發存取多個表,應盡量約定以相同的順序來訪問表,這樣可以大大降低產生死鎖的機會
  • 通過 SELECT ... LOCK IN SHARE MODE 獲取行的讀鎖后,如果當前事務再需要對該記錄進行更新操作,則很有可能造成死鎖。
  • 改變事務隔離級別。

InnoDB 默認是如何對待死鎖的?

InnoDB 默認是使用設置死鎖時間來讓死鎖超時的策略,默認 innodblockwait_timeout 設置的時長是 50s。

如何開啟死鎖檢測?

設置 innodbdeadlockdetect 設置為 on 可以主動檢測死鎖,在 Innodb 中這個值默認就是 on 開啟的狀態。

什么是全局鎖?它的應用場景有哪些?

全局鎖就是對整個數據庫實例加鎖,它的典型使用場景就是做全庫邏輯備份。 這個命令可以使整個庫處於只讀狀態。使用該命令之后,數據更新語句、數據定義語句、更新類事務的提交語句等操作都會被阻塞。

什么是共享鎖?

共享鎖又稱讀鎖 (read lock),是讀取操作創建的鎖。其他用戶可以並發讀取數據,但任何事務都不能對數據進行修改(獲取數據上的排他鎖),直到已釋放所有共享鎖。當如果事務對讀鎖進行修改操作,很可能會造成死鎖。

什么是排它鎖?

排他鎖 exclusive lock(也叫 writer lock)又稱寫鎖。

若某個事物對某一行加上了排他鎖,只能這個事務對其進行讀寫,在此事務結束之前,其他事務不能對其進行加任何鎖,其他進程可以讀取,不能進行寫操作,需等待其釋放。

排它鎖是悲觀鎖的一種實現,在上面悲觀鎖也介紹過。

若事務 1 對數據對象 A 加上 X 鎖,事務 1 可以讀 A 也可以修改 A,其他事務不能再對 A 加任何鎖,直到事物 1 釋放 A 上的鎖。這保證了其他事務在事物 1 釋放 A 上的鎖之前不能再讀取和修改 A。排它鎖會阻塞所有的排它鎖和共享鎖。

使用全局鎖會導致什么問題?

如果在主庫備份,在備份期間不能更新,業務停擺,所以更新業務會處於等待狀態。

如果在從庫備份,在備份期間不能執行主庫同步的 binlog,導致主從延遲。

如何處理邏輯備份時,整個數據庫不能插入的情況?

如果使用全局鎖進行邏輯備份就會讓整個庫成為只讀狀態,幸好官方推出了一個邏輯備份工具 MySQLdump 來解決了這個問題,只需要在使用 MySQLdump 時,使用參數 -single-transaction 就會在導入數據之前啟動一個事務來保證數據的一致性,並且這個過程是支持數據更新操作的。

如何設置數據庫為全局只讀鎖?

使用命令 flush tables with read lock(簡稱 FTWRL)就可以實現設置數據庫為全局只讀鎖。

除了 FTWRL 可以設置數據庫只讀外,還有什么別的方法?

除了使用 FTWRL 外,還可以使用命令 set global readonly=true 設置數據庫為只讀。

FTWRL 和 set global readonly=true 有什么區別?

FTWRL 和 set global readonly=true 都是設置整個數據庫為只讀狀態,但他們最大的區別就是,當執行 FTWRL 的客戶端斷開之后,整個數據庫會取消只讀,而 set global readonly=true 會一直讓數據處於只讀狀態。

如何實現表鎖?

MySQL 里標記鎖有兩種:表級鎖、元數據鎖(meta data lock)簡稱 MDL。表鎖的語法是 lock tables t read/write。

可以用 unlock tables 主動釋放鎖,也可以在客戶端斷開的時候自動釋放。lock tables 語法除了會限制別的線程的讀寫外,也限定了本線程接下來的操作對象。

對於 InnoDB 這種支持行鎖的引擎,一般不使用 lock tables 命令來控制並發,畢竟鎖住整個表的影響面還是太大。

MDL:不需要顯式使用,在訪問一個表的時候會被自動加上。

MDL 的作用:保證讀寫的正確性。

在對一個表做增刪改查操作的時候,加 MDL 讀鎖;當要對表做結構變更操作的時候,加 MDL 寫鎖。

讀鎖之間不互斥,讀寫鎖之間,寫鎖之間是互斥的,用來保證變更表結構操作的安全性。

MDL 會直到事務提交才會釋放,在做表結構變更的時候,一定要小心不要導致鎖住線上查詢和更新。

悲觀鎖和樂觀鎖有什么區別?

顧名思義,就是很悲觀,每次去拿數據的時候都認為別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會 block 直到它拿到鎖。正因為如此,悲觀鎖需要耗費較多的時間,另外與樂觀鎖相對應的,悲觀鎖是由數據庫自己實現了的,要用的時候,我們直接調用數據庫的相關語句就可以了。

說到這里,由悲觀鎖涉及到的另外兩個鎖概念就出來了,它們就是共享鎖與排它鎖。共享鎖和排它鎖是悲觀鎖的不同的實現,它倆都屬於悲觀鎖的范疇。

樂觀鎖是用數據版本(Version)記錄機制實現,這是樂觀鎖最常用的一種實現方式。何謂數據版本?即為數據增加一個版本標識,一般是通過為數據庫表增加一個數字類型的 version 字段來實現。當讀取數據時,將 version 字段的值一同讀出,數據每更新一次,對此 version 值加 1。當我們提交更新的時候,判斷數據庫表對應記錄的當前版本信息與第一次取出來的version值進行比對,如果數據庫表當前版本號與第一次取出來的 version 值相等,則予以更新,否則認為是過期數據。

比如: 1、數據庫表三個字段,分別是id、value、version select id,value,version from t where id=#{id} 2、每次更新表中的value字段時,為了防止發生沖突,需要這樣操作

update t
set value=2,version=version+1
where id=#{id} and version=#{version}

樂觀鎖有什么優點和缺點?

因為沒有加鎖所以樂觀鎖的優點就是執行性能高。它的缺點就是有可能產生 ABA 的問題,ABA 問題指的是有一個變量 V 初次讀取的時候是 A 值,並且在准備賦值的時候檢查到它仍然是 A 值,會誤以為沒有被修改會正常的執行修改操作,實際上這段時間它的值可能被改了其他值,之后又改回為 A 值,這個問題被稱為 ABA 問題。

InnoDB 存儲引擎有幾種鎖算法?

  • Record Lock — 單個行記錄上的鎖;
  • Gap Lock — 間隙鎖,鎖定一個范圍,不包括記錄本身;
  • Next-Key Lock — 鎖定一個范圍,包括記錄本身。

InnoDB 如何實現行鎖?

行級鎖是 MySQL 中粒度最小的一種鎖,他能大大減少數據庫操作的沖突。

INNODB 的行級鎖有共享鎖(S LOCK)和排他鎖(X LOCK)兩種。共享鎖允許事物讀一行記錄,不允許任何線程對該行記錄進行修改。排他鎖允許當前事物刪除或更新一行記錄,其他線程不能操作該記錄。

共享鎖:SELECT ... LOCK IN SHARE MODE,MySQL 會對查詢結果集中每行都添加共享鎖,前提是當前線程沒有對該結果集中的任何行使用排他鎖,否則申請會阻塞。

排他鎖:select * from t where id=1 for update,其中 id 字段必須有索引,MySQL 會對查詢結果集中每行都添加排他鎖,在事物操作中,任何對記錄的更新與刪除操作會自動加上排他鎖。前提是當前沒有線程對該結果集中的任何行使用排他鎖或共享鎖,否則申請會阻塞。

優化鎖方面你有什么建議?

  • 盡量使用較低的隔離級別。
  • 精心設計索引, 並盡量使用索引訪問數據, 使加鎖更精確, 從而減少鎖沖突的機會。
  • 選擇合理的事務大小,小事務發生鎖沖突的幾率也更小。
  • 給記錄集顯示加鎖時,最好一次性請求足夠級別的鎖。比如要修改數據的話,最好直接申請排他鎖,而不是先申請共享鎖,修改時再請求排他鎖,這樣容易產生死鎖。
  • 不同的程序訪問一組表時,應盡量約定以相同的順序訪問各表,對一個表而言,盡可能以固定的順序存取表中的行。這樣可以大大減少死鎖的機會。
  • 盡量用相等條件訪問數據,這樣可以避免間隙鎖對並發插入的影響。
  • 不要申請超過實際需要的鎖級別。
  • 除非必須,查詢時不要顯示加鎖。 MySQL 的 MVCC 可以實現事務中的查詢不用加鎖,優化事務性能;MVCC 只在 COMMITTED READ(讀提交)和 REPEATABLE READ(可重復讀)兩種隔離級別下工作。
  • 對於一些特定的事務,可以使用表鎖來提高處理速度或減少死鎖的可能。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM