為什么MySQL 默認隔離級別是RR场航,又被阿里設置為RC

我們知道缠导,我們可以通過這個命令查看數(shù)據(jù)庫當前的隔離級別,MySQL 默認隔離級別是RR.?

select @@tx_isolation;


ANSI/ISO SQL定義的標準隔離級別有四種旗闽,從高到底依次為:可序列化(Serializable)酬核、可重復讀(Repeatable Reads)蜜另、提交讀(Read Committed)、未提交讀(Read Uncommitted)嫡意。

RU隔離級別下举瑰,可能發(fā)生臟讀、幻讀蔬螟、不可重復讀等問題此迅。

未提交讀的數(shù)據(jù)庫鎖情況(實現(xiàn)原理)

事務在讀數(shù)據(jù)的時候并未對數(shù)據(jù)加鎖。

事務在修改數(shù)據(jù)的時候只對數(shù)據(jù)增加行級共享鎖旧巾。

RC隔離級別下耸序,解決了臟讀的問題,存在幻讀鲁猩、不可重復讀的問題坎怪。

提交讀的數(shù)據(jù)庫鎖情況

事務對當前被讀取的數(shù)據(jù)加 行級共享鎖(當讀到時才加鎖),一旦讀完該行廓握,立即釋放該行級共享鎖搅窿;

事務在更新某數(shù)據(jù)的瞬間(就是發(fā)生更新的瞬間),必須先對其加 行級排他鎖隙券,直到事務結(jié)束才釋放男应。

RR隔離級別下,解決了臟讀娱仔、不可重復讀的問題沐飘,存在幻讀的問題。

可重復讀的數(shù)據(jù)庫鎖情況

事務在讀取某數(shù)據(jù)的瞬間(就是開始讀取的瞬間)牲迫,必須先對其加 行級共享鎖耐朴,直到事務結(jié)束才釋放;

事務在更新某數(shù)據(jù)的瞬間(就是發(fā)生更新的瞬間)恩溅,必須先對其加 行級排他鎖隔箍,直到事務結(jié)束才釋放。

Serializable隔離級別下脚乡,解決了臟讀蜒滩、幻讀、不可重復讀的問題奶稠。

可序列化的數(shù)據(jù)庫鎖情況

事務在讀取數(shù)據(jù)時俯艰,必須先對其加 表級共享鎖 ,直到事務結(jié)束才釋放锌订;

事務在更新數(shù)據(jù)時竹握,必須先對其加 表級排他鎖 ,直到事務結(jié)束才釋放辆飘。

雖然可序列化解決了臟讀啦辐、不可重復讀谓传、幻讀等讀現(xiàn)象。但是序列化事務會產(chǎn)生以下效果:

1.無法讀取其它事務已修改但未提交的記錄芹关。

2.在當前事務完成之前续挟,其它事務不能修改目前事務已讀取的記錄。

3.在當前事務完成之前侥衬,其它事務所插入的新記錄诗祸,其索引鍵值不能在當前事務的任何語句所讀取的索引鍵范圍中。

這四種隔離級別是ANSI/ISO SQL定義的標準定義的轴总,我們比較常用的MySQL對這四種隔離級別是都支持的直颅。


Oracle默認的隔離級別是 RC,而MySQL默認的隔離級別是 RR怀樟。那么功偿,你知道為什么嗎?

Oracle 的隔離級別

前面我們說過往堡,Oracle只只支持ANSI/ISO SQL定義的Serializable和Read Committed脖含,其實,根據(jù)Oracle官方文檔給出的介紹投蝉,Oracle支持三種隔離級別:

即Oracle支持Read Committed、Serializable和Read-Only征堪。

Read-Only只讀隔離級別類似于可序列化隔離級別瘩缆,但是只讀事務不允許在事務中修改數(shù)據(jù),除非用戶是SYS佃蚜。

在Oracle這三種隔離級別中庸娱,Serializable和Read-Only顯然都是不適合作為默認隔離級別的,那么就只剩Read Committed這個唯一的選擇了谐算。

MySQL 的隔離級別

在MySQL設計之處熟尉,他的定位就是提供一個穩(wěn)定的關(guān)系型數(shù)據(jù)庫。而為了要解決MySQL單點故障帶來的問題洲脂,MySQL采用主從復制的機制斤儿。

所謂主從復制,其實就是通過搭建MySQL集群恐锦,整體對外提供服務往果,集群中的機器分為主服務器(Master)和從服務器(Slave),主服務器提供寫服務一铅,從服務器提供讀服務陕贮。

為了保證主從服務器之間的數(shù)據(jù)的一致性,就需要進行數(shù)據(jù)同步.


MySQL在主從復制的過程中潘飘,數(shù)據(jù)的同步是通過bin log進行的肮之,簡單理解就是主服務器把數(shù)據(jù)變更記錄到bin log中掉缺,然后再把bin log同步傳輸給從服務器,從服務器接收到bin log之后戈擒,再把其中的數(shù)據(jù)恢復到自己的數(shù)據(jù)庫存儲中眶明。

那么,binlog里面記錄的是什么內(nèi)容呢峦甩?格式是怎樣的呢赘来?

MySQL的bin log主要支持三種格式,分別是statement凯傲、row以及mixed犬辰。MySQL是在5.1.5版本開始支持row的、在5.1.8版本中開始支持mixed冰单。

statement和row最大的區(qū)別幌缝,當binlog的格式為statemen時,binlog 里面記錄的就是 SQL 語句的原文(這句話很重要=肭贰:选!后面會用的到)荒叼。

關(guān)于這幾種格式的區(qū)別轿偎,就不在這里詳細展開了,之所以要支持row格式被廓,主要是因為statement格式中存在很多問題坏晦,最明顯的就是可能會導致主從數(shù)據(jù)庫的數(shù)據(jù)不一致。

那么嫁乘,這個主從同步和bin log我們要講的隔離級別有啥關(guān)系呢馆铁?

有關(guān)系沛慢,而且關(guān)系很大焦读。

因為MySQL早期只有statement這種bin log格式侥袜,這時候,如果使用提交讀(Read Committed)挎春、未提交讀(Read Uncommitted)這兩種隔離級別會出現(xiàn)問題看疙。

比如,在MySQL官網(wǎng)上直奋,有人就給官方曾經(jīng)提過一個相關(guān)的Bug

這個bug的復現(xiàn)過程如下:

有一個數(shù)據(jù)庫表t1狼荞,表中有如下兩條記錄:

CREATETABLE`t1` (? `a`int(11)DEFAULTNULL,? `b`int(11)DEFAULTNULL,? KEY `a` (`a`)) ENGINE=InnoDBDEFAULTCHARSET=latin1;insertintot1values(10,2),(20,1);

接著開始執(zhí)行兩個事務的寫操作:

以上兩個事務執(zhí)行之后,數(shù)據(jù)庫里面的記錄會變成(11帮碰,2)和(20相味,2),這個發(fā)上在主庫的數(shù)據(jù)變更大家都能理解殉挽。

因為事務的隔離級別是read committed丰涉,所以拓巧,事務1在更新時,只會對b=2這行加上行級鎖一死,不會影響到事務2對b=1這行的寫操作肛度。

以上兩個事務執(zhí)行之后,會在bin log中記錄兩條記錄投慈,因為事務2先提交承耿,所以UPDATE t1 SET b=2 where b=1;會被優(yōu)先記錄,然后再記錄UPDATE t1 SET a=11 where b=2;(再次提醒:statement格式的bin log記錄的是SQL語句的原文)

這樣bin log同步到備庫之后伪煤,SQL語句回放時加袋,會先執(zhí)行UPDATE t1 SET b=2 where b=1;,再執(zhí)行UPDATE t1 SET a=11 where b=2;抱既。

這時候职烧,數(shù)據(jù)庫中的數(shù)據(jù)就會變成(11,2)和(11防泵,2)蚀之。這就導致主庫和備庫的數(shù)據(jù)不一致了!=菖ⅰ足删!

為了避免這樣的問題發(fā)生。MySQL就把數(shù)據(jù)庫的默認隔離級別設置成了Repetable Read锁右,那么壹堰,Repetable Read的隔離級別下是如何解決這樣問題的那?

那是因為Repetable Read這種隔離級別骡湖,會在更新數(shù)據(jù)的時候不僅對更新的行加行級鎖,還會增加GAP lock峻厚。上面的例子响蕴,在事務2執(zhí)行的時候,因為事務1增加了GAP lock惠桃,就會導致事務執(zhí)行被卡住浦夷,需要等事務1提交或者回滾后才能繼續(xù)執(zhí)行。

除了設置默認的隔離級別外辜王,MySQL還禁止在使用statement格式的bin log的情況下劈狐,使用READ COMMITTED作為事務隔離級別。

一旦用戶主動修改隔離級別呐馆,嘗試更新時肥缔,會報錯:

ERROR1598(HY000):Binaryloggingnotpossible. Message: Transaction level'READ-COMMITTED' in InnoDB is not safe for binlog mode 'STATEMENT'

小結(jié)

所以,為什么MySQL選擇RR作為默認的數(shù)據(jù)庫隔離級別汹来,其實就是為了兼容歷史上的那種statement格式的bin log续膳。



那么改艇,為啥阿里要把這個數(shù)據(jù)庫隔離級別修改成 RC 呢,背后有什么思考嗎坟岔?

RR 和 RC 的區(qū)別

想要搞清楚這個問題谒兄,我們需要先弄清楚 RR 和 RC 的區(qū)別,分析下各自的優(yōu)缺點社付。

一致性讀

一致性讀承疲,又稱為快照讀∨缚В快照即當前行數(shù)據(jù)之前的歷史版本燕鸽。快照讀就是使用快照信息顯示基于某個時間點的查詢結(jié)果扛或,而不考慮與此同時運行的其他事務所執(zhí)行的更改绵咱。

在MySQL 中,只有READ COMMITTED 和 REPEATABLE READ這兩種事務隔離級別才會使用一致性讀熙兔。

在 RC 中悲伶,每次讀取都會重新生成一個快照,總是讀取行的最新版本住涉。

在 RR 中麸锉,快照會在事務中第一次SELECT語句執(zhí)行時生成,只有在本事務中對數(shù)據(jù)進行更改才會更新快照舆声。

在數(shù)據(jù)庫的RC 這種隔離級別中花沉,還支持"半一致讀",一條update語句媳握,如果 where 條件匹配到的記錄已經(jīng)加鎖碱屁,那么InnoDB會返回記錄最近提交的版本,由MySQL上層判斷此是否需要真的加鎖蛾找。

鎖機制

數(shù)據(jù)庫的鎖娩脾,在不同的事務隔離級別下,是采用了不同的機制的打毛。在 MySQL 中柿赊,有三種類型的鎖,分別是Record Lock幻枉、Gap Lock和 Next-Key Lock碰声。

Record Lock表示記錄鎖,鎖的是索引記錄熬甫。

Gap Lock是間隙鎖胰挑,鎖的是索引記錄之間的間隙。

Next-Key Lock是Record Lock和Gap Lock的組合,同時鎖索引記錄和間隙洽腺。他的范圍是左開右閉的脚粟。

在 RC 中,只會對索引增加Record Lock蘸朋,不會添加Gap Lock和Next-Key Lock核无。

在 RR 中,為了解決幻讀的問題藕坯,在支持Record Lock的同時团南,還支持Gap Lock和Next-Key Lock;

主從同步

在數(shù)據(jù)主從同步時炼彪,不同格式的 binlog 也對事務隔離級別有要求吐根。

MySQL的binlog主要支持三種格式,分別是statement辐马、row以及mixed拷橘,但是,RC 隔離級別只支持row格式的binlog喜爷。如果指定了mixed作為 binlog 格式冗疮,那么如果使用RC,服務器會自動使用基于row 格式的日志記錄檩帐。

而 RR 的隔離級別同時支持statement术幔、row以及mixed三種。

為什么互聯(lián)網(wǎng)公司選擇使用 RC

提升并發(fā)

互聯(lián)網(wǎng)公司和傳統(tǒng)企業(yè)最大的區(qū)別是什么湃密?

高并發(fā)诅挑!

沒錯,互聯(lián)網(wǎng)業(yè)務的并發(fā)度比傳統(tǒng)企業(yè)要高處很多泛源。2020年雙十一當天拔妥,訂單創(chuàng)建峰值達到 58.3 萬筆/秒。

要怎么做才能扛得住這么大的并發(fā)量达箍,要做的没龙、可以做的事情實在是太多了。

而有一個是通過修改數(shù)據(jù)庫的隔離級別來提升并發(fā)度幻梯。

為什么 RC 比 RR 的并發(fā)度要好呢?

首先努释,RC 在加鎖的過程中碘梢,是不需要添加Gap Lock和 Next-Key Lock 的,只對要修改的記錄添加行級鎖就行了伐蒂。

這就使得并發(fā)度要比 RR 高很多煞躬。

另外,因為 RC 還支持"半一致讀",可以大大的減少了更新語句時行鎖的沖突恩沛;對于不滿足更新條件的記錄在扰,可以提前釋放鎖,提升并發(fā)度雷客。

減少死鎖

因為RR這種事務隔離級別會增加Gap Lock和 Next-Key Lock芒珠,這就使得鎖的粒度變大,那么就會使得死鎖的概率增大搅裙。

死鎖:一個事務鎖住了表A皱卓,然后又訪問表B;另一個事務鎖住了表B部逮,然后企圖訪問表A娜汁;這時就會互相等待對方釋放鎖,就導致了死鎖兄朋。

總結(jié)

MySQL數(shù)據(jù)庫的 RR 和 RC 兩種事務隔離級別掐禁,主要在加鎖機制、主從同步以及一致性讀方面存在一些差異颅和。

而很多大廠傅事,為了提升并發(fā)度和降低死鎖發(fā)生的概率,會把數(shù)據(jù)庫的隔離級別從默認的 RR 調(diào)整成 RC融虽。

當然享完,這樣做也不是完全沒有問題,首先使用 RC 之后有额,就需要自己解決幻讀的問題般又,這個很多時候幻讀問題其實是可以忽略的,或者可以用其他手段解決巍佑。

還有就是使用 RC 的時候茴迁,不能使用statement格式的 binlog,這種影響其實可以忽略不計了萤衰,因為MySQL是在5.1.5版本開始支持row的堕义、在5.1.8版本中開始支持mixed,后面這兩種可以代替 statement格式脆栋。


那么我們回答了以下問題倦卖,

1、RR和RC到底有什么區(qū)別椿争?RR是如何解決不可重復讀問題的怕膛?

2、既然MySQL數(shù)據(jù)庫默認選擇了RR秦踪,那么褐捻,為啥大的互聯(lián)網(wǎng)公司會把默認的隔離級別改成RC掸茅?

然而你或許還會有以下問題:

1、row格式和statement有什么區(qū)別柠逞?使用row的情況下昧狮,可以使用RR嗎?

2板壮、文中提到的RC的GAP lock到底是什么逗鸣?Next-key Lock


?數(shù)據(jù)庫使用鎖是為了支持更好的并發(fā),提供數(shù)據(jù)的完整性和一致性个束。InnoDB是一個支持行鎖的存儲引擎慕购,鎖的類型有:共享鎖(S)、排他鎖(X)茬底、意向共享(IS)沪悲、意向排他(IX)。為了提供更好的并發(fā)阱表,InnoDB提供了非鎖定讀:不需要等待訪問行上的鎖釋放殿如,讀取行的一個快照。該方法是通過InnoDB的一個特性:MVCC來實現(xiàn)的最爬。

InnoDB有三種行鎖的算法:

1涉馁,Record Lock:單個行記錄上的鎖。

2爱致,Gap Lock:間隙鎖烤送,鎖定一個范圍,但不包括記錄本身糠悯。GAP鎖的目的帮坚,是為了防止同一事務的兩次當前讀,出現(xiàn)幻讀的情況互艾。

3试和,Next-Key Lock:1+2,鎖定一個范圍纫普,并且鎖定記錄本身阅悍。對于行的查詢,都是采用該方法昨稼,主要目的是解決幻讀的問題节视。


mysql binlog的三種格式簡單概括總結(jié)

1、三種格式:row假栓、statement寻行、mixed

2、區(qū)別:row格式文件比較大但指,statement比較小寡痰,row格式保存的是一行一行的數(shù)據(jù),statement保存的是sql語句棋凳,mixed格式介于二者之間拦坠,statement容易丟數(shù)據(jù),row格式則不會

3剩岳、statement容易丟數(shù)據(jù)原因是贞滨,有時候,SQL語句里面會用到一些函數(shù)拍棕,比如說取當前日期的函數(shù)sysdate晓铆,你要是用statement,binlog里同步過去的就是這個帶有函數(shù)的SQL語句绰播,而主庫的當前日期骄噪,和binlog同步到slave上的當前日期,肯定是有差異的蠢箩,這樣兩條數(shù)據(jù)就不一致了链蕊,所以這樣同步的數(shù)據(jù),就會有問題

4谬泌、row是直接把表插入到備份庫中滔韵,statement是導出主庫語句后,導入到備份庫中掌实,存在時間差陪蜻。

每種格式的概括

STATEMENT

記錄的是執(zhí)行的SQL語句

優(yōu)點:

日志記錄量相對較小, 節(jié)約磁盤及網(wǎng)絡IO

缺點:

可能造成MySQL復制的主備服務器數(shù)據(jù)不一致

必須記錄上下文信息, 以保證語句在從服務器上執(zhí)行結(jié)果相同

對于特定函數(shù)如 UUID(), user() 這種非確定性函數(shù)是無法正確復制

ROW

記錄的是每一行數(shù)據(jù)的修改, MySQL5.7+的默認ROW格式.

優(yōu)點:

可以避免MySQL復制中出現(xiàn)主從不一致的問題

對每一行數(shù)據(jù)的修改比STATEMENT模式高效

可在誤刪改數(shù)據(jù)后, 同時無備份可以恢復時, 通過分析binlog日志進行反向處理達到恢復數(shù)據(jù)目的

缺點:

由于記錄每一行數(shù)據(jù)的修改, 所以日志量比較大

可通過binlog_row_image=FULL | MINIMAL | NOBLOB 設置日志記錄的方式.

FULL: 記錄行中所有列修改前后的數(shù)據(jù).

MINIMAL: 記錄行中所有列修改前的數(shù)據(jù)+被修改列修改后的數(shù)據(jù).

NOBLOB: 記錄行中所有列修改前的數(shù)據(jù)+(未對行中TEXT和BLOB類型列修改時, 記錄TEXT和BLOB類型以外的列的數(shù)據(jù).)

MIXED

混合STATEMENT和ROW兩種格式, MySQL會根據(jù)執(zhí)行的SQL語句自動選擇.

一般的復制使用STATEMENT格式,對于STATEMENT格式無法復制的操作使用ROW格式.

如何選擇binlog日志格式?

在同一個IDC機房中, 建議使用MIXED或ROW格式, 當使用ROW格式時, 建議設置binlog_row_image=MINIMAL


關(guān)于以上幾個問題贱鼻,你對哪個更感興趣呢宴卖?

talk is easy, show me the code

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市忱嘹,隨后出現(xiàn)的幾起案子嘱腥,更是在濱河造成了極大的恐慌,老刑警劉巖拘悦,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件齿兔,死亡現(xiàn)場離奇詭異,居然都是意外死亡础米,警方通過查閱死者的電腦和手機分苇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來屁桑,“玉大人医寿,你說我怎么就攤上這事∧⒏” “怎么了靖秩?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵须眷,是天一觀的道長。 經(jīng)常有香客問我沟突,道長花颗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任惠拭,我火速辦了婚禮扩劝,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘职辅。我一直安慰自己棒呛,他們只是感情好,可當我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布域携。 她就那樣靜靜地躺著簇秒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪秀鞭。 梳的紋絲不亂的頭發(fā)上宰睡,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天,我揣著相機與錄音气筋,去河邊找鬼拆内。 笑死,一個胖子當著我的面吹牛宠默,可吹牛的內(nèi)容都是我干的麸恍。 我是一名探鬼主播,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼搀矫,長吁一口氣:“原來是場噩夢啊……” “哼抹沪!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起瓤球,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤融欧,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后卦羡,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體噪馏,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年绿饵,在試婚紗的時候發(fā)現(xiàn)自己被綠了欠肾。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡拟赊,死狀恐怖刺桃,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情吸祟,我是刑警寧澤瑟慈,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布桃移,位于F島的核電站,受9級特大地震影響葛碧,放射性物質(zhì)發(fā)生泄漏谴轮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一吹埠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧疮装,春花似錦缘琅、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至樊展,卻和暖如春呻纹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背专缠。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工雷酪, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人涝婉。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓哥力,卻偏偏與公主長得像,于是被迫代替她去往敵國和親墩弯。 傳聞我的和親對象是個殘疾皇子吩跋,可洞房花燭夜當晚...
    茶點故事閱讀 45,055評論 2 355

推薦閱讀更多精彩內(nèi)容