一文講懂?dāng)?shù)據(jù)庫(kù)隔離級(jí)別

文 |?豆豆
來源:Python 技術(shù)「ID: pythonall」

相信做后端開發(fā)的童鞋肯定沒少和數(shù)據(jù)庫(kù)打交道,提起數(shù)據(jù)庫(kù),又不能不說數(shù)據(jù)庫(kù)事務(wù)隔離級(jí)別,畢竟這是保證數(shù)據(jù)可靠一致的重要基石。網(wǎng)上介紹數(shù)據(jù)庫(kù)隔離級(jí)別的文章大都很生硬,理論居多,今天派森醬就用一個(gè)簡(jiǎn)單的故事來給大家說說隔離級(jí)別。
奮斗多年,派森醬攢了些積蓄,想著總給別人打工也不是長(zhǎng)久之計(jì),于是萌生辭職創(chuàng)業(yè)的念頭,和自己的同窗好友蟒蛇君合計(jì)之后覺得可行,于是就炒了老板魷魚自己當(dāng)老板去啦。
別看派森醬這幾年在公司悶頭寫代碼,業(yè)余之際也沒忘記給自己充電。產(chǎn)品,運(yùn)營(yíng),市場(chǎng),公關(guān),財(cái)務(wù)等他都略知一二,本著勤儉節(jié)約的原則,派森醬每周都會(huì)拉著蟒蛇君一起對(duì)賬,以便控制不必要的開支,這天他們突然發(fā)現(xiàn)公司賬面上的錢和實(shí)際的好像對(duì)不上,公司賬上少了一萬大洋。這可把倆人嚇壞了。
數(shù)據(jù)丟失
從傍晚核對(duì)到凌晨,二人終于發(fā)現(xiàn)了其中的貓膩,原來是因?yàn)樗麄兺瑫r(shí)操作了公司賬戶。

派森醬添加的一萬大洋給丟失了。
這可難不倒派森醬,畢竟程序員出身,很快就找到了解決之道。加鎖呀,遇事不決先加鎖[狗頭]。他們把這個(gè)鎖叫做 X 鎖,該鎖有一個(gè)特點(diǎn),就是在同一時(shí)刻只可以有一人持有,在未釋放之前,另外一個(gè)人無法獲得該鎖。于是,通過加鎖的方式巧妙地將二人的并行操作變成了串行,完美地解決了同時(shí)修改同一行數(shù)據(jù)造成數(shù)據(jù)丟失的問題。

這就是數(shù)據(jù)庫(kù)最低的隔離級(jí)別 「Read uncommitted 讀未提交」。可以解決數(shù)據(jù)丟失問題。
臟讀
自此以后,派森醬和蟒蛇君在寫數(shù)據(jù)的時(shí)候都要加上 X 鎖才行,程序運(yùn)行了很久都沒發(fā)現(xiàn)什么問題,直到有一天,公司賬上又多了一萬大洋。
蟒蛇君說:老弟,是不是我們的鎖有問題呀。
“不可能,加鎖我可是專業(yè)的”。派森醬說。
說完,派森醬立馬在腦海中構(gòu)思著各種可能,終于,想到了些什么。

你讀取到了我回滾的數(shù)據(jù),所以,錢就多了。
”老弟,我讀取數(shù)據(jù)的時(shí)候,你不能回滾啊,這樣我讀取的數(shù)據(jù)就是不靠譜的了,他們已經(jīng)是「臟數(shù)據(jù)」了“。蟒蛇君怨聲載道。
派森醬:“那怎么行,難道我操作錯(cuò)了也不可以回滾嗎,這也太霸道了。要不我們讀取數(shù)據(jù)的時(shí)候也加一個(gè) X 鎖吧?!?/p>
不不不,這樣子太嚴(yán)格了,但是我們可以弄一個(gè)新的鎖出來,簡(jiǎn)稱 S 鎖,用于讀取數(shù)據(jù)。
這個(gè)鎖和 X 鎖有所區(qū)別,如果一行數(shù)據(jù)加了 X 鎖,就不可以再加 S 鎖了,同樣加了 S 鎖也不可以再加 X 鎖,但是加了 S 鎖的記錄可以再加 S 鎖。簡(jiǎn)言之就是 X 鎖和 X 鎖是互斥的,X 鎖和 S 鎖也是互斥的,但 S 鎖和 S 鎖不是互斥的。
同時(shí)為了避免影響數(shù)據(jù)的寫入,讀取完數(shù)據(jù)之后我們要立馬釋放 S 鎖。
于是,臟數(shù)據(jù)的問題也被派森醬給解決了。
這就是二級(jí)隔離級(jí)別 「Read committed 讀提交」??梢越鉀Q數(shù)據(jù)丟失、臟讀問題。

不可重復(fù)讀
又到了周末對(duì)賬時(shí)刻,剛好是月底,派森醬要核對(duì)下上月余額與當(dāng)前月余額的差額,是否和明細(xì)都能對(duì)得上。由于本月流水較多,對(duì)到一半倆人有點(diǎn)累了,于是他們就先把差額給記錄了下來,假設(shè)為 10000 - 5000 = 5000,接著就去吃飯去了,飯后回去接著對(duì),這一停不要緊,回來后發(fā)現(xiàn)錢變少了,倆人心態(tài)崩了。
怎么回事,二人瘋狂分析日志,看看能否找到蛛絲馬跡。
原來在他們吃飯的過程中,銀行自動(dòng)扣了本月稅收。這可尷尬了,我讀數(shù)據(jù)時(shí),咋還有人改呢。剛開始是 5000?,現(xiàn)在變成了 4000。

問題就出在 S 鎖的釋放時(shí)刻,讀取完立馬釋放,就容易被人見縫插針,我們要一直持有 S 鎖才行,直到把事務(wù)提交上去。
于是,不可重復(fù)讀的問題也被解決。

這就是三級(jí)隔離級(jí)別「Repeatable read 可重復(fù)讀」。可以解決數(shù)據(jù)丟失、臟讀、不可重復(fù)讀問題。這也是 MySQL 的默認(rèn)隔離級(jí)別。
幻讀
后來公司越做越大,公司員工也越來越多,這天派森醬心情大好,就想著給公司的員工多發(fā)點(diǎn)福利,本月每人增加 1000 績(jī)效獎(jiǎng),于是執(zhí)行了下面的語句。
SELECT?name,?salary?FROM?xxx;
UPDATE?xxx?SET?salary?=?salary?+?1000;
SELECT?name,?salary?FROM?xxx;
更新完之后派森醬想確認(rèn)下是否全部都修改了,就又查詢了一遍,這一查發(fā)現(xiàn)果然有一個(gè)人沒有改。
原來,在派森醬查詢和更新的中間,蟒蛇君又新增了一條數(shù)據(jù),這又引發(fā)了新的問題。
咋處理個(gè)數(shù)據(jù)就這么麻煩呢。
沒辦法,要想避免這個(gè)問題,我們只能一個(gè)一個(gè)串行執(zhí)行,要保證同一時(shí)刻只能有一個(gè)人操作數(shù)據(jù)庫(kù)。
這就是隔離界別的最高級(jí)別了 「Serializable 串行化」。可以解決數(shù)據(jù)丟失、臟讀、不可重復(fù)讀、幻讀問題
總結(jié)
至此我們說完了數(shù)據(jù)庫(kù)的隔離級(jí)別,他們是層層遞進(jìn)的關(guān)系,事實(shí)上,通過加鎖的方式完全可以實(shí)現(xiàn),只不過會(huì)嚴(yán)重影響生產(chǎn)效率。
下一篇文章我們來看看數(shù)據(jù)庫(kù)大叔們到底是是如何實(shí)現(xiàn)這么復(fù)雜的隔離級(jí)別的。
參考
https://mp.weixin.qq.com/s/tSF_w9xUOj3Q2hmOxJkwLg
PS:公號(hào)內(nèi)回復(fù)「Python」即可進(jìn)入Python 新手學(xué)習(xí)交流群,一起 100 天計(jì)劃!
老規(guī)矩,兄弟們還記得么,右下角的 “在看” 點(diǎn)一下,如果感覺文章內(nèi)容不錯(cuò)的話,記得分享朋友圈讓更多的人知道!


【代碼獲取方式】
