mysql 的鎖

http://www.javashuo.com/article/p-msbhlrah-hv.htmlhtml

  • 好好看看

MySQL大體可概括爲如下3種鎖:數據庫

  • 表級鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖衝突的機率最高,併發度最低。
  • 行級鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的機率最低,併發度也最高。
  • 頁面鎖:開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表鎖和行鎖之間,併發度通常

MySQL表級鎖的鎖模式(MyISAM)

  • MySQL表級鎖有兩種模式:表共享鎖(Table Read Lock)和表獨佔寫鎖(Table Write Lock)。
  • LOCK tables orders read local,order_detail read local;併發

    SELECT SUM(total) FROM orders;spa

    SELECT SUM(subtotal) FROM order_detail;線程

    Unlock tables;code

 MyISAM存儲引擎有一個系統變量concurrent_insert,專門用以控制其併發插入的行爲,其值分別能夠爲0、1或2。htm

  • 當concurrent_insert設置爲0時,不容許併發插入。
  • 當concurrent_insert設置爲1時,若是MyISAM容許在一個讀表的同時,另外一個進程從表尾插入記錄。這也是MySQL的默認設置。
  • 當concurrent_insert設置爲2時,不管MyISAM表中有沒有空洞,都容許在表尾插入記錄,都容許在表尾併發插入記錄。

MyISAM的鎖調度blog

  • 寫請求後到,寫鎖也會插到讀請求以前!
    • 這是由於MySQL認爲寫請求通常比讀請求重要。

InnoDB鎖問題

  •  InnoDB與MyISAM的最大不一樣有兩點:
    • 一是支持事務(TRANSACTION);
    • 二是採用了行級鎖。

事務(Transaction)及其ACID屬性

併發事務帶來的問題排序

  • 更新丟失(Lost Update):
  • 髒讀(Dirty Reads):
  • 不可重複讀(Non-Repeatable Reads):
    • 一個事務在讀取某些數據已經發生了改變、或某些記錄已經被刪除了!這種現象叫作「不可重複讀」。
  • 幻讀(Phantom Reads):
    • 一個事務按相同的查詢條件從新讀取之前檢索過的數據,
    • 卻發現其餘事務插入了知足其查詢條件的新數據,這種現象就稱爲「幻讀」。

數據庫實現事務隔離的方式,基本能夠分爲如下兩種。索引

  • 一種是在讀取數據前,對其加鎖,阻止其餘事務對數據進行修改。

  • 另外一種是不用加任何鎖,

    • 經過必定機制生成一個數據請求時間點的一致性數據快照(Snapshot),

    • 並用這個快照來提供必定級別(語句級或事務級)的一致性讀取。

隔離級別/讀數據一致性及容許的併發反作用 讀數據一致性 髒讀 不可重複讀 幻讀

未提交讀(Read uncommitted)

最低級別,只能保證不讀取物理上損壞的數據
已提交度(Read committed) 語句級
可重複讀(Repeatable read) 事務級
可序列化(Serializable) 最高級別,事務級

InnoDB實現瞭如下兩種類型的行鎖。

  • 共享鎖(s):容許一個事務去讀一行,
    • 阻止其餘事務得到相同數據集的排他鎖。
  • 排他鎖(X):容許獲取排他鎖的事務更新數據,
    • 阻止其餘事務取得相同的數據集共享讀鎖和排他寫鎖。
  • 另外,爲了容許行鎖和表鎖共存,實現多粒度鎖機制,

    • InnoDB還有兩種內部使用的意向鎖(Intention Locks),這兩種意向鎖都是表鎖。

      •  

        意向共享鎖(IS):事務打算給數據行共享鎖,

        • 事務在給一個數據行加共享鎖前必須先取得該表的IS鎖。

      • 意向排他鎖(IX):事務打算給數據行加排他鎖,

        • 事務在給一個數據行加排他鎖前必須先取得該表的IX鎖。

InnoDB行鎖實現方式

  •  InnoDB行鎖是經過索引上的索引項來實現的
  • 這一點MySQL與Oracle不一樣,
    • 後者是經過在數據中對相應數據行加鎖來實現的。
  • InnoDB這種行鎖實現特色意味者:
    • 只有經過索引條件檢索數據,InnoDB纔會使用行級鎖
    • 不然,InnoDB將使用表鎖!

間隙鎖(Next-Key鎖)

  • 當咱們用範圍條件而不是相等條件檢索數據,並請求共享或排他鎖時,
    • InnoDB會給符合條件的已有數據的索引項加鎖;
  • 對於鍵值在條件範圍內但並不存在的記錄,叫作「間隙(GAP)」,
    • InnoDB也會對這個「間隙」加鎖,這種鎖機制不是所謂的間隙鎖(Next-Key鎖)。

何時使用表鎖

 

  •  對於InnoDB表,在絕大部分狀況下都應該使用行級鎖,
    • 由於事務和行鎖每每是咱們之因此選擇InnoDB表的理由。
    • 但在個另特殊事務中,也能夠考慮使用表級鎖。
      • 第一種狀況是:事務須要更新大部分或所有數據,表又比較大,
        • 若是使用默認的行鎖,不只這個事務執行效率低,
        • 並且可能形成其餘事務長時間鎖等待和鎖衝突,
        • 這種狀況下能夠考慮使用表鎖來提升該事務的執行速度。
      • 第二種狀況是:事務涉及多個表,比較複雜,極可能引發死鎖,形成大量事務回滾。
        • 這種狀況也能夠考慮一次性鎖定事務涉及的表,
        • 從而避免死鎖、減小數據庫因事務回滾帶來的開銷。
  • 固然,應用中這兩種事務不能太多,不然,就應該考慮使用MyISAM表。
  • 在InnoDB下 ,使用表鎖要注意如下兩點。
    • (1)使用LOCK TALBES雖然能夠給InnoDB加表級鎖,
      • 但必須說明的是,表鎖不是由InnoDB存儲引擎層管理的,
      • 而是由其上一層MySQL Server負責的
      • 僅當autocommit=0、innodb_table_lock=1(默認設置)時,
        • InnoDB層才能知道MySQL加的表鎖,MySQL Server才能感知InnoDB加的行鎖,
        • 這種狀況下,InnoDB才能自動識別涉及表級鎖的死鎖;
        • 不然,InnoDB將沒法自動檢測並處理這種死鎖。
    •  (2)在用LOCAK TABLES對InnoDB鎖時要注意,要將AUTOCOMMIT設爲0,不然MySQL不會給表加鎖;
      • 事務結束前,不要用UNLOCAK TABLES釋放表鎖,由於UNLOCK TABLES會隱含地提交事務;
      • COMMIT或ROLLBACK產不能釋放用LOCAK TABLES加的表級鎖,必須用UNLOCK TABLES釋放表鎖。
        • SET AUTOCOMMIT=0;

          LOCAK TABLES t1 WRITE, t2 READ, ...;

          [do something with tables t1 and here];

          COMMIT;

          UNLOCK TABLES;

關於死鎖

  • MyISAM表鎖是deadlock free的
  • 可是在InnoDB中,除單個SQL組成的事務外,鎖是逐步得到的,這就決定了InnoDB發生死鎖是可能的。
    •  發生死鎖後,InnoDB通常都能自動檢測到,並使一個事務釋放鎖並退回,另外一個事務得到鎖,繼續完成事務。
    • 但在涉及外部鎖,或涉及鎖的狀況下,InnoDB並不能徹底自動檢測到死鎖,
      • 這須要經過設置鎖等待超時參數innodb_lock_wait_timeout來解決。

下面就經過實例來介紹幾種死鎖的經常使用方法。

  • (1)在應用中,若是不一樣的程序會併發存取多個表,
    • 應儘可能約定以相同的順序爲訪問表,這樣能夠大大下降產生死鎖的機會。
  • (2)在程序以批量方式處理數據的時候,
    • 若是事先對數據排序,保證每一個線程按固定的順序來處理記錄,也能夠大大下降死鎖的可能。
  • (3)在事務中,若是要更新記錄,應該直接申請足夠級別的鎖,即排他鎖,
    • 而不該該先申請共享鎖,更新時再申請排他鎖,甚至死鎖。
  •  (4)在REPEATEABLE-READ隔離級別下,
    • 若是兩個線程同時對相同條件記錄用SELECT...ROR UPDATE加排他鎖,
      • 在沒有符合該記錄狀況下,兩個線程都會加鎖成功。
    • 程序發現記錄尚不存在,就試圖插入一條新記錄,若是兩個線程都這麼作,就會出現死鎖。
    • 這種狀況下,將隔離級別改爲READ COMMITTED,就能夠避免問題。
  • (5)當隔離級別爲READ COMMITED時,
    • 若是兩個線程都先執行SELECT...FOR UPDATE,判斷是否存在符合條件的記錄,若是沒有,就插入記錄。
    • 此時,只有一個線程能插入成功,另外一個線程會出現鎖等待,
      • 當第1個線程提交後,第2個線程會因主鍵重出錯,但雖然這個線程出錯了,卻會得到一個排他鎖!
      • 這時若是有第3個線程又來申請排他鎖,也會出現死鎖。
      • 對於這種狀況,能夠直接作插入操做,而後再捕獲主鍵重異常,或者在遇到主鍵重錯誤時,老是執行ROLLBACK釋放得到的排他鎖。
相關文章
相關標籤/搜索