1. 程式人生 > >MySql-鎖

MySql-鎖

    鎖是計算機協調多個程序或純執行緒併發訪問某一資源的機制。

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

InnoDB鎖問題

    InnoDB與MyISAM的最大不同有兩點:一是支援事務(TRANSACTION);二是採用了行級鎖。
    行級鎖和表級鎖本來就有許多不同之處,另外,事務的引入也帶來了一些新問題。

併發事務處理帶來的問題

    1.更新丟失(Lost Update)
    2.髒讀(Dirty Reads) 讀未提交
    3.不可重複讀(Non-Repeatable Reads)  讀已提交
    4.幻讀(Phantom Reads)

在併發事務處理帶來的問題中,“更新丟失”通常應該是完全避免的。但防止更新丟失,並不能單靠資料庫事務控制器來解決,需要應用程式對要更新的資料加必要的鎖來解決,因此,防止更新丟失應該是應用的責任。

“髒讀”、“不可重複讀”和“幻讀”,其實都是資料庫讀一致性問題,必須由資料庫提供一定的事務隔離機制來解決。資料庫實現事務隔離的方式,基本可以分為以下兩種。
    一種是在讀取資料前,對其加鎖,阻止其他事務對資料進行修改。
    另一種是不用加任何鎖,通過一定機制生成一個數據請求時間點的一致性資料快照(Snapshot),並用這個快照來提供一定級別(語句級或事務級)的一致性讀取。從使用者的角度,好像是資料庫可以提供同一資料的多個版本,因此,這種技術叫做資料多版本併發控制(MultiVersion Concurrency Control,簡稱MVCC或MCC),也經常稱為多版本資料庫。
資料庫的事務隔離級別越嚴格,併發副作用越小,但付出的代價也就越大,因為事務隔離實質上就是使事務在一定程度上“序列化”進行,這顯然與“併發”是矛盾的,同時,不同的應用對讀一致性和事務隔離程度的要求也是不同的,比如許多應用對“不可重複讀”和“幻讀”並不敏感,可能更關心資料併發訪問的能力。
為了解決“隔離”與“併發”的矛盾,ISO/ANSI SQL92定義了4個事務隔離級別,每個級別的隔離程度不同,允許出現的副作用也不同,應用可以根據自己業務邏輯要求,通過選擇不同的隔離級別來平衡"隔離"與"併發"的矛盾
 

例子:

打個比方,我們到淘寶上買一件商品,商品只有一件庫存,這個時候如果還有另一個人買,那麼如何解決是你買到還是另一個人買到的問題?

這裡肯定要用到事物,我們先從庫存表中取出物品數量,然後插入訂單,付款後插入付款表資訊,然後更新商品數量。在這個過程中,使用鎖可以對有限的資源進行保護,解決隔離和併發的矛盾

鎖的分類

從對資料操作的型別(讀\寫)角度來分

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

從對資料操作的粒度分

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

    表鎖:偏向InnoDB儲存引擎,開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的概率最低,併發度也最高。

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

表鎖例子(MyISAM引擎)

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

【表級鎖分析--建表SQL】

create table mylock(
 id int not null primary key auto_increment,
 name varchar(20)
)engine myisam;

insert into mylock(name) values('a');
insert into mylock(name) values('b');
insert into mylock(name) values('c');
insert into mylock(name) values('d');
insert into mylock(name) values('e');

【手動增加表鎖】
      lock table tableName1 read(write),tableName2 read(write),其它;
【查看錶上加過的鎖】
      show open tables;

      

【釋放表鎖】
      unlock tables;

我們為mylock表加read鎖(讀阻塞寫例子)

我們為mylock表加write鎖(MyISAM儲存引擎的寫阻塞讀例子)

MyISAM在執行查詢語句(SELECT)前,會自動給涉及的所有表加讀鎖,在執行增刪改操作前,會自動給涉及的表加寫鎖。 
MySQL的表級鎖有兩種模式:
   表共享讀鎖(Table Read Lock)
   表獨佔寫鎖(Table Write Lock)

結論: 結合上表,所以對MyISAM表進行操作,會有以下情況: 
  1、對MyISAM表的讀操作(加讀鎖),不會阻塞其他程序對同一表的讀請求,但會阻塞對同一表的寫請求。只有當讀鎖釋放後,才會執行其它程序的寫操作。 
  2、對MyISAM表的寫操作(加寫鎖),會阻塞其他程序對同一表的讀和寫操作,只有當寫鎖釋放後,才會執行其它程序的讀寫操作。
 簡而言之,就是讀鎖會阻塞寫,但是不會堵塞讀。而寫鎖則會把讀和寫都堵塞。

行鎖例子(InnoDB引擎)

create table test_innodb_lock (a int(11),b varchar(16))engine=innodb;

insert into test_innodb_lock values(1,'b2');
insert into test_innodb_lock values(3,'3');
insert into test_innodb_lock values(4,'4000');
insert into test_innodb_lock values(5,'5000');
insert into test_innodb_lock values(6,'6000');
insert into test_innodb_lock values(7,'7000');
insert into test_innodb_lock values(8,'8000');
insert into test_innodb_lock values(9,'9000');
insert into test_innodb_lock values(1,'b1');

--建立索引
create index test_innodb_a_ind on test_innodb_lock(a);
create index test_innodb_lock_b_ind on test_innodb_lock(b);

 session1                                                                                    session2

索引失效導致行鎖變表鎖(如果沒有索引,所以update會鎖表,如果加了索引,就會鎖行,但是索引失效也會鎖表)

間隙鎖

 

SELECT * from test_innodb_lock WHERE a = 1 FOR UPDATE;

 

 

表鎖分析

【看看哪些表被加鎖了】
       mysql>show open tables;

【如何分析表鎖定】可以通過檢查table_locks_waited和table_locks_immediate狀態變數來分析系統上的表鎖定:
       show status like 'table%';

       

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

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

行鎖分析

【如何分析行鎖定】通過檢查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(等待總時長)這三項。
     尤其是當等待次數很高,而且每次等待時長也不小的時候,我們就需要分析系統中為什麼會有如此多的等待,然後根據分析結果著手指定優化計劃。
 

優化建議

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