理解事務(wù)的隔離特性
臟讀、不可重復(fù)讀簇爆、幻讀
- 臟讀:讀到別的事務(wù)還未提交的修改
- 不可重復(fù)讀:讀到別的事務(wù)已提交的修改,即在同一事務(wù)中多次查詢結(jié)果不同
- 幻讀:由于未讀到其他事務(wù)新插入或刪除的數(shù)據(jù)導(dǎo)致后續(xù)插入或刪除操作出錯,即之前的查詢結(jié)果是不真實的
臟讀和不可重復(fù)讀都針對的是現(xiàn)有數(shù)據(jù)被修改的情況怠硼,幻讀針對的是插入或刪除新數(shù)據(jù)的情況。
事務(wù)的隔離級別
在 SQL 標(biāo)準(zhǔn)中定義了四種數(shù)據(jù)庫的事務(wù)的隔離級別:READ UNCOMMITED
移怯、READ COMMITED
香璃、REPEATABLE READ
和 SERIALIZABLE
。不同級別的特性如下:
級別 | 臟讀 | 不可重復(fù)讀 | 幻讀 |
---|---|---|---|
READ UNCOMMITED | 可能 | 可能 | 可能 |
READ COMMITED | 不可能 | 可能 | 可能 |
REPEATABLE READ | 不可能 | 不可能 | 可能 |
SERIALIZABLE | 不可能 | 不可能 | 不可能 |
默認(rèn)的隔離級別為REPEATABLE READ
舟误。
事務(wù)的隔離級別針對的是客戶端葡秒,是對當(dāng)前客戶端的約束,限制當(dāng)前客戶端的讀操作嵌溢,即
- SERIALIZABLE級別強制當(dāng)前客戶端發(fā)起的寫事務(wù)等待其他客戶端的寫事務(wù)提交后再執(zhí)行
- REPEATABLE READ級別限制當(dāng)前客戶端不能讀到其他客戶端的事務(wù)的修改
- READ COMMITED級別限制當(dāng)前客戶端不能讀到其他客戶端的事務(wù)未提交的修改
- READ UNCOMMITED級別不對當(dāng)前客戶端的讀事務(wù)進行限制
一個栗子
以如下的 score 表為例:
id | student_id | subject_id | score |
---|---|---|---|
1 | 1 | 1001 | 90 |
2 | 1 | 1002 | 80 |
3 | 2 | 1001 | 60 |
4 | 2 | 1002 | 60.5 |
運行兩個并行的事務(wù):事務(wù)A和事務(wù)B眯牧。
其中,事務(wù)A執(zhí)行對score表中的score列的修改操作:
-- 事務(wù)A
BEGIN;
SELECT * FROM score;
UPDATE
score
SET
score = score + 5;
SELECT * FROM score;
事務(wù)B執(zhí)行對score表的查詢操作:
-- 事務(wù)B
BEGIN;
SELECT * FROM score;
隔離級別為 REPEATABLE READ 時
在事務(wù)A還未提交修改時赖草,事務(wù)B查詢score表時学少,查詢不到事務(wù)A執(zhí)行的修改:
即沒有發(fā)生臟讀現(xiàn)象。
當(dāng)事務(wù)A提交(COMMIT
)上述修改后秧骑,事務(wù)B查詢score表的結(jié)果如下:
同樣查詢不到事務(wù)A執(zhí)行的修改旱易,即沒有發(fā)生不可重復(fù)讀現(xiàn)象。
因此在REPEATABLE READ
隔離等級下腿堤,查詢到的是表的歷史版本阀坏。
如果在事務(wù)A提交之前想在事務(wù)B中修改表中的數(shù)據(jù),如:
UPDATE
score
SET
score = score - 5;
MySQL在運行一段時間后會如下錯誤:
這是因為事務(wù)B在等待事務(wù)A先提交所做的修改笆檀,并且因為等待超時報錯忌堂。
這說明事務(wù)在執(zhí)行過程中禁止當(dāng)其他事務(wù)存在未提交修改時同時修改數(shù)據(jù)。
如果事務(wù)A在原表中插入一條數(shù)據(jù)酗洒,例如:
INSERT INTO score
VALUES
(5, 3, 1001, 85);
無論事務(wù)A是否提交該數(shù)據(jù)士修,事務(wù)B的查詢結(jié)果均為:
即事務(wù)B查詢不到新插入的數(shù)據(jù),但此時如果事務(wù)B想在表中插入同樣的數(shù)據(jù)時樱衷,運行會報錯:
即發(fā)生了幻讀現(xiàn)象棋嘲。
隔離級別為 READ COMMITED 時
在事務(wù)A還未提交修改時,事務(wù)B查詢score表的結(jié)果為:
此時矩桂,事務(wù)B查詢不到事務(wù)A未提交的修改沸移,即沒有發(fā)生臟讀現(xiàn)象。
當(dāng)事務(wù)A提交上述修改后,事務(wù)B的查詢結(jié)果為:
此時雹锣,事務(wù)B可以查詢到事務(wù)A提交后的修改网沾,即發(fā)生了不可重復(fù)讀現(xiàn)象。
如果事務(wù)A在原表中插入一條數(shù)據(jù)蕊爵,事務(wù)B查詢不到新插入的數(shù)據(jù)辉哥,但此時如果事務(wù)B想在表中插入同樣的數(shù)據(jù)時,運行會報錯:
即發(fā)生了幻讀現(xiàn)象攒射。
隔離級別為 READ UNCOMMITED 時
在事務(wù)A還未提交修改時醋旦,事務(wù)B查詢score表的結(jié)果為:
此時,事務(wù)B可以查詢到事務(wù)A未提交的修改会放,即發(fā)生了臟讀現(xiàn)象浑度。
當(dāng)事務(wù)A提交上述修改后,事務(wù)B的查詢結(jié)果為:
此時鸦概,事務(wù)B可以查詢到事務(wù)A提交后的修改,即發(fā)生了不可重復(fù)讀現(xiàn)象甩骏。
如果事務(wù)A在原表中插入一條數(shù)據(jù)窗市,事務(wù)B查詢不到新插入的數(shù)據(jù),但此時如果事務(wù)B想在表中插入同樣的數(shù)據(jù)時饮笛,運行會報錯:
即發(fā)生了幻讀現(xiàn)象咨察。
隔離級別為 SERIALIZABLE 時
在事務(wù)A還未提交修改時,事務(wù)B查詢score表的結(jié)果時福青,MySQL在運行一段時間后會報如下錯誤:
這表明事務(wù)B一直在等待事務(wù)A提交其修改直到超時摄狱。
說明了當(dāng)前的隔離級別實際上是將并行的事務(wù)強制串行執(zhí)行了。
當(dāng)事務(wù)A提交上述修改后无午,事務(wù)B查詢score表的結(jié)果如下:
同樣查詢不到事務(wù)A執(zhí)行的修改媒役,即沒有發(fā)生不可重復(fù)讀現(xiàn)象。
如果事務(wù)A在原表中插入一條數(shù)據(jù)宪迟,在事務(wù)A還未交該數(shù)據(jù)時酣衷,事務(wù)B想插入同樣的數(shù)據(jù),MySQL在運行一段時間后會報如下錯誤:
這表明事務(wù)B一直在等待事務(wù)A提交其修改直到超時次泽。
即沒有發(fā)生幻讀現(xiàn)象穿仪。