色婷婷狠狠18禁久久YY,CHINESE性内射高清国产,国产女人18毛片水真多1,国产AV在线观看

查詢賦值mysql,MySQL的可重復(fù)讀級別能解決幻讀嗎

黃文隆2年前21瀏覽0評論
查詢賦值mysql,MySQL的可重復(fù)讀級別能解決幻讀嗎?

測試:

創(chuàng)建一張測試用的表dept:

根據(jù)上面的流程執(zhí)行,預(yù)期來說應(yīng)該是事物1的第一條select查詢出一條數(shù)據(jù),第二個select查詢出兩條數(shù)據(jù)(包含事物2提交的數(shù)據(jù))。

但是在實際測試中發(fā)現(xiàn)第二條select實際上也只查詢處理一條數(shù)據(jù)。這是但是根據(jù)數(shù)據(jù)庫理論的可重復(fù)讀的實現(xiàn)(排他鎖和共享鎖)這是不應(yīng)該的情況。

在了解實際原因前我們先復(fù)習(xí)下事物的相關(guān)理論。

數(shù)據(jù)庫原理理論事物

事務(wù)(Transaction),一般是指要做的或所做的事情。在計算機(jī)術(shù)語中是指訪問并可能更新數(shù)據(jù)庫中各種數(shù)據(jù)項的一個程序執(zhí)行單元(unit)。事務(wù)由事務(wù)開始(begin transaction)和事務(wù)結(jié)束(end transaction)之間執(zhí)行的全體操作組成。在關(guān)系數(shù)據(jù)庫中,一個事務(wù)可以是一組SQL語句或整個程序。

為什么要有事物

一個數(shù)據(jù)庫事務(wù)通常包含對數(shù)據(jù)庫進(jìn)行讀或?qū)懙囊粋€操作序列。它的存在包含有以下兩個目的:

為數(shù)據(jù)庫操作提供了一個從失敗中恢復(fù)到正常狀態(tài)的方法,同時提供了數(shù)據(jù)庫在異常狀態(tài)下仍能保持一致性的方法。

當(dāng)多個應(yīng)用程序在并發(fā)訪問數(shù)據(jù)庫時,可以在這些應(yīng)用程序之間提供一個隔離方法,保證彼此的操作互相干擾。

事物特性

事務(wù)具有4個特性:原子性、一致性、隔離性、持久性。這四個屬性通常稱為 ACID 特性。

原子性(atomicity):一個事務(wù)應(yīng)該是一個不可分割的工作單位,事務(wù)中包括的操作要么都成功,要么都不成功。

一致性(consistency):事務(wù)必須是使數(shù)據(jù)庫從一個一致性狀態(tài)變到另一個一致性狀態(tài)。一致性與原子性是密切相關(guān)的。

隔離性(isolation):一個事務(wù)的執(zhí)行不能被其他事務(wù)干擾。即一個事務(wù)內(nèi)部的操作及使用的數(shù)據(jù)在事物未提交前對并發(fā)的其他事務(wù)是隔離的,并發(fā)執(zhí)行的各個事務(wù)之間不能互相影響。

持久性(durability):一個事務(wù)一旦成功提交,它對數(shù)據(jù)庫中數(shù)據(jù)的改變就應(yīng)該是永久性的。接下來的其他操作或故障不應(yīng)該對其有任何影響。

事物之間的幾個特性并不是一組同等的概念:

如果在任何時刻都只有一個事物,那么其天然是具有隔離性的,這時只要保證原子性就能具有一致性。

如果存在并發(fā)的情況下,就需要保證原子性和隔離性才能保證一致性。

數(shù)據(jù)庫并發(fā)事物中存在的問題

如果不考慮事務(wù)的隔離性,會發(fā)生以下幾種問題:

臟讀:臟讀是指在一個事務(wù)處理過程里讀取了另一個未提交的事務(wù)中的數(shù)據(jù)。當(dāng)一個事務(wù)正在多次修改某個數(shù)據(jù),而在這個事務(wù)中這多次的修改都還未提交,這時一個并發(fā)的事務(wù)來訪問該數(shù)據(jù),就會造成兩個事務(wù)得到的數(shù)據(jù)不一致。

不可重復(fù)讀:不可重復(fù)讀是指在對于數(shù)據(jù)庫中的某條數(shù)據(jù),一個事務(wù)范圍內(nèi)多次查詢返回不同的數(shù)據(jù)值(這里不同是指某一條或多條數(shù)據(jù)的內(nèi)容前后不一致,但數(shù)據(jù)條數(shù)相同),這是由于在查詢間隔,該事物需要用到的數(shù)據(jù)被另一個事務(wù)修改并提交了。不可重復(fù)讀和臟讀的區(qū)別是,臟讀是某一事務(wù)讀取了另一個事務(wù)未提交的臟數(shù)據(jù),而不可重復(fù)讀則是讀取了其他事務(wù)提交的數(shù)據(jù)。需要注意的是在某些情況下不可重復(fù)讀并不是問題。

幻讀:幻讀是事務(wù)非獨立執(zhí)行時發(fā)生的一種現(xiàn)象。例如事務(wù)T1對一個表中所有的行的某個數(shù)據(jù)項做了從“1”修改為“2”的操作,這時事務(wù)T2又對這個表中插入了一行數(shù)據(jù)項,而這個數(shù)據(jù)項的數(shù)值還是為“1”并且提交給數(shù)據(jù)庫。而操作事務(wù)T1的用戶如果再查看剛剛修改的數(shù)據(jù),會發(fā)現(xiàn)還有一行沒有修改,其實這行是從事務(wù)T2中添加的,就好像產(chǎn)生幻覺一樣,這就是發(fā)生了幻讀。幻讀和不可重復(fù)讀都是讀取了另一條已經(jīng)提交的事務(wù)(這點就臟讀不同),所不同的是不可重復(fù)讀可能發(fā)生在update,delete操作中,而幻讀發(fā)生在insert操作中。

排他鎖,共享鎖

排它鎖(Exclusive),又稱為X 鎖,寫鎖。

共享鎖(Shared),又稱為S 鎖,讀鎖。

讀寫鎖之間有以下的關(guān)系:

一個事務(wù)對數(shù)據(jù)對象O加了 S 鎖,可以對 O進(jìn)行讀取操作,但是不能進(jìn)行更新操作。加鎖期間其它事務(wù)能對O 加 S 鎖,但是不能加 X 鎖。一個事務(wù)對數(shù)據(jù)對象 O 加了 X 鎖,就可以對 O 進(jìn)行讀取和更新。加鎖期間其它事務(wù)不能對 O 加任何鎖。

即讀寫鎖之間的關(guān)系可以概括為:多讀單寫

事物的隔離級別

在事物中存在以下幾種隔離級別:

讀未提交(Read Uncommitted):解決更新丟失問題。如果一個事務(wù)已經(jīng)開始寫操作,那么其他事務(wù)則不允許同時進(jìn)行寫操作,但允許其他事務(wù)讀此行數(shù)據(jù)。該隔離級別可以通過“排他寫鎖”實現(xiàn),即事物需要對某些數(shù)據(jù)進(jìn)行修改必須對這些數(shù)據(jù)加 X 鎖,讀數(shù)據(jù)不需要加 S 鎖。

讀已提交(Read Committed):解決了臟讀問題。讀取數(shù)據(jù)的事務(wù)允許其他事務(wù)繼續(xù)訪問該行數(shù)據(jù),但是未提交的寫事務(wù)將會禁止其他事務(wù)訪問該行。這可以通過“瞬間共享讀鎖”和“排他寫鎖”實現(xiàn), 即事物需要對某些數(shù)據(jù)進(jìn)行修改必須對這些數(shù)據(jù)加 X 鎖,讀數(shù)據(jù)時需要加上 S 鎖,當(dāng)數(shù)據(jù)讀取完成后立刻釋放 S 鎖,不用等到事物結(jié)束。

可重復(fù)讀取(Repeatable Read):禁止不可重復(fù)讀取和臟讀取,但是有時可能出現(xiàn)幻讀數(shù)據(jù)。讀取數(shù)據(jù)的事務(wù)將會禁止寫事務(wù)(但允許讀事務(wù)),寫事務(wù)則禁止任何其他事務(wù)。Mysql默認(rèn)使用該隔離級別。這可以通過“共享讀鎖”和“排他寫鎖”實現(xiàn),即事物需要對某些數(shù)據(jù)進(jìn)行修改必須對這些數(shù)據(jù)加 X 鎖,讀數(shù)據(jù)時需要加上 S 鎖,當(dāng)數(shù)據(jù)讀取完成并不立刻釋放 S 鎖,而是等到事物結(jié)束后再釋放。

串行化(Serializable):解決了幻讀的問題的。提供嚴(yán)格的事務(wù)隔離。它要求事務(wù)序列化執(zhí)行,事務(wù)只能一個接著一個地執(zhí)行,不能并發(fā)執(zhí)行。僅僅通過“行級鎖”是無法實現(xiàn)事務(wù)序列化的,必須通過其他機(jī)制保證新插入的數(shù)據(jù)不會被剛執(zhí)行查詢操作的事務(wù)訪問到。

MySQL中的隔離級別的實現(xiàn)

上面的內(nèi)容解釋了一些數(shù)據(jù)庫理論的概念,但是在MySQL、ORACLE這樣的數(shù)據(jù)庫中,為了性能的考慮并不是完全按照上面介紹的理論來實現(xiàn)的。

MVCC

多版本并發(fā)控制(Multi-Version Concurrency Control, MVCC)是MySQL中基于樂觀鎖理論實現(xiàn)隔離級別的方式,用于實現(xiàn)讀已提交和可重復(fù)讀取隔離級別的實現(xiàn)。

實現(xiàn)(隔離級別為可重復(fù)讀)

在說到如何實現(xiàn)前先引入兩個概念:

系統(tǒng)版本號:一個遞增的數(shù)字,每開始一個新的事務(wù),系統(tǒng)版本號就會自動遞增。

事務(wù)版本號:事務(wù)開始時的系統(tǒng)版本號。

在MySQL中,會在表中每一條數(shù)據(jù)后面添加兩個字段:

創(chuàng)建版本號:創(chuàng)建一行數(shù)據(jù)時,將當(dāng)前系統(tǒng)版本號作為創(chuàng)建版本號賦值

刪除版本號:刪除一行數(shù)據(jù)時,將當(dāng)前系統(tǒng)版本號作為刪除版本號賦值

SELECT

select時讀取數(shù)據(jù)的規(guī)則為:創(chuàng)建版本號<=當(dāng)前事務(wù)版本號,刪除版本號為空或>當(dāng)前事務(wù)版本號。

創(chuàng)建版本號<=當(dāng)前事務(wù)版本號保證取出的數(shù)據(jù)不會有后啟動的事物中創(chuàng)建的數(shù)據(jù)。這也是為什么在開始的示例中我們不會查出后來添加的數(shù)據(jù)的原因

刪除版本號為空或>當(dāng)前事務(wù)版本號保證了至少在該事物開啟之前數(shù)據(jù)沒有被刪除,是應(yīng)該被查出來的數(shù)據(jù)。

INSERT

insert時將當(dāng)前的系統(tǒng)版本號賦值給創(chuàng)建版本號字段。

UPDATE

插入一條新紀(jì)錄,保存當(dāng)前事務(wù)版本號為行創(chuàng)建版本號,同時保存當(dāng)前事務(wù)版本號到原來刪除的行,實際上這里的更新是通過delete和insert實現(xiàn)的。

DELETE

刪除時將當(dāng)前的系統(tǒng)版本號賦值給刪除版本號字段,標(biāo)識該行數(shù)據(jù)在那一個事物中會被刪除,即使實際上在位commit時該數(shù)據(jù)沒有被刪除。根據(jù)select的規(guī)則后開啟懂?dāng)?shù)據(jù)也不會查詢到該數(shù)據(jù)。

MVCC真的解決了幻讀?

從最開始我們的測試示例和上面的理論支持來看貌似在MySQL中通過MVCC就解決了幻讀的問題,那既然這樣串行化讀貌似就沒啥意義了,帶著疑問繼續(xù)測試。

測試前數(shù)據(jù):

事物 1事物 2beginbeginselect * from dept-insert into dept(name) values("研發(fā)部")-commitupdate dept set name="財務(wù)部"(工作中如果不想被辭退一定要寫where條件)commit

根據(jù)上面的結(jié)果我們期望的結(jié)果是這樣的:

但是實際上我們的經(jīng)過是:

本來我們希望得到的結(jié)果只是第一條數(shù)據(jù)的部門改為財務(wù),但是結(jié)果確實兩條數(shù)據(jù)都被修改了。這種結(jié)果告訴我們其實在MySQL可重復(fù)讀的隔離級別中并不是完全解決了幻讀的問題,而是解決了讀數(shù)據(jù)情況下的幻讀問題。而對于修改的操作依舊存在幻讀問題,就是說MVCC對于幻讀的解決時不徹底的。

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

出現(xiàn)了上面的情況我們需要知道為什么會出現(xiàn)這種情況。在查閱了一些資料后發(fā)現(xiàn)在RR級別中,通過MVCC機(jī)制,雖然讓數(shù)據(jù)變得可重復(fù)讀,但我們讀到的數(shù)據(jù)可能是歷史數(shù)據(jù),不是數(shù)據(jù)庫最新的數(shù)據(jù)。這種讀取歷史數(shù)據(jù)的方式,我們叫它快照讀 (snapshot read),而讀取數(shù)據(jù)庫最新版本數(shù)據(jù)的方式,叫當(dāng)前讀 (current read)。

select 快照讀

當(dāng)執(zhí)行select操作是innodb默認(rèn)會執(zhí)行快照讀,會記錄下這次select后的結(jié)果,之后select 的時候就會返回這次快照的數(shù)據(jù),即使其他事務(wù)提交了不會影響當(dāng)前select的數(shù)據(jù),這就實現(xiàn)了可重復(fù)讀了。快照的生成當(dāng)在第一次執(zhí)行select的時候,也就是說假設(shè)當(dāng)A開啟了事務(wù),然后沒有執(zhí)行任何操作,這時候B insert了一條數(shù)據(jù)然后commit,這時候A執(zhí)行 select,那么返回的數(shù)據(jù)中就會有B添加的那條數(shù)據(jù)。之后無論再有其他事務(wù)commit都沒有關(guān)系,因為快照已經(jīng)生成了,后面的select都是根據(jù)快照來的。

當(dāng)前讀

對于會對數(shù)據(jù)修改的操作(update、insert、delete)都是采用當(dāng)前讀的模式。在執(zhí)行這幾個操作時會讀取最新的記錄,即使是別的事務(wù)提交的數(shù)據(jù)也可以查詢到。假設(shè)要update一條記錄,但是在另一個事務(wù)中已經(jīng)delete掉這條數(shù)據(jù)并且commit了,如果update就會產(chǎn)生沖突,所以在update的時候需要知道最新的數(shù)據(jù)。也正是因為這樣所以才導(dǎo)致上面我們測試的那種情況。

select的當(dāng)前讀需要手動的加鎖:

有個問題說明下

在測試過程中最開始我以為使用語句就是開始一個事物了,所以在上面第二次測試中因為先開始的事物1,結(jié)果在事物1中卻查到了事物2新增的數(shù)據(jù),當(dāng)時認(rèn)為這和前面MVCC中的select的規(guī)則不一致了,所以做了如下測試:

可以看到如果只是執(zhí)行語句實際上并沒有開啟一個事物。

下面在后添加一條select語句:

所以要明白實際上是對數(shù)據(jù)進(jìn)行了增刪改查等操作后才開啟了一個事物。

如何解決幻讀

很明顯可重復(fù)讀的隔離級別沒有辦法徹底的解決幻讀的問題,如果我們的項目中需要解決幻讀的話也有兩個辦法:

使用串行化讀的隔離級別MVCC+next-key locks:next-key locks由record locks(索引加鎖) 和 gap locks(間隙鎖,每次鎖住的不光是需要使用的數(shù)據(jù),還會鎖住這些數(shù)據(jù)附近的數(shù)據(jù))

實際上很多的項目中是不會使用到上面的兩種方法的,串行化讀的性能太差,而且其實幻讀很多時候是我們完全可以接受的。