偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

臟讀、幻讀,要想搞懂不容易!

數(shù)據(jù)庫(kù) MySQL
臟讀、幻讀、不可重復(fù)讀、當(dāng)前讀、快照讀,這些名詞經(jīng)常搞的讓人頭暈。因?yàn)橐话闳舜竽X的主線(xiàn)就是單線(xiàn)程的,并不能一次性處理多個(gè)事務(wù)。

[[394503]]

本文轉(zhuǎn)載自微信公眾號(hào)「小姐姐味道」,作者小姐姐養(yǎng)的狗02號(hào)。轉(zhuǎn)載本文請(qǐng)聯(lián)系小姐姐味道公眾號(hào)。   

臟讀、幻讀、不可重復(fù)讀、當(dāng)前讀、快照讀,這些名詞經(jīng)常搞的讓人頭暈。因?yàn)橐话闳舜竽X的主線(xiàn)就是單線(xiàn)程的,并不能一次性處理多個(gè)事務(wù)。

要想記憶深刻,我們得借助幾個(gè)實(shí)例。讀完本文,你一定會(huì)豁然開(kāi)朗,忍不住三連走起。

但在這之前,我們需要看一下當(dāng)前的數(shù)據(jù)庫(kù)隔離級(jí)別,到底是什么。比如MySQL。

  1. select @@tx_isolation; 

MySQL就包含4種隔離級(jí)別,隔離的當(dāng)然是數(shù)據(jù)。要修改隔離級(jí)別的話(huà),可以使用下面的SQL語(yǔ)句。

  1. set session transaction isolation level read uncommitted
  2. set session transaction isolation level read committed
  3. set session transaction isolation level repeatable read
  4. set session transaction isolation level serializable

ok,我們創(chuàng)建一張小小的測(cè)試表,來(lái)看一下并發(fā)環(huán)境下的魔幻效果。

  1. CREATE TABLE `xjjdog_tx` ( 
  2.  `id` INT(11) NOT NULL
  3.  `nameVARCHAR(50) NOT NULL COLLATE 'utf8_general_ci'
  4.  `money` BIGINT(20) NOT NULL DEFAULT '0'
  5.  PRIMARY KEY (`id`) USING BTREE 
  6. COLLATE='utf8_general_ci' 
  7. ENGINE=InnoDB 
  8. INSERT INTO `xjjdog_tx` (`id`, `name`, `money`) VALUES (2, 'xjjdog1', 100); 
  9. INSERT INTO `xjjdog_tx` (`id`, `name`, `money`) VALUES (1, 'xjjdog0', 100); 

1. 臟讀

臟讀,意思就是讀出了臟數(shù)據(jù)。啥叫臟數(shù)據(jù)?就是另外一個(gè)事務(wù)還沒(méi)有提交的數(shù)據(jù)。在read uncommitted隔離級(jí)別下,就會(huì)出現(xiàn)臟讀。比如下面這個(gè)時(shí)序

  1. 事務(wù) A:set session transaction isolation level read uncommitted
  2. 事務(wù) B:set session transaction isolation level read uncommitted
  3. 事務(wù) A:START TRANSACTION ; 
  4. 事務(wù) B:START TRANSACTION ; 
  5. 事務(wù) A:UPDATE xjjdog_tx SET money=money+100 WHERE NAME='xjjdog0'
  6. 事務(wù) B:UPDATE xjjdog_tx SET money=money+100 WHERE NAME='xjjdog0'
  7. 事務(wù) A:ROLLBACK ; 
  8. 事務(wù) B:COMMIT ; 
  9. 事務(wù) B:SELECT * FROM xjjdog_tx ; 

在這個(gè)場(chǎng)景下,money的原始值為100,分別在兩個(gè)session中進(jìn)行了加100的操作,然后回滾了其中的一個(gè)session事務(wù)。結(jié)果,經(jīng)過(guò)查詢(xún),發(fā)現(xiàn)money的值保持100不變。也就是其中一次加100的操作被覆蓋掉了。

所以臟讀發(fā)生有幾個(gè)條件。

  • 高并發(fā)場(chǎng)景,在一個(gè)事務(wù)A開(kāi)始之后還沒(méi)結(jié)束之前,有另外一個(gè)事務(wù)參與了事務(wù)A所涉及的數(shù)據(jù)行讀寫(xiě)
  • 事務(wù)隔離級(jí)別處于最低的讀未提交
  • 在你使用到這些數(shù)據(jù)之后,事務(wù)A回滾,造成你之前拿到的數(shù)據(jù)已經(jīng)不再存在

解決方式,只需要設(shè)置成隔離級(jí)別比read uncommitted高即可。

2. 不可重復(fù)讀

把隔離級(jí)別設(shè)置成read committed即可避免臟讀,這其實(shí)非常好理解。臟讀產(chǎn)生的根本原因就是在事務(wù)的執(zhí)行期間有別的操作亂入,這個(gè)隔離級(jí)別要求事務(wù)A提交之后,修改后的值,才能被事務(wù)B讀到,所以臟讀是不可能會(huì)發(fā)生的,從根本上杜絕了。

但read commited會(huì)發(fā)生不可重復(fù)讀的情況。

顧名思義,就是在一個(gè)事務(wù)周期內(nèi),對(duì)于一個(gè)值的讀取,產(chǎn)生了兩個(gè)結(jié)果。

不可重復(fù)讀,證明了世界并不是總圍繞著你轉(zhuǎn)的。在你的事務(wù)執(zhí)行期間,會(huì)有無(wú)數(shù)的其他事務(wù)執(zhí)行,如果你的事務(wù)持續(xù)時(shí)間超過(guò)了這些事務(wù),那么你就可能讀到兩個(gè)或者更多的值。

讓我來(lái)給你講一個(gè)故事。

從前,有一顆桃樹(shù),長(zhǎng)了12棵桃子。有一只猴子,叫做xjjdog,它想吃上面的桃子,但桃子還不熟。

第二天去看的時(shí)候,它發(fā)現(xiàn)桃子少了一個(gè),變成了11個(gè),經(jīng)過(guò)仔細(xì)打聽(tīng),原來(lái)是被猴子A搶先吃掉一個(gè)。

第二天去看的時(shí)候,桃子又少了一個(gè),變成了10個(gè),原來(lái)是被饞嘴的猴子B吃掉一個(gè)。

如此這般,桃子一天天少了下去,只剩下最后的2個(gè)了,但桃子還是沒(méi)熟。

再不摘桃子就沒(méi)了,xjjdog摘下了最后的2個(gè)桃子,正打算大快朵頤,結(jié)果跳出一只猴子X(jué),說(shuō)我盯著這些桃子已經(jīng)1年了...

在這故事中,猴子A、B的事務(wù)持續(xù)周期是1天;xjjdog的事務(wù)持續(xù)周期是直到桃子成熟;猴子X(jué)的持續(xù)周期更長(zhǎng),可能是一年。它們每天看到的桃子,并不總是12個(gè)。今天的桃子,可能被其他的猴子(事務(wù))給吃掉了,造成了觀(guān)測(cè)的結(jié)果是不一樣的,這就是不可重復(fù)讀的概念。

有時(shí)候,即使讀到的值是一樣的,也不能證明沒(méi)問(wèn)題。比如有財(cái)務(wù)挪用了2億去炒股,然后在月底把2億還了回來(lái),雖然最終的金額都是一致的,但由于你的對(duì)賬周期長(zhǎng),就發(fā)現(xiàn)不了這種差異。

如何解決不可重復(fù)讀呢?先要看一下不可重復(fù)讀是不是問(wèn)題。

有的系統(tǒng),要求的就是這樣的邏輯,每次在事務(wù)中讀取到不一樣的值,它是可以忍受的。但如果你想要在桃子成熟之前,桃子的數(shù)量都在你的掌控之中,那不可重復(fù)讀就是一種問(wèn)題。

一種非常好的方式,就是xjjdog一直站在桃樹(shù)地下。當(dāng)有別的猴子想要摘桃,就把它趕走。這種方式可行,但在數(shù)據(jù)庫(kù)中非常低效,這是serializable級(jí)別的做法。

MySQL有一個(gè)默認(rèn)的事務(wù)隔離級(jí)別,叫做repeatable read,使用了MVCC的方式(innodb),要更輕量級(jí)一些。

3. 可重復(fù)讀

這就是MVCC(Multi-Version Concurrency Control)的功勞了,它有三個(gè)特點(diǎn)。

每行數(shù)據(jù)都存在一個(gè)版本,每次數(shù)據(jù)更新時(shí)都更新該版本

修改時(shí),拷貝一份,當(dāng)前版本隨意修改,事務(wù)之間無(wú)干擾

保存時(shí)比較版本號(hào),如果成功commit覆蓋原記錄,失敗則rollback

MVCC在InnoDB中的實(shí)現(xiàn)主要是為了提高數(shù)據(jù)庫(kù)并發(fā)性能,用更好的方式去處理讀-寫(xiě)沖突,做到即使有讀寫(xiě)沖突時(shí),也能做到不加鎖,非阻塞并發(fā)讀。它的實(shí)現(xiàn)關(guān)鍵也有三項(xiàng)技術(shù):

  1. 3個(gè)隱式字段:DB_TRX_ID,最近修改它的事務(wù)ID;DB_ROLL_PTR,回滾指針,指向上一個(gè)版本;DB_ROW_ID,隱藏主鍵
  2. undo日志:的對(duì)同一記錄的修改,會(huì)生成針對(duì)此記錄的版本變更鏈表
  3. read view:快照讀操作的時(shí)候,產(chǎn)生的讀視圖。除了使用上面的額外信息,它也會(huì)維護(hù)一個(gè)活躍的事務(wù)ID集合

一切的關(guān)鍵,就在于快照這兩個(gè)字上面。

比如事務(wù)A對(duì)某個(gè)記錄進(jìn)行了快照讀,那么在快照讀的這一刻,就生成了一個(gè)Read View。在這一刻,事務(wù)B和C,還沒(méi)有commit,事務(wù)D和E,在建立ReadView那一刻之前,commit完成,那么這個(gè)Read View,就不能夠讀到B和C的修改。

但可惜的是,可重復(fù)讀,只能解決快照讀的不可重復(fù)讀,快照讀的時(shí)機(jī),也會(huì)影響讀取的準(zhǔn)確程度。請(qǐng)看下面兩種情況。

下面這種情況讀到的是500。

事務(wù)A 事務(wù)B
開(kāi)啟事務(wù) 開(kāi)啟事務(wù)
快照讀(無(wú)影響)查詢(xún)金額為500 快照讀查詢(xún)金額為500
更新金額為400  
提交事務(wù)  
  select 快照讀金額為500
  select lock in share mode當(dāng)前讀金額為400

下面這種情況讀到的是400。

事務(wù)A 事務(wù)B
開(kāi)啟事務(wù) 開(kāi)啟事務(wù)
快照讀(無(wú)影響)查詢(xún)金額為500  
更新金額為400  
提交事務(wù)  
  select 快照讀金額為400
  select lock in share mode當(dāng)前讀金額為400
 

(表格來(lái)自[SnailMann]的博客)。

4. 幻讀

幻讀,這個(gè)詞本身就非常的迷幻。在RU、RC、RR級(jí)別下,都會(huì)出現(xiàn)幻讀。

拿一個(gè)最簡(jiǎn)單的例子來(lái)說(shuō)。讓你select一條記錄是否存在然后打算進(jìn)行后續(xù)插入時(shí),如果這條記錄不存在,然后你執(zhí)行了插入操作,但在實(shí)際執(zhí)行插入操作的時(shí)候,結(jié)果卻報(bào)錯(cuò)了,這條記錄已經(jīng)存在了,這就是幻讀。

首先,確認(rèn)目前時(shí)可重復(fù)讀級(jí)別。如果不是,則修改之。

  1. SELECT @@tx_isolation 
  2. set session transaction isolation level repeatable read 

讓我們來(lái)看一下這個(gè)靈異過(guò)程。

有5個(gè)步驟,我都給你標(biāo)好了。下面一一介紹。

  1. 事務(wù)A使用begin開(kāi)啟一個(gè)事務(wù),然后查詢(xún)id為3的記錄,此時(shí)不存在。但由于快照讀開(kāi)啟了一個(gè)針對(duì)于id為3的記錄的read view,所以在這個(gè)事務(wù)自始至終都不能夠讀到為3的記錄。很好,這就是我們不可重復(fù)讀所需要的
  2. 接下來(lái),事務(wù)B插入了一條id為3的記錄,并提交成功
  3. 事務(wù)A此時(shí)也想插入這條記錄,于是執(zhí)行了相同的插入操作,結(jié)果數(shù)據(jù)庫(kù)報(bào)錯(cuò),顯示這條記錄已經(jīng)存在
  4. 事務(wù)A此時(shí)一臉懵逼,想看一下這條記錄到底是啥,但當(dāng)它再次執(zhí)行select語(yǔ)句的時(shí)候,卻查不到這條記錄
  5. 但在其他事務(wù)中,是可以看到這條記錄的,因?yàn)樗呀?jīng)正確提交

這就是幻讀。

5. 如何解決幻讀

幻讀有錯(cuò)么?多數(shù)情況下沒(méi)錯(cuò),就是報(bào)錯(cuò)怪異了些。要防止幻讀,需要開(kāi)啟FOR UPDATE這樣高強(qiáng)度的鎖定,實(shí)際情況是非常少用。

為什么上面的操作,insert能報(bào)錯(cuò),但select卻無(wú)法查到數(shù)據(jù)呢?這就不得不提一下數(shù)據(jù)庫(kù)讀的兩種模式:

快照讀:普通的select操作,是從read view中讀取數(shù)據(jù),讀取的可能是歷史數(shù)據(jù)

當(dāng)前讀:insert、update、delete、select..for update這種操作,讀取的總是當(dāng)前的最新數(shù)據(jù)

對(duì)于當(dāng)前讀,你讀取的行,以及行的間隙都會(huì)被加鎖,直到事務(wù)提交時(shí)才會(huì)釋放,其他的事務(wù)無(wú)法進(jìn)行修改,所以也不會(huì)出現(xiàn)不可重復(fù)讀、幻讀的情形。所以insert能夠發(fā)現(xiàn)沖突,而普通select卻不可以。要想解決幻讀,就需要加X(jué)鎖。在上面這種情況,就可以在事務(wù)A中執(zhí)行:

  1. SELECT * FROM xjjdog_tx WHERE id=3 FOR UPDATE 

當(dāng)這么做的時(shí)候,即使id為3的記錄不存在,它也會(huì)創(chuàng)建鎖(在背后可能根據(jù)記錄的存在與否加行X鎖或者next-key lock間隙x鎖)。

6. 總結(jié)

下面簡(jiǎn)單總結(jié)一下。

臟讀,就是一個(gè)事務(wù)讀取到另一個(gè)事務(wù)還沒(méi)有提交的記錄。當(dāng)其他事務(wù)發(fā)生回滾的時(shí)候,就會(huì)出現(xiàn)問(wèn)題。

不可重復(fù)讀,意思是在同一個(gè)事務(wù)里,讀多次可能會(huì)獲得不一致的結(jié)果。這是因?yàn)樵谑聞?wù)執(zhí)行期間,有別的事務(wù)修改了這些記錄。

MySQL默認(rèn)是可重復(fù)讀,但會(huì)發(fā)生幻讀的情況?;米x是由于快照讀和當(dāng)前讀的差別產(chǎn)生的。

要想解決幻讀,就需要加鎖(X鎖,Gap鎖等),比如for update,全部改成當(dāng)前讀直到事務(wù)結(jié)束,自然沒(méi)有問(wèn)題。

所謂的最高級(jí)別serializable,不過(guò)是全部搞成了當(dāng)前讀而已,在高并發(fā)的環(huán)境下效率,可想而知。所以幾乎沒(méi)有用的。

作者簡(jiǎn)介:小姐姐味道 (xjjdog),一個(gè)不允許程序員走彎路的公眾號(hào)。聚焦基礎(chǔ)架構(gòu)和Linux。十年架構(gòu),日百億流量,與你探討高并發(fā)世界,給你不一樣的味道。我的個(gè)人微信xjjdog0,歡迎添加好友,進(jìn)一步交流。

 

責(zé)任編輯:武曉燕 來(lái)源: 小姐姐味道
相關(guān)推薦

2022-01-03 07:18:05

臟讀幻讀 MySQL

2024-04-19 08:18:47

MySQLSQL隔離

2019-03-21 09:06:00

數(shù)據(jù)庫(kù)復(fù)讀幻讀

2022-04-27 07:32:02

臟讀幻讀不可重復(fù)讀

2024-04-24 08:26:35

事務(wù)數(shù)據(jù)InnoDB

2020-06-09 08:19:25

微服務(wù)網(wǎng)站架構(gòu)

2012-06-13 14:58:09

BYOD移動(dòng)辦公

2023-08-09 17:22:30

MVCCMySQL數(shù)據(jù)

2023-11-01 14:13:00

MySQL事務(wù)隔離級(jí)別

2017-04-27 13:30:14

AndroidWebView移動(dòng)應(yīng)用

2020-06-18 10:52:17

運(yùn)維架構(gòu)技術(shù)

2018-01-24 07:28:20

2013-09-22 09:16:25

碼農(nóng)程序員黑客

2009-02-12 17:25:21

Windows7試用下載

2009-09-04 08:19:24

Windows 7優(yōu)缺點(diǎn)

2011-12-16 14:52:55

移動(dòng)互聯(lián)聯(lián)想

2023-08-31 22:17:15

JavaMySQLB+樹(shù)

2025-02-26 10:40:44

數(shù)據(jù)庫(kù)并發(fā)幻讀

2024-09-02 00:00:00

MySQL幻讀數(shù)據(jù)

2015-06-11 11:21:12

免費(fèi)Wi-Fi
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)