日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

從事的隔離級別談起

眾所周知,事務有四大特性,簡稱ACID:原子性、一致性、隔離性、持久性。

對于隔離性,簡單來說就是多個事務之間是彼此隔離的,互不影響。但想要做到完全的互不影響是很難的,因為數據的強一致性,很多時候需要犧牲性能去達成。比如如果我們能接受事務的串行執行,那一定是互不影響的。然而現實是,MySQL作為一個數據庫,必然是要支持一定程度的并行執行的,也就是多個事務同時去執行。

?

凡并行程序,往往是在性能和數據一致性上做取舍。較好的解決方案要么是最終一致,要么是盡量縮小串行執行的范圍。

?

如果多個事務同時并行執行,在沒有隔離的情況,可能會發生臟讀、不可重復讀、幻讀的問題。

案例數據(demo表):

id(主鍵) c(普通索引) d(無索引) 5 5 5 10 10 10 15 15 15 20 20 20 25 25 25

「臟讀」

一個事務讀取了另一個事務未提交的數據。

InnoDB的行鎖,原來為你做了這么多

 

臟讀

「不可重復讀」

一個事務讀取同一行數據,多次讀取結果不同。

InnoDB的行鎖,原來為你做了這么多

 

不可重復讀

「幻讀」

一個事務讀取到了別的事務插入的數據。

InnoDB的行鎖,原來為你做了這么多

 

幻讀

但InnoDB因為使用了MVCC,讀取的是“快照”版本,有一些不同,但如果不上鎖,同樣可能會有幻讀問題。

InnoDB的行鎖,原來為你做了這么多

 

InnoDB的幻讀

事務用了四種不同的隔離級別用來解決這些問題。

  • Read uncommitted(未提交讀)
  • Read Committed(已提交讀,簡稱RC)
  • Repeatable Reads(可重復讀,簡稱RR)
  • Serializable(串行化)

隔離級別越高,解決的問題越多,但并發性能也會越差。它們之間的關系如下表:

隔離級別 臟讀 不可重復讀 幻讀 Read uncommitted 是 是 是 Read Committed 是 是 Repeatable Reads 是 Serializable

?

但InnoDB有些許不同,InnoDB默認的隔離級別是RR,但是通過MVCC和間隙鎖來一定程度上的解決了幻讀的問題。這也是我們今天這篇文章后面會詳細介紹的。

?

無鎖思想:MVCC

MVCC即“多版本并發控制”,但是它在很多情況下避免了加鎖操作,因此開銷更低。

主流的關系型數據庫都實現了MVCC,但實現機制各有不同。實際上MVCC也沒有一個統一的標準。但大都實現了非阻塞的讀操作,寫操作也只是鎖定必要的行。本文以下內容所說的MVCC都指的是InnoDB實現的MVCC。

在Mysql的InnoDB引擎,是通過給每行記錄后面保存兩個隱藏的列來實現的。一個是保存行的創建時間,另一個保存了行的過期時間(或刪除時間)。

?

實際上存儲的并不是實際的一個時間戳,而是“系統版本號”。

?

每次開啟一個事務,系統版本號都會遞增。事務開始時,系統版本號會作為事務的版本號,用來和查詢到的行的版本號進行比較。

MVCC只在REPEATABLE READ和READ COMMITTED兩個隔離級別下工作,其它兩個隔離級別不能工作。因為READ UNCOMMITTED總是讀取最新的數據行,而不是符合當前事務版本的數據行。而SERIALIZABLE則會對所有讀取的行都加鎖。

在MySQL中,正常的SELECT語句,后面不加FOR UPDATE和LOCK IN SHARE MODE的,就是用的MVCC去讀。

?

MVCC和我們在應用層面去實現的“樂觀鎖”有一樣的思想:用版本號,在盡量無鎖的情況下實現一定程度的一致性。

?

InnoDB行鎖的概念

InnoDB的行鎖(也稱為臨鍵鎖) Next-Key Locks,「是MySQL對外暴露的鎖的基本單位,它會智能選擇記錄鎖或間隙鎖,鎖住一行或多行或一個間隙」。而記錄鎖又分為共享鎖和排他鎖,間隙鎖的概念下面有一個插入意向鎖。這些鎖的關系大概是這樣:

InnoDB的行鎖,原來為你做了這么多

 

鎖的關系

記錄鎖

所謂記錄鎖 Record Locks,就是鎖住確定的一行行記錄。它分為共享鎖和排它鎖。分別對應不同的SQL寫法。

共享鎖

共享鎖 Shared Locks ,簡稱S鎖。使用以下SQL可能觸發:

SELECT ... LOCK IN SHARE MODE

之所以說“可能”觸發,是因為它查到了數據庫有確定的記錄才會鎖住這些記錄,否則會變成間隙鎖。這個其實很好理解,找到了數據,才鎖它。如果沒找到數據,就鎖這個間隙。

排他鎖

排他鎖 Exclusive locks ,簡稱X鎖。使用一下SQL可能觸發:

SELECT ... FOR UPDATE

這里的“可能”含義與上面同理,不贅述。

間隙鎖

間隙鎖 Gap - Lock,顧名思義,鎖住一個間隙。上文我們提到過,InnoDB默認的隔離級別是RR,但是通過間隙鎖來一定程度上的解決了幻讀的問題。它是怎么解決的呢?就是通過間隙鎖來解決的。

上面兩種SQL,如果沒有查找到確定的記錄,就會根據條件去鎖住一個間隙。間隙鎖是根據已有數據的一個左開右閉的區間。

還是這個案例數據(假設數據都是從1開始):

id(主鍵) c(普通索引) d(無索引) 5 5 5 10 10 10 15 15 15 20 20 20 25 25 25

對于下面這些區間的操作,會有對應的間隙鎖:(0, 5], (5, 10], (10, 15], (15, 20], (20, 25], (25, 正無窮)。

什么意思呢?假如你的SQL查詢的范圍不同,那它鎖住的區間就不同。比如:

-- 鎖住(0, 5]
SELECT * FROM demo where id = 3;
-- 鎖住(10, 15]
SELECT * FROM demo where id = 11;

間隙鎖其實是“共享”的。也就是說,多個事務可以獲取同一個區間的間隙鎖。

InnoDB的行鎖,原來為你做了這么多

 

間隙鎖不互相阻塞

插入意向鎖

插入意向鎖 Insert Intention Locks,代表當前事務準備插入一行數據。使用INSERT/UPDATE/DELETE等語句會獲得插入意向鎖。

「插入意向鎖和插入意向鎖之間是兼容的,只要插入的鍵值不同,就不會相互阻塞」。比如以下兩個SQL,在不同的事務中,哪怕它們在同一個間隙,只要沒有間隙鎖,就不會阻塞:

InnoDB的行鎖,原來為你做了這么多

 

插入兩條不同的記錄不阻塞

但如果兩個事務插入同一個key,那就會阻塞。

InnoDB的行鎖,原來為你做了這么多

 

兩個插入意向鎖阻塞的情況

插入意向鎖可以保證兩個事務插入key不同的數據的時候不沖突,提升并發性。

「但是間隙鎖會阻塞插入意向鎖」!這也可以理解,因為InnoDB想在RR隔離級別就解決幻讀問題。所以A事務用SELECT語句獲取了一個間隙鎖,自然不希望B事務在這個期間往這個間隙插入一條新的記錄。

InnoDB的行鎖,原來為你做了這么多

 

間隙鎖阻塞插入意向鎖

與索引的關系

不管哪種行級鎖,「行級鎖的其實都是索引」。所以在上面的demo中,如果對id(主鍵)或者column c(普通索引)操作,都會觸發相應的行級鎖,但如果對column d(無索引)做同樣的操作,InnoDB就會對表中所有數據加鎖,實際效果跟表級鎖一樣。

所以一定要注意,如果要上鎖,需要注意是否走了索引,不要弄成了表級鎖造成安全事故。

一個死鎖案例

最后給一個關于間隙鎖和插入意向鎖的死鎖案例吧,也是之前在項目上遇到過的真實案例。

過程

  • 事務A select ... for update 查找一個不存在的數據,獲得間隙鎖;
  • 事務B select ... for update 查找一個不存在的數據,獲得相同位置的間隙鎖;
  • 事務A insert into ... 到這個間隙,嘗試獲得插入意向鎖,但被事務B持有的間隙鎖阻塞。
  • 事務B insert into ... 到這個間隙,嘗試獲得插入意向鎖,但被事務A持有的間隙鎖阻塞,MySQL監測到死鎖,回滾事務B;
  • 因為事務B回滾,所以事務B持有的間隙鎖被釋放,所以之前事務A的插入語句不再阻塞,順利執行插入操作。

?

報錯信息:Deadlock found when trying to get lock; try restarting transaction

?

用圖把這個過程描述出來:

InnoDB的行鎖,原來為你做了這么多

 

死鎖過程

最終結果

事務A插入成功,事務B因為死鎖被回滾。

總結

間隙鎖主要還是用于防止幻讀的情況,所以多個事務能夠同時獲取同一段間隙鎖本身并沒有問題,間隙鎖能夠阻塞插入意向鎖也并沒有問題。

而插入意向鎖可以看成是一種特殊的間隙鎖,是用于在同一個間隙,插入不同的數據,不會互相阻塞。它比普通間隙鎖的數據一致性更低,但并發性能更好;

解決思路

普通間隙鎖還是主要用于讀操作防止幻讀。所以我們在想進行插入操作的時候,其實沒有必要對即將插入的間隙使用普通間隙鎖,直接使用insert語句產生的插入意向鎖就好了。

如果要保證數據的一致性,可以使用插入意向鎖配合主鍵、唯一鍵等約束。

附錄

那些在文章中用過的SQL:

-- 建表
create table demo
(
 id int unsigned not null,
 c int unsigned not null,
 d int unsigned not null,
 constraint demo_pk
  primary key (id),
 constraint idx_c
  unique (c)
);
-- 插數據
INSERT INTO demo VALUES
(5, 5, 5),
(10, 10, 10),
(15, 15, 15),
(20, 20, 20),
(25, 25, 25);
-- 禁止自動提交事務
SET AUTOCOMMIT = 0;
-- 開啟事務
BEGIN;
-- 提交事務
COMMIT;
-- 會獲取鎖的查詢
SELECT * FROM demo WHERE id < 5 FOR UPDATE ;

關于作者

我是Yasin,一個不斷精進的菜雞。

微信公眾號:編了個程

個人網站:https://yasinshaw.com

關注我的公眾號,和我一起成長~

分享到:
標簽:InnoDB 行鎖
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定