MySQL鎖相關知識點


MySQL 鎖

  MySQL 的鎖機制,主要用於高並發場景下,比較重要的知識點是保證數據的一致性的和事務隔離性,在高並發下控制並發訪問。

 

 

 

鎖的種類

  MySQL 中鎖的分類按照不同類型的划分可以分成不同的鎖,

  • 按照鎖的粒度:
    • 表鎖:粒度最大的鎖,開銷小,加鎖快,不會出現死鎖,但是由於粒度太大,因此造成鎖的沖突幾率大,並發性能低。常見的有 MyISAM 儲存引擎就支持表鎖,MyISAM的表鎖模式有兩種:表共享讀鎖和表獨占寫鎖

      當一個線程獲取到 MyISAM 表的讀鎖的時候,會阻塞其他用戶對該表的寫操作,但是不會阻塞其它用戶對該用戶的讀操作。

      相反的,當一個線程獲取到 MyISAM 表的寫鎖的時候,就會阻塞其它用戶的讀寫操作對其它的線程具有排它性。

    • 頁鎖:粒度是介於行鎖和表鎖之間的一種鎖,頁鎖是在 BDB 中支持的一種鎖機制,很少提及和使用
    • 行鎖:粒度最小的鎖機制,行鎖的加鎖開銷性能大,加鎖慢,並且會出現死鎖,但是行鎖的鎖沖突的幾率低,並發性能高。

      行鎖是 InnoDB 默認的支持的鎖機制,MyISAM 不支持行鎖,這個也是 InnoDB 和 MyISAM 的區別之一。

      行鎖在使用的方式上可以划分為:共享讀鎖( S 鎖)排它寫鎖( X 鎖)

      當一個事務對 MySQL 中的一條數據行加上了 S 鎖,當前事務不能修改該行數據只能執行讀操作,其他事務只能對該行數據加 S 鎖不能加 X 鎖。若是一個事務對一行數據加了 X 鎖,該事務能夠對該行數據執行讀和寫操作,其它事務不能對該行數據加任何的鎖,既不能讀也不能寫。

  • 按照使用的方式共享鎖排它鎖
  • 按照思想:數據庫管理系統中為了控制並發,保證在多個事務執行時的數據一致性以及事務的隔離性,使用悲觀鎖和樂觀鎖來解決並發場景下的問題。
    • 樂觀鎖:需要程序員自己去實現的鎖機制
    • 悲觀鎖:悲觀鎖的實現是基於Mysql自身的鎖機制實現

悲觀鎖和樂觀鎖是在很多框架都存在的一種思想,不要狹義地認為它們是某一種框架的鎖機制。

 

MyISAM

  MyISAM中 默認支持的表級鎖有兩種:共享讀鎖獨占寫鎖。表級鎖在 MyISAM 和 InnoDB 的存儲引擎中都支持,但是 InnoDB 默認支持的是行鎖。

  MySQL 中平時讀寫操作都是隱式的進行加鎖和解鎖操作,MySQL 已經自動實現加鎖和解鎖的操作,若是想要測試鎖機制,就要顯示的自己控制鎖機制。

MySQL 中可以通過以下 sql 來顯示的在事務中顯式的進行加鎖和解鎖操作:

-- 顯式的添加表級讀鎖
LOCK TABLE 表名 READ
-- 顯示的添加表級寫鎖
LOCK TABLE 表名 WRITE
-- 顯式的解鎖(當一個事務commit的時候也會自動解鎖)
unlock tables;

 

MyISAM 表級寫鎖

CREATE TABLE IF NOT EXISTS employee (
    id INT PRIMARY KEY auto_increment,
    name VARCHAR(40),
    money INT
)ENGINE MyISAM

INSERT INTO employee(name, money) VALUES('黎杜', 1000);
INSERT INTO employee(name, money) VALUES('非科班的科班', 2000);
示例使用的表結構及數據
LOCK TABLE employee WRITE
第一個程序使用鎖
select * from employee  # 阻塞
第二個程序對同一個表進行操作時,會阻塞

第一個程序可以任意讀寫該表,但其他程序不行。只有在鎖被釋放時,別的程序才能對該表進行操作

 

MyISAM表級共享讀鎖

測試數據同上

LOCK TABLEemployee read;
第一個程序使用鎖

第一個程序進行插入、更新數據,發現都會報錯,只能查詢數據。

其他程序也無法進行插入、更新數據,但是可以查詢數據。

 

MyISAM表級鎖競爭情況

MyISAM存儲引擎中,可以通過查詢變量來查看並發場景鎖的爭奪情況

show status like 'table_locks%';

通過 table_locks_waited 和 table_locks_immediate 的值的大小分析鎖的競爭情況。

  • Table_locks_immediate:表示能夠立即獲得表級鎖的鎖請求次數;
  • Table_locks_waited表示不能立即獲取表級鎖而需要等待的鎖請求次數分析,值越大競爭就越嚴重

 

並發插入

  上述例子中的加鎖和釋放鎖都是在 MySQL 中已經實現了的隱式的操作,實際並不會這么做的。

  MyISAM 存儲引擎中,雖然讀寫操作是串行化的,但是它也支持並發插入,這個需要設置內部變量 concurrent_insert的值。它的值有三個值0、1、2。可以通過以下的 sql 查看 concurrent_insert 的默認值為AUTO(或者1)

  • 值為 NEVER (or 0) 表示不支持比並發插入;
  • 值為 AUTO (或者1)表示在MyISAM表中沒有被刪除的行,運行另一個線程從表尾插入數據;
  • 值為 ALWAYS (or 2)表示不管是否有刪除的行,都允許在表尾插入數據。
show variable like '%concurrent_insert';

 

鎖調度

  MyISAM 存儲引擎中,假如同時一個讀請求,一個寫請求過來的話,會優先處理寫請求,因為 MyISAM 存儲引擎中認為寫請求比讀請求重要。這樣就會導致,假如大量的讀寫請求過來,就會導致讀請求長時間的等待,或者"線程餓死",因此 MyISAM 不適合運用於大量讀寫操作的場景,這樣會導致長時間讀取不到用戶數據,用戶體驗感極差。

  可以通過設置low-priority-updates參數,設置請求鏈接的優先級,使得 MySQL 優先處理讀請求。

 

InnoDB

  InnoDB 和 MyISAM 不同的是,InnoDB 支持行鎖事務

  InnoDB 中除了有表鎖行級鎖的概念,還有 Gap Lock(間隙鎖)、Next-key Lock鎖,間隙鎖主要用於范圍查詢的時候,鎖住查詢的范圍,並且間隙鎖也是解決幻讀的方案

  InnoDB 中的行級鎖是對索引加的鎖,在不通過索引查詢數據的時候,InnoDB 就會使用表鎖。但是通過索引查詢的時候是否使用索引,還要看 MySQL 的執行計划,MySQL 的優化器會判斷是一條 sql 執行的最佳策略。若是 MySQL 覺得執行索引查詢還不如全表掃描速度快,那么 MySQL 就會使用全表掃描來查詢,這是即使 sql 語句中使用了索引,最后還是執行為全表掃描,加的是表鎖。

 

InnoDB 的行鎖與表鎖

  InnoDB的行鎖也是分為行級共享讀鎖(S鎖)排它寫鎖(X鎖),原理特點和 MyISAM 的表級鎖兩種模式是一樣的。

  1. 執行非索引條件查詢執行的是表鎖。
  2. 執行索引查詢是否是加行鎖,還得看 MySQL 的執行計划,可以通過 explain 關鍵字來查看。
  3. 用普通鍵索引的查詢,遇到索引值相同的,也會對其他的操作數據行的產生影響。

InnoDB 的間隙鎖

  當使用范圍條件查詢而不是等值條件查詢的時候,InnoDB 就會給符合條件的范圍索引加鎖,在條件范圍內並不存的記錄就叫做"間隙(GAP)"

  在事務的四大隔離級別中,不可重復讀會產生幻讀的現象,只能通過提高隔離級別到串行化來解決幻讀現象。但是 MySQL 中的不可重復是已經解決了幻讀問題,它通過引入間隙鎖的實現來解決幻讀,通過給符合條件的間隙加鎖,防止再次查詢的時候出現新數據產生幻讀的問題。

  1. 主鍵索引不需要間隙鎖——主鍵索引具有唯一性,不允許出現重復,那么當進行等值查詢的時候id=3,只能有且只有一條數據,是不可能再出現id=3的第二條數據。因此它只要鎖定這條數據(鎖定索引),在下次查詢當前讀的時候不會被刪除、或者更新id=3的數據行,也就保證了數據的一致性,所以主鍵索引由於他的唯一性的原因,是不需要加間隙鎖的。
  2. 范圍查詢會加上間隙鎖
  3. 是用不存在的檢索條件,會使用間隙鎖

死鎖

  死鎖在 InnoDB 中才會出現死鎖,MyISAM 是不會出現死鎖,因為 MyISAM支持的是表鎖,一次性獲取了所有的鎖,其它的線程只能排隊等候。而 InnoDB 默認支持行鎖,獲取鎖是分步的,並不是一次性獲取所有的鎖,因此在鎖競爭的時候就會出現死鎖的情況。雖然 InnoDB 會出現死鎖,但是並不影響 InnoDB 成為最受歡迎的存儲引擎,MyISAM 可以理解為串行化操作,讀寫有序,因此支持的並發性能低下。

當一個事務開始並且update一條id=1的數據行時,成功獲取到寫鎖,此時另一個事務執行也update另一條id=2的數據行時,也成功獲取到寫鎖(id為主鍵)。
此時cpu將時間分配給了事務一,事務一接着也是update id=2的數據行,因為事務二已經獲取到id=2數據行的鎖,所以事務已處於等待狀態。
事務二有獲取到了時間,像執行update id=1的數據行,但是此時id=1的鎖被事務一獲取到了,事務二也處於等待的狀態,因此形成了死鎖。
死鎖案例

 

死鎖的解決方案

  要解決死鎖問題,在程序的設計上,當發現程序有高並發的訪問某一個表時,盡量對該表的執行操作串行化,或者鎖升級,一次性獲取所有的鎖資源。也可以設置參數innodb_lock_wait_timeout,超時時間,並且將參數innodb_deadlock_detect 打開,當發現死鎖的時候,自動回滾其中的某一個事務。

      

總結

  MyISAM的表鎖分為兩種模式:「共享讀鎖」「排它寫鎖」。獲取的讀鎖的線程對該數據行只能讀,不能修改,其它線程也只能對該數據行加讀鎖。獲取到寫鎖的線程對該數據行既能讀也能寫,對其他線程對該數據行的讀寫具有排它性。

  MyISAM中默認寫優先於去操作,因此MyISAM一般不適合運用於大量讀寫操作的程序中。

  InnoDB的行鎖雖然會出現死鎖的可能,但是InnoDB的支持的並發性能比MyISAM好,行鎖的粒度最小,一定的方法和措施可以解決死鎖的發生,極大的發揮InnoDB的性能。

  InnoDB中引入了間隙鎖的概念來決解出現幻讀的問題,也引入事務的特性,通過事務的四種隔離級別,來降低鎖沖突,提高並發性能。

 

 

 

                                         


免責聲明!

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



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