事務隔離級別及相關鎖實踐

這是我參與新手入門的第1篇文章數據庫

事務隔離性

事務的四個特性:原子性,一致性,隔離性,持久性。 其中原子性、一致性、持久性容易理解。今天重點來聊聊隔離性。markdown

隔離性簡單來講,就是每一個事務是相互隔離的,你作你的事,他作他的事,你們相互隔離開來。 可是,你們都是在同一個數據庫下操做,那這個「隔離」到底隔離到什麼程度呢?是「老死不相往來」,仍是」臨時隔離「,又或者是"你中有我,我中有你"?這就是涉及到了新的概念:隔離級別併發

事務隔離級別

事務隔離級別有:工具

  • 讀未提交(READ-UNCOMMITTED):最低的隔離級別,容許讀取還沒有提交的數據變動
  • 讀已提交(READ-COMMITTED):容許讀取併發事務已經提交的數據
  • 可重複讀(REPEATABLE-READ):對同一字段的屢次讀取結果都是一致的,除非數據是被自己事務本身所修改
  • 串行化(SERIALIZABLE):最高的隔離級別,徹底服從ACID的隔離級別。全部的事務依次逐個執行,這樣事務之間就徹底不可能產生干擾

MySQL默認隔離級別爲:可重複讀spa

經過實際演示,咱們能夠很好理解各隔離級別。 相關準備:設計

MySQL 版本:5.7 (可經過 select version() 命令查詢)code

表:orm

CREATE TABLE `student` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`number` varchar(20) NOT NULL COMMENT '學號',
`name` varchar(20) DEFAULT NULL COMMENT '姓名',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='學生表';
複製代碼

初始數據:索引

id	number	name
1	2021070501	張三
複製代碼

演示工具:Navicat (經過開啓多個查詢窗口,實現開啓多個事務)事務

演示一:

步驟 查詢窗口1(事務1) 查詢窗口2(事務2)
step1 執行SQL:
-- 開啓事務
BEGIN;
> OK
> 時間: 0s
UPDATE student SET name = '李四' WHERE id = 1;
> Affected rows: 1
> 時間: 0.002s
step2 BEGIN;
SELECT * FROM student WHERE id = 1;
=======結果=======
id number name
1 2021070501 張三
step3 COMMIT;
> OK
> 時間: 0.004s;
step4 SELECT * FROM student WHERE id = 1;
=======結果=======
id number name
1 2021070501 張三
step5 COMMIT;
> OK
> 時間: 0s
step6 BEGIN;
SELECT * FROM student WHERE id = 1;
=======結果=======
id number name
1 2021070501 李四

從上面演示能夠分析出:

  1. step2中的結果能夠看到name仍是「張三」,說明事務2並無讀取事務1未提交的數據,因此能夠排除「讀未提交」隔離級別
  2. 若是MySQL事務的默認隔離級別爲「讀已提交」,則step3已提交事務1,step4中的結果應爲「李四」,但實際結果仍是「張三」,這從側面印證了MySQL事務的隔離級別爲「可重複讀」(兩次讀取的結果都爲張三)

事務相關的鎖

事務之間雖然有隔離性,但若是兩個事務操做同一數據,那結果會怎麼樣呢,最終以哪一個事務執行結果爲準?沒有事務的狀況下,咱們很容易理解,哪條SQL語句最後執行,就以哪一個數據爲準。但加了事務,狀況就變得複雜起來了,SQL執行順序與事務提交順序可能不一致。好比:

事務1先執行

UPDATE student SET name = '張三1' WHERE id = 1;
複製代碼

事務2再執行

UPDATE student SET name = '張三2' WHERE id = 1;
複製代碼

而後先提交事務2,再提交事務1。那最終的結果是「張三1」仍是「張三2」呢?站在SQL執行順序角度來講,結果應該爲「張三2」;站在事務提交順序來講,結果應爲「張三1」。

那最終實際結果呢?答案是:這種狀況永遠不會發生!

咱們能夠實際操做試試: 演示二:

步驟 查詢窗口1(事務1) 查詢窗口2(事務2)
step1 BEGIN
> OK
> 時間: 0s


UPDATE student SET name = '張三1' WHERE id = 1
> Affected rows: 1
> 時間: 0.001s
step2 BEGIN
> OK
> 時間: 0s


UPDATE student SET name = '張三2' WHERE id = 1
======觀察結果=======
沒反應,查詢時間一直走
step3 COMMIT;
> OK
> 時間: 0.004s;
step4 再次打開窗口,會發現有如下結果
> Affected rows: 1
> 時間: 4.843s

從演示二咱們能夠看出,step2並不能正常執行,須要等待step3提交。這就是MYSQL的機制。

當事務1先執行UPDATE student SET name = '張三1' WHERE id = 1時便得到數據行(id=1)的鎖。事務2想執行UPDATE student SET name = '張三2' WHERE id = 1時,由於該數據行是被鎖住的,因此不能執行,須要等待事務1釋放鎖。事務1提交事務便會把鎖釋放掉。因此,不一樣事務之間修改同一數據不會出現修改順序和提交順序不一致的狀況。

按照鎖粒度,能夠把MYSQL鎖簡單歸爲:行鎖表鎖。行鎖就是鎖定行數據,表鎖是鎖定整張表數據。上面所說示例,就是行鎖。通常來講,匹配條件是主鍵或索引列時,數據的update和delete會加行鎖。若是增刪改查時匹配的條件字段不帶有索引,innodb使用的將是表級鎖。因而可知,咱們數據庫表設計中合理的索引是很是關鍵的。

有鎖的地方,必然就會出現死鎖的問題。之後有機會再聊更多的鎖及死鎖問題

相關文章
相關標籤/搜索