中文字幕在线观看,亚洲а∨天堂久久精品9966,亚洲成a人片在线观看你懂的,亚洲av成人片无码网站,亚洲国产精品无码久久久五月天

MYSQL事務(wù)隔離級別

2018-07-31    來源:importnew

容器云強(qiáng)勢上線!快速搭建集群,上萬Linux鏡像隨意使用

本文會根據(jù)實(shí)際工作中碰到的例子,梳理清楚數(shù)據(jù)庫事務(wù)的隔離級別。內(nèi)容很簡單,如果你能靜下心來看完,一定會對你理解隔離級別有很大的幫助。

想象一個(gè)場景。抽獎,如果用戶中獎了,一般有如下幾個(gè)流程:

  • 扣減獎品數(shù)量;
  • 記錄用戶中獎信息;
  • 試想如果扣減獎品數(shù)量了,結(jié)果記錄用戶中獎數(shù)據(jù)的時(shí)候失敗了,那么數(shù)據(jù)就會出現(xiàn)不一致的問題。

這種場景,就可以使用事務(wù)。因?yàn)槭聞?wù)的一個(gè)特性,就是原子性:要么不做,要么全做

上述問題解決了。再想一下這樣的場景:
在抽獎前,先查詢獎品剩余數(shù)量,如果剩余數(shù)量<1,則任務(wù)抽獎活動已經(jīng)結(jié)束,不再進(jìn)行抽獎。如果事務(wù)A扣減獎品數(shù)量但未提交,事務(wù)B查詢剩余獎品數(shù)量,此時(shí)應(yīng)該是多少呢?這就和事務(wù)的隔離級別有關(guān)系了。

在討論隔離級別前,我們先做一些數(shù)據(jù)庫的初始化操作:

建表:

CREATE TABLE `Tran_test` (
  `id` bigint(20) NOT NULL,
  `userId` bigint(20) NOT NULL DEFAULT '0',
  `weChatId` varchar(50) NOT NULL DEFAULT '' COMMENT '微信id(openId、uninId)',
  `orderId` bigint(20) NOT NULL DEFAULT '0' COMMENT '商城訂單id',
  `count` bigint(10) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

初始化1個(gè)獎品:

insert into Tran_test (id,count) values(1,1)

未提交讀

事務(wù)中的修改,即使沒有提交,也會被其他事務(wù)讀取。

下面通過mysql演示:

設(shè)置隔離級別為為提交讀:

SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

可以看到,事務(wù)B讀取到了事務(wù)A未提交的數(shù)據(jù),它任務(wù)抽獎活動已經(jīng)結(jié)束。但如果此時(shí)事務(wù)A回滾,count仍然為1,則活動實(shí)際是未結(jié)束的,這就是臟讀。因此,實(shí)際中,一般不會采用這種隔離級別。

提交讀

提交讀隔離級別可以解決上述臟讀問題,其只能讀到其他事務(wù)已經(jīng)提交的數(shù)據(jù)。

更改數(shù)據(jù)庫隔離級別:

SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;

可以看到,在事務(wù)A提交前的改動,事務(wù)B是讀取不到的。只有A事務(wù)提交后,B才能讀取到事務(wù)A的改動。

我們看到,在事務(wù)B中,先后兩次讀取,count的值是不一樣的,這就是不可重復(fù)讀。而可重復(fù)讀隔離級別可以解決這個(gè)問題。

可重復(fù)讀

更改數(shù)據(jù)庫隔離級別:

SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;

可以看到,不論事務(wù)A是否提交,事務(wù)B讀到的count值都是不變的。這就是可重復(fù)讀。

除了上面提到的臟讀、不可重復(fù)讀,還有一種情況是幻讀:在事務(wù)中,前后兩次查詢,記錄數(shù)量是不一樣的。

比如事務(wù)B是事務(wù)A插入一條記錄的前后執(zhí)行查詢,會發(fā)現(xiàn)相同的查詢條件,查出來的記錄數(shù)不一樣。由于mysql的RR(可重復(fù)讀)一并解決了幻讀的問題,所以我們直接看上述場景,在mysql中的表現(xiàn):

可見,在事務(wù)A提交前后,事務(wù)B查詢的結(jié)果數(shù)量是一直的,并沒有出現(xiàn)幻讀的情況。

一點(diǎn)思考

下面默認(rèn)都是討論的msyql RR隔離級別的情況。

如果兩個(gè)用戶同時(shí)抽獎,而且同時(shí)中獎。兩者都進(jìn)入了中獎的事務(wù)。A事務(wù)扣減了獎品數(shù)量,B也執(zhí)行了扣減數(shù)量。假設(shè)獎品數(shù)量是N,如果是可重復(fù)讀,那么,如果兩個(gè)事務(wù)并行進(jìn)行,那么不論A有沒有提交,B讀到的數(shù)量都是N,執(zhí)行后為N-1,而事務(wù)A也是N-1,這樣不就有問題了嗎?我們期望的是N-2。

當(dāng)初這個(gè)問題讓我很困惑。這反應(yīng)了當(dāng)時(shí)我對數(shù)據(jù)庫鎖和快照讀、當(dāng)前讀兩個(gè)知識點(diǎn)的欠缺。

快照讀、當(dāng)前讀

將設(shè)事務(wù)A已經(jīng)提交,由于是可重復(fù)讀,那事務(wù)B讀到的獎品數(shù)量一致是N,執(zhí)行-1,數(shù)據(jù)變成N-1,而不是我們期望的N-2。

如果理解了快照讀和當(dāng)前讀的概念,上面的困惑就不會存在了。

在事務(wù)中,執(zhí)行普通select查詢之后,會創(chuàng)建快照,后面再執(zhí)行相同的select語句時(shí),查詢的其實(shí)是前面生成的快照。這也就是為什么會有可重復(fù)讀。

而如果執(zhí)行

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 ?;

會執(zhí)行當(dāng)前讀,獲取最新數(shù)據(jù);氐角懊娴膯栴},如果事務(wù)B執(zhí)行N-1操作,會觸發(fā)當(dāng)前讀,讀取事務(wù)A提交后的數(shù)據(jù),也就是N-1,在此基礎(chǔ)上執(zhí)行-1操作,最終N變成N-2。

并發(fā)更新

上面解決了事務(wù)A已經(jīng)提交的額情況。但如果事務(wù)A更新獎品數(shù)量后但還未提交呢?此時(shí)事務(wù)B執(zhí)行當(dāng)前讀拿到的也是N啊。了解數(shù)據(jù)庫鎖機(jī)制的話,就不會有這種困惑了。事務(wù)A提交前,會一直持有排他鎖(具體是行鎖還是表鎖,要看查詢條件有沒有走索引),此時(shí)事務(wù)B更新是會阻塞的。也就是說,只有事務(wù)A提交,或回滾之后,事務(wù)B才能獲得排它鎖,從而進(jìn)行更新獎品的操作。

關(guān)于數(shù)據(jù)庫的鎖,大家可以參考這篇文章:http://hedengcheng.com/?p=771

標(biāo)簽: Mysql 數(shù)據(jù)庫

版權(quán)申明:本站文章部分自網(wǎng)絡(luò),如有侵權(quán),請聯(lián)系:west999com@outlook.com
特別注意:本站所有轉(zhuǎn)載文章言論不代表本站觀點(diǎn)!
本站所提供的圖片等素材,版權(quán)歸原作者所有,如需使用,請與原作者聯(lián)系。

上一篇:深入理解單例模式(上)

下一篇:JVM面試題