1. 程式人生 > >[MySQL高階](六) 鎖機制

[MySQL高階](六) 鎖機制

1. 概述

1.1 定義

  鎖時計算機協調多個程序或執行緒併發訪問某一資源的機制。    在資料庫中,除了傳統的計算資源(如CPU、RAM、I/O等)的爭用以外,資料也是一種供需要使用者共享的資源。如何保證資料併發訪問的一致性、有效性是所有資料庫必須解決的一個問題,鎖衝突也是影響資料庫併發訪問效能的一個重要因素。從這個角度來說,鎖對資料庫而言顯得尤其重要,也更加複雜。

1.2 應用

當然這裡是舉個例子表達意思,事實更加複雜。 

1.3 鎖的分類

  • 從對資料庫操作的型別分,分為讀鎖和寫鎖 

        讀鎖(共享鎖):針對同一份資料,多個讀操作可以同時進行而不會互相影響          寫鎖(排它鎖):當前寫操作沒有完成前,它會阻斷其他寫鎖和讀鎖

  • 從對資料操作的粒度分,分為表鎖和行鎖

2. 三鎖

2.1 表鎖(偏讀)

表鎖偏向MyISAM儲存引擎,開銷小,加鎖快,無思索,鎖定粒度大,發生鎖衝突的概率最高,併發度最低。

2.1.1 基本操作

  • 建表SQL

CREATE TABLE `mylock` (   `id` int(11) NOT NULL AUTO_INCREMENT,   `NAME` varchar(20) DEFAULT NULL,   PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8;

  • 插入資料

INSERT INTO `test`.`mylock` (`id`, `NAME`) VALUES ('1', 'a'); INSERT INTO `test`.`mylock` (`id`, `NAME`) VALUES ('2', 'b'); INSERT INTO `test`.`mylock` (`id`, `NAME`) VALUES ('3', 'c'); INSERT INTO `test`.`mylock` (`id`, `NAME`) VALUES ('4', 'd');

  • 手動增加表鎖

lock table 表名稱 read(write),表名稱2 read(write),其他;

  • 查看錶上加過的鎖

show open tables;

  • 刪除表鎖

unlock tables;

2.1.2 案例分析(加讀鎖)

2.1.3 案例分析(加寫鎖)

2.1.4 案例結論

總結:  簡而言之,就是讀鎖會阻塞寫,但是不會阻塞讀。而寫鎖則會把讀和寫都阻塞。

2.1.5 表鎖分析

  • 檢視哪些表被加鎖了

show open tables;

  • 如何分析表鎖定 可以通過檢查table_lock_waited和table_locks_immediate狀態變數來分析系統上的表鎖定

show status like 'table%';

這裡有兩個狀態變數記錄MySQL內部表級鎖定的情況,兩個變數說明如下:  Table_locks_immediate:產生表級鎖定的次數,表示可以立即獲取鎖的查詢次數,每立即獲取鎖值加1  Table_locks_waited 出現表級鎖定爭用而發生等待的次數(不能立即獲取鎖的次數,每等待一次鎖值加1),此值高則說明存在著較嚴重的表級鎖爭用情況。

  • MYISAM的讀寫鎖排程是寫優先,這也是MYISAM不適合做寫為主表的引擎。因為寫鎖後,其他執行緒不能做任何操作,大量的更新會使查詢很難得到鎖,從而造成永久阻塞。

2.2 行鎖(偏寫)

行鎖偏向InnoDB儲存引擎,開銷大,加鎖慢,會出現死鎖,鎖定粒度最小,發生鎖衝突的概率最低,併發度也最高。InnoDB與MYISAM的最大不同有兩點:一是支援事務(TRANSACTION);二是採用了行級鎖。

2.2.1 行鎖支援事務

  • 事務(Transaction)及其ACID屬性 
  • 併發事務處理帶來的問題 

✈ 更新丟失(Lost Update)    當兩個或多個事務選擇同一行,然後基於最初選定的值更新該行時,由於每個事務都不知道其他事務的存在,就會發生丟失更新問題–最後的更新覆蓋了由其他事務所做的更新。      ✈ 髒讀(Dirty Reads)    一個事務正在對一條記錄做修改,在這個事務完成並提交前,這條記錄的資料就處於不一致的狀態;這時,另一個事務也來讀取同一條記錄,如果不加控制,第二個事務讀取了這些“髒”資料,並據此作進一步的處理,就會產生未提交的資料依賴關係。這種現象被形象的叫做“髒讀”。    一句話:事務A讀取到了事務B已經修改但尚未提交的資料,還在這個資料基礎上做了操作。此時,如果B事務回滾,A讀取的資料無效,不符合一致性要求。      ✈ 不可重讀(Non-Repeatable Reads)    一個事務在讀取某些資料後的某個時間,再次讀取以前讀過的資料,卻發現其讀出的資料已經發生了改變、或某些記錄已經被刪除了!這種現象就叫做“不可重複讀”。    一句話:事務A讀取到了事務B已經提交的修改資料,不符合隔離性      ✈ 幻讀(Phantom Reads)    一個事務按相同的查詢條件重新讀取以前檢索過的資料,卻發現其他事務插入了滿足其查詢條件的新資料,這種現象就稱為“幻讀”。    一句話:事務A讀取到了事務B提交的新增資料,不符合隔離性

髒讀是事務B裡面修改了資料  幻讀是事務B裡面新增了資料

  • 事務隔離級別 

2.2.2 行鎖案例分析

  • 建表SQL

CREATE TABLE `test_innodb_locl` (   `a` int(11) NOT NULL,   `b` varchar(255) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `test`.`test_innodb_locl` (`a`, `b`) VALUES ('1', 'b2'); INSERT INTO `test`.`test_innodb_locl` (`a`, `b`) VALUES ('2', '3'); INSERT INTO `test`.`test_innodb_locl` (`a`, `b`) VALUES ('4', '4000'); INSERT INTO `test`.`test_innodb_locl` (`a`, `b`) VALUES ('5', '5000');

create index idx_test_innodb_a_ind on test_innodb_locl; create index idx_test_innodb_b_ind on test_innodb_locl;

  • 行鎖定基本演示 

2.2.3 間隙鎖危害

2.2.4 案例結論

  Innodb儲存引擎由於實現了行級鎖定,雖然在鎖定機制的實現方面所帶來的效能損耗可能比表級鎖定會要更高一下,但是在整體併發處理能力方面要遠遠優於MYISAM的表級鎖定的。當系統併發量高的時候,Innodb的整體效能和MYISAM相比就會有比較明顯的優勢了。    但是,Innodb的行級鎖定同樣也有其脆弱的一面,當我們使用不當的時候,可能會讓Innodb的整體效能表現不僅不能比MYISAM高,甚至可能會更差。

2.2.5 行鎖分析

通過檢查InnoDB_row_lock狀態變數來分析系統上的行鎖的爭奪情況

show status like 'innodb_row_lock%';

對各個狀態量的說明如下:  Innodb_row_lock_current_waits: 當前正在等待鎖定的數量  Innodb_row_lock_time: 從系統啟動到現在鎖定總時間長度  Innodb_row_lock_time_avg: 每次等待所花平均時間  Innodb_row_lock_time_max:從系統啟動到現在等待最長的一次所花時間  Innodb_row_lock_waits:系統啟動後到現在總共等待的次數

對於這5個狀態變數,比較重要的主要是:  Innodb_row_lock_time_avg (等待平均時長)  Innodb_row_lock_waits (等待總次數)  Innodb_row_lock_time(等待總時長)  尤其是當等待次數很高,而且每次等待時長也不小的時候,我們就需要分析系統中為什麼會有如此多的等待,然後根據分析結果著手製定優化計劃。

2.2.6 優化建議

  • 儘可能讓所有資料檢索都通過索引來完成,避免無索引行鎖升級為表鎖
  • 合理設計索引,儘量縮小鎖的範圍
  • 儘可能減少檢索條件,避免間隙鎖
  • 儘量控制事務大小,減少鎖定資源量和時間長度
  • 儘可能低級別事務隔離

2.3 行鎖(偏寫)

開銷和加鎖時間介於表鎖和行鎖之間,會出現死鎖,鎖定粒度介於表鎖和行鎖之間,併發度一般。 ---------------------