1. 程式人生 > >Mysql學習-06 MySql鎖機制

Mysql學習-06 MySql鎖機制

鎖定義:

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


鎖的分類:

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

  2. 從對資料操作的粒度分 (表鎖/行鎖)

          為了儘可能提高資料庫的併發度,每次鎖定的資料範圍越小越好,理論上每次只鎖定當前操作的資料的方案會得到最大的併發度,但是管理鎖是很耗資源的事情(涉及獲取,檢查,釋放鎖等動作),因此資料庫系統需要在高併發響應和系統性能兩方面進行平衡,這樣就產生了“鎖粒度(Lock granularity)”的概念。
          一種提高共享資源併發發性的方式是讓鎖定物件更有選擇性。儘量只鎖定需要修改的部分資料,而不是所有的資源。更理想的方式是,只對會修改的資料片進行精確的鎖定。任何時候,在給定的資源上,鎖定的資料量越少,則系統的併發程度越高,只要相互之間不發生衝突即可。
 
表鎖(偏讀):

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

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

     

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


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

    1.由於行鎖支援事務

          事務是由一組SQL語句組成的邏輯處理單元,事務具有以下4個屬性,通常簡稱為事務的ACID屬性。 
          l 原子性(Atomicity):事務是一個原子操作單元,其對資料的修改,要麼全都執行,要麼全都不執行。 
          l 一致性(Consistent):在事務開始和完成時,資料都必須保持一致狀態。這意味著所有相關的資料規則都必須應用於事務的修改,以保持資料的完整性;事務結束時,所有的內部資料結構(如B樹索引或雙向連結串列)也都必須是正確的。 
          l 隔離性(Isolation):資料庫系統提供一定的隔離機制,保證事務在不受外部併發操作影響的“獨立”環境執行。這意味著事務處理過程中的中間狀態對外部是不可見的,反之亦然。 
          l 永續性(Durable):事務完成之後,它對於資料的修改是永久性的,即使出現系統故障也能夠保持。 

       2.併發事務處理帶來的問題 

         更新丟失(Lost Update)

 
當兩個或多個事務選擇同一行,然後基於最初選定的值更新該行時,由於每個事務都不知道其他事務的存在,
就會發生丟失更新問題--最後的更新覆蓋了由其他事務所做的更新。
 
例如,兩個程式設計師修改同一java檔案。每程式設計師獨立地更改其副本,然後儲存更改後的副本,這樣就覆蓋
了原始文件。最後儲存其更改副本的編輯人員覆蓋前一個程式設計師所做的更改。
 
 
 
如果在一個程式設計師完成並提交事務之前,另一個程式設計師不能訪問同一檔案,則可避免此問題。 

        髒讀(Dirty Reads)

 
 一個事務正在對一條記錄做修改,在這個事務完成並提交前,這條記錄的資料就處於不一致狀態;這時,
另一個事務也來讀取同一條記錄,如果不加控制,第二個事務讀取了這些“髒”資料,並據此做進一步的處
理,就會產生未提交的資料依賴關係。這種現象被形象地叫做”髒讀”。 
 
 
一句話:事務A讀取到了事務B已修改但尚未提交的的資料,還在這個資料基礎上做了操作。此時,如果B
事務回滾,A讀取
的資料無效,不符合一致性要求。

       不可重複讀(Non-Repeatable Reads)

  在一個事務內,多次讀同一個資料。在這個事務還沒有結束時,另一個事務也訪問該同一資料。
那麼,在第一個事務的兩次讀資料之間。由於第二個事務的修改,那麼第一個事務讀到的資料可能不一樣,
這樣就發生了在一個事務內兩次讀到的資料是不一樣的,因此稱為不可重複讀,即原始讀取不可重複。
 
  一句話:一個事務範圍內兩個相同的查詢卻返回了不同資料。
 

       幻讀(Phantom Reads)

  一個事務按相同的查詢條件重新讀取以前檢索過的資料,卻發現其他事務插入了滿足其查詢條件的
新資料,這種現象就稱為“幻讀”。
 
一句話:事務A 讀取到了事務B提交的新增資料,不符合隔離性。 
 

      3.事務隔離級別

          髒讀”、“不可重複讀”和“幻讀”,其實都是資料庫讀一致性問題,必須由資料庫提供一定的事務隔離機制來解決。 

         

        資料庫的事務隔離越嚴格,併發副作用越小,但付出的代價也就越大,因為事務隔離實質上就是使事務在一定程度上 “序列化”進行,這顯然與“併發”是矛盾的。同時,不同的應用對讀一致性和事務隔離程度的要求也是不同的,比如許多應用對“不可重複讀”和“幻讀”並不敏感,可能更關心資料併發訪問的能力。 
       常看當前資料庫的事務隔離級別:show variables like 'tx_isolation'; 

讀鎖:

共享鎖(Share Lock)
        共享鎖又稱讀鎖,是讀取操作建立的鎖。其他使用者可以併發讀取資料,但任何事務都不能對資料進行修改(獲取資料上的排他鎖),直到已釋放所有共享鎖。
如果事務T對資料A加上共享鎖後,則其他事務只能對A再加共享鎖,不能加排他鎖。獲准共享鎖的事務只能讀資料,不能修改資料。
用法 -->SELECT ... LOCK IN SHARE MODE; 
        在查詢語句後面增加 LOCK IN SHARE MODE ,Mysql會對查詢結果中的每行都加共享鎖,當沒有其他執行緒對查詢結果集中的任何一行使用排他鎖時,可以成功申請共享鎖,否則會被阻塞。其他執行緒也可以讀取使用了共享鎖的表,而且這些執行緒讀取的是同一個版本的資料。

寫鎖:

排他鎖(eXclusive Lock)
      共享鎖又稱寫鎖,如果事務T對資料A加上排他鎖後,則其他事務不能再對A加任任何型別的封鎖。獲准排他鎖的事務既能讀資料,又能修改資料。
 
用法
       SELECT ... FOR UPDATE;
       在查詢語句後面增加 FOR UPDATE ,Mysql會對查詢結果中的每行都加排他鎖,當沒有其他執行緒對查詢結果集中的任何一行使用排他鎖時,可以成功申請排他鎖,否則會被阻塞。
無索引行鎖升級為表鎖:


間隙鎖危害:

    間隙鎖帶來的插入問題

【什麼是間隙鎖】
當我們用範圍條件而不是相等條件檢索資料,並請求共享或排他鎖時,InnoDB會給符合條件的已有資料記錄的索引項加鎖;對於鍵值在條件範圍內但並不存在的記錄,叫做“間隙(GAP)”,
InnoDB也會對這個“間隙”加鎖,這種鎖機制就是所謂的間隙鎖(GAP Lock)。
【危害】
因為Query執行過程中通過過範圍查詢的話,他會鎖定整個範圍內所有的索引鍵值,即使這個鍵值並不存在。
間隙鎖有一個比較致命的弱點,就是當鎖定一個範圍鍵值之後,即使某些不存在的鍵值也會被無辜的鎖定,而造成在鎖定的時候無法插入鎖定鍵值範圍內的任何資料。在某些場景下這可能會對效能造成很大的危害

結論:

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

【如何分析行鎖定】

    通過檢查InnoDB_row_lock狀態變數來分析系統上的行鎖的爭奪情況
   mysql>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(等待總時長)這三項。
        尤其是當等待次數很高,而且每次等待時長也不小的時候,我們就需要分析系統中為什麼會有如此多的等待,然後根據分析結果著手指定優化計劃。
最後可以通過
 SELECT * FROM information_schema.INNODB_TRX;
來查詢正在被鎖阻塞的sql語句

優化建議:
  1、不要三心二意,鎖住哪行,就改哪行
  2、要說話算數,說讀鎖就幹讀的事,說寫鎖就幹寫的事
  3、不要拖拖拉拉,用了鎖,就趕緊辦事,辦完事趕緊提交
  4、如果鎖,請輕鎖,一鎖一大片,中間再也無法容納他人了。(間隙鎖的問題)
  5、一人負你,全天下都恨。(索引失效會引起行鎖變表鎖)。
  6、用情越深,付出越大。(隔離級別越高,鎖成本越高)。