mysql知識(鎖和事務)

mysql知識

幾篇不錯的博客 link
死鎖分析
mysql總體介紹

mysql中的鎖有好幾種,按照作用範圍分爲行鎖和表鎖,gap鎖,next-key lock,intention lock(意向鎖)。按照作用性質,又分爲共享鎖和排它鎖。還有虛擬的樂觀鎖與悲觀鎖。
首先要明白,mysql的鎖其實是對索引加鎖,如果查詢語句中沒有索引,則對錶加鎖。(討論的是select for update語句)
現總結幾個簡單的規則:使用主鍵或者索引進行等值查詢的時候,如果沒有查出記錄不加鎖,否則對查出的數據加行鎖。
如果進行like查詢或者不等值值查詢,不管有沒有索引,都要加表鎖(可能是這種查詢設計的行比較多,直接加了表鎖)。
如果一條sql語句涉及了多個索引,查詢出的結果要對每個索引項都要加鎖。這時如果表有多個索引,不同的查詢走不同的索引,雖然訪問的是不同記錄,也是有可能衝突的。(實際項目中就碰到過一次)
鎖表的情況:會對所有的記錄加排它鎖,也會在記錄間加上gap鎖。此時mysql也會做一些優化,詳見[semi-consistent read原理]
更加詳細的規則,可以參考這個博客link

如果一條語句涉及多條記錄,這些記錄是分開加鎖的,即對一條記錄加鎖完成後,才處理下一條。並且一條記錄涉及到對多個索引加鎖時,也是一個索引一個索引的加。
在事務中加的鎖,

Index Condition Pushdown(ICP) 若支持ICP,則不滿足Index Filter的記錄,無需加記錄X鎖,因爲不滿足條件的記錄在存儲引擎裏就能過濾掉;

具體加不加鎖,加什麼鎖,跟數據庫當前的隔離級別也有密切的關係。

gap鎖
可重複讀隔離級別就是通過gap鎖實現的。因爲mysql的鎖定是有序的,gap鎖鎖住了位置,就能保證其他數據不能插入進來。
對於使用唯一索引鎖定行以搜索唯一行的語句,不需要使用間隙鎖定。

next-key lock
索引記錄上的下一個鍵鎖定還會影響該索引記錄之前的“間隙”。也就是說,下一個鍵鎖定是索引記錄鎖定,並在索引記錄之前的間隔上加上間隙鎖定。
默認情況下,InnoDB在REPEATABLE READ事務隔離級別下運行。在這種情況下,InnoDB使用next-key鎖進行搜索和索引掃描,這可以防止幻象行(Phantom Rows);

意向鎖
InnoDB支持多個粒度鎖定,允許行鎖和表鎖共存。爲了在多個粒度級別實現鎖定,InnoDB使用意向鎖。意向鎖是表級鎖,用於指示事務對錶中某一行的事務需要哪種類型的鎖(共享或排他)。
意向鎖定協議如下:
在事務可以獲取表中某一行的共享鎖之前,它必須先獲得一個IS鎖或更強的表。
在一個事務可以獲得一個表中某一行的排它鎖之前,它必須首先在該表上獲得一個IX鎖

意向鎖不會阻止除完整表請求之外的任何內容(例如,LOCK TABLES … WRITE)。意向鎖的主要目的是顯示某人正在鎖定一行,或者要鎖定表中的一行。

的人可能會對意向鎖的目的並不是完全的理解,我們在這裏可以舉一個例子:如果沒有意向鎖,當已經有人使用行鎖對錶中的某一行進行修改時,如果另外一個請求要對全表進行修改,
那麼就需要對所有的行是否被鎖定進行掃描,在這種情況下,效率是非常低的;不過,在引入意向鎖之後,當有人使用行鎖對錶中的某一行進行修改之前,會先爲表添加意向互斥鎖(IX),
再爲行記錄添加互斥鎖(X),在這時如果有人嘗試對全表進行修改就不需要判斷表中的每一行數據是否被加鎖了,只需要通過等待意向互斥鎖被釋放就可以了。

Two-Phase Locking
加鎖階段與解鎖階段,並且保證加鎖階段與解鎖階段不相交。MySQL爲例,來簡單看看2PL在MySQL中的實現。
在事務中加的鎖,只有在commit之後纔會統一釋放掉。
link

死鎖
死鎖的發生與否,並不在於事務中有多少條SQL語句,死鎖的關鍵在於:兩個(或以上)的Session加鎖的順序不一致。而使用本文上面提到的,分析MySQL每條SQL語句的加鎖規則,
分析出每條語句的加鎖順序,然後檢查多個併發SQL間是否存在以相反的順序加鎖的情況,就可以分析出各種潛在的死鎖情況,也可以分析出線上死鎖發生的原因。

MVCC Lock-Based CC(給予鎖的併發控制)

在MVCC併發控制中,讀操作可以分成兩類:快照讀 (snapshot read)與當前讀 (current read)。快照讀,讀取的是記錄的可見版本 (有可能是歷史版本),不用加鎖。
當前讀,讀取的是記錄的最新版本,並且當前讀返回的記錄,都會加上鎖,保證其他事務不會再併發修改這條記錄。

MySQL 就通過文章中提到的回滾日誌(undo log)實現了 MVCC。[回滾日誌並不能將數據庫物理地恢復到執行語句或者事務之前的樣子];它是邏輯日誌,當回滾日誌被使用時,它只會按照日誌邏輯地將數據庫中的修改撤銷掉看,
可以理解爲,我們在事務中使用的每一條 INSERT 都對應了一條 DELETE,每一條 UPDATE 也都對應一條相反的 UPDATE 語句。

在一個支持MVCC併發控制的系統中,哪些讀操作是快照讀?哪些操作又是當前讀呢?以MySQL InnoDB爲例:

快照讀:簡單的select操作,屬於快照讀,不加鎖。(當然,也有例外,下面會分析)
select * from table where ?;

當前讀:特殊的讀操作,插入/更新/刪除操作,屬於當前讀,需要加鎖。
select * from table where ? lock in share mode;
select * from table where ? for update;
insert into table values ();
update table set ? where ?;
delete from table where ?;
所有以上的語句,都屬於當前讀,讀取記錄的最新版本。並且,讀取之後,還需要保證其他併發事務不能修改當前記錄,對讀取記錄加鎖。其中,除了第一條語句,對讀取記錄加S鎖 (共享鎖)外,其他的操作,都加的是X鎖 (排它鎖)。

針對一條當前讀的SQL語句,InnoDB與MySQL Server的交互,是一條一條進行的,因此,加鎖也是一條一條進行的。先對一條滿足條件的記錄加鎖,返回給MySQL Server,做一些DML操作;然後在讀取下一條加鎖,直至讀取完畢

ACID(事務的四特性)

在MySQL/InnoDB中,所謂的讀不加鎖,並不適用於所有的情況,而是隔離級別相關的。Serializable隔離級別,讀不加鎖就不再成立,所有的讀操作,都是當前讀。

  1. 隔離性(Iso)

    mysql隔離級別分爲以下四種:
    read uncommited:
    read commited
    repeatable read(可能出現幻讀)
    serializable

每個隔離級別都解決了一個問題,具體如下圖所示:
隔離級別

RR級別是read commited的基礎上通過gap鎖實現的。
RR級別下通過select lock in share mode; 也能查詢到最新的數據。

  1. Durity(持久性)

持久性是通過redo log實現的。redo log記錄的是新數據的備份。在事務提交前,只要將Redo Log持久化即可,不需要將數據持久化。
當系統崩潰時,雖然數據沒有持久化,但是RedoLog已經持久化。系統可以根據RedoLog的內容,將所有數據恢復到最新的狀態。

  1. Atomic(原子性)

通過undo log實現。

執行計劃
索引

索引分爲聚簇索引和非聚簇索引,innodb只能有一個聚簇索引。聚簇索引採用b+樹實現。一般數據庫b樹索引中的葉子結點存放的是包含數據的頁號,還要把數據頁加載到內存,然後解析出來。
現在b+樹直接把行數據放在了葉子結點裏,可直接得到數據。聚簇索引即是索引又是數據。

2、Hash索引

InnoDB中自適應哈希索引使用的是散列表的數據結構,並且DBA無法干預。

redo log、undo log

link

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章