1.MYSQL(一)---基礎(chǔ)架構(gòu)(查詢)
2.MYSQL(二)---日志系統(tǒng)(更新)
3.MYSQL(三)---事務(wù)隔離
在上文中講到了數(shù)據(jù)庫的架構(gòu)和查詢,在我們使用數(shù)據(jù)庫的時候的最常見的查詢的流程梁只。
數(shù)據(jù)在進行查詢的時候會有緩存缚柳,但是當(dāng)更新了這個表數(shù)據(jù)的時候埃脏,這個緩存就會進行清空,這個數(shù)據(jù)更新的過程是怎么執(zhí)行的呢秋忙?
同時作為開發(fā)我們肯定是了解數(shù)據(jù)庫使用日志的重要性彩掐,數(shù)據(jù)的丟失恢復(fù)則是需要日志系統(tǒng)。那么我們是如何進行準(zhǔn)確的恢復(fù)數(shù)據(jù)的呢灰追?
update T set c=2 where id=2;
這個操作的流程我們還是可以從這個流程圖上來理解:
我們執(zhí)行這條更新語句堵幽,和查詢相同的就是我們先需要找個這條數(shù)據(jù),這個過程和上邊幾戶一樣弹澎,找到數(shù)據(jù)以后進行更新操作朴下,但是就是在這個更新的過程涉及到的新知識,就是redo log和binlog苦蒿。這個實在MySQL5.6開始執(zhí)行的新特性有的殴胧。
redo log
??什么是redo log?
????舉個簡單的例子來說:孔乙己中酒館老板記賒賬依靠的是一個小黑板和賬本,如果賒賬的人不多會寫在黑板上進行記錄佩迟,如果人多了团滥,黑板寫不下,就會找賬本進行記錄报强。
如果有人要進行還賬或者賒賬的話灸姊,會有以下操作:
??????????????????- 直接查賬本,對賬本上進行操作
??????????????????- 在黑板上記錄秉溉,滿了或者打烊了再在賬本進行操作
??在生意火爆的時候厨钻,操作肯定是先記在黑板上,然后在進行賬本上統(tǒng)一操作坚嗜。(這就對比了數(shù)據(jù)庫插入的高并發(fā))。這就是WAL技術(shù)诗充,WAL的全稱是Write-Ahead Logging苍蔬,就是先寫日志,再寫磁盤蝴蜓。
具體來說碟绑,當(dāng)有更新需求來的時候,InnoDB就會把這個更新寫到redo log里面茎匠,并更新內(nèi)存格仲,同時,InnoDB會在空閑的時候?qū)?shù)據(jù)更新進磁盤诵冒。
這個時候會發(fā)現(xiàn)另一個問題凯肋,黑板(redo log)不夠用了怎么辦?這個時候就不要停下來先計入賬本(更新進磁盤)汽馋,同時會將寫進磁盤的數(shù)據(jù)在redo log進行抹除侮东。
有了redo log圈盔,InnoDB就會保證在數(shù)據(jù)庫發(fā)生了異常之后,之前提交的記錄不會丟失悄雅,這個能力稱為crash-safe驱敲。
什么是crash-safe?
CrashSafe指MySQL服務(wù)器宕機重啟后宽闲,能夠保證:
?? ??? 1.所有已經(jīng)提交的事務(wù)的數(shù)據(jù)仍然存在众眨。
?? ??? 2.所有沒有提交的事務(wù)的數(shù)據(jù)自動回滾。
binlog
MySQL是分為兩層的容诬,server和引擎娩梨,其中上邊的redo log是InnoDB引擎中的日志,server中得日志就是binlog放案。
既然是日志姚建,為嘛要分為兩個?
MySQL開始自帶的引擎是MyISAM,主要實現(xiàn)的功能是server層的功能吱殉,但是它沒有crash-safe得功能掸冤,而InnoDB是另一個公司以插件形式引入MySQL的,既然只依靠binlog是沒有crash-safe能力的友雳,所以InnoDB使用另外一套日志系統(tǒng)——也就是redo log來實現(xiàn)crash-safe能力稿湿。
這兩種日志有以下三點不同:
??1.redo log是InnoDB引擎特有的;binlog是MySQL的Server層實現(xiàn)的押赊,所有引擎都可以使用饺藤。
??2.redo log是物理日志,記錄的是“在某個數(shù)據(jù)頁上做了什么修改”流礁;binlog是邏輯日志涕俗,記錄的是這個語句的原始邏輯,比如“給ID=2這一行的c字段加1 ”神帅。
??3.redo log是循環(huán)寫的再姑,空間固定會用完;binlog是可以追加寫入的找御≡疲“追加寫”是指binlog文件寫到一定大小后會切換到下一個,并不會覆蓋以前的日志霎桅。
有了對這兩個日志的概念性理解栖疑,我們再來看執(zhí)行器和InnoDB引擎在執(zhí)行這個簡單的update語句時的內(nèi)部流程。
??1.執(zhí)行器先找引擎取ID=2這一行滔驶。ID是主鍵遇革,引擎直接用樹搜索找到這一行。如果ID=2這一行所在的數(shù)據(jù)頁本來就在內(nèi)存中,就直接返回給執(zhí)行器澳淑;否則比原,需要先從磁盤讀入內(nèi)存,然后再返回杠巡。
??2.執(zhí)行器拿到引擎給的行數(shù)據(jù)量窘,把這個值加上1,比如原來是N氢拥,現(xiàn)在就是N+1蚌铜,得到新的一行數(shù)據(jù),再調(diào)用引擎接口寫入這行新數(shù)據(jù)嫩海。
??3.引擎將這行新數(shù)據(jù)更新到內(nèi)存中冬殃,同時將這個更新操作記錄到redo log里面,此時redo log處于prepare狀態(tài)叁怪。然后告知執(zhí)行器執(zhí)行完成了审葬,隨時可以提交事務(wù)。
??4.執(zhí)行器生成這個操作的binlog奕谭,并把binlog寫入磁盤涣觉。
??5.執(zhí)行器調(diào)用引擎的提交事務(wù)接口,引擎把剛剛寫入的redo log改成提交(commit)狀態(tài)血柳,更新完成官册。
兩階段提交
為什么必須有“兩階段提交”呢?這是為了讓兩份日志之間的邏輯一致难捌。要說明這個問題膝宁,我們得從文章開頭的那個問題說起:怎樣讓數(shù)據(jù)庫恢復(fù)到半個月內(nèi)任意一秒的狀態(tài)?
前面我們說過了根吁,binlog會記錄所有的邏輯操作员淫,并且是采用“追加寫”的形式。如果你的DBA承諾說半個月內(nèi)可以恢復(fù)击敌,那么備份系統(tǒng)中一定會保存最近半個月的所有binlog介返,同時系統(tǒng)會定期做整庫備份。這里的“定期”取決于系統(tǒng)的重要性愚争,可以是一天一備,也可以是一周一備挤聘。
當(dāng)需要恢復(fù)到指定的某一秒時轰枝,比如某天下午兩點發(fā)現(xiàn)中午十二點有一次誤刪表,需要找回數(shù)據(jù)组去,那你可以這么做:
??首先鞍陨,找到最近的一次全量備份,如果你運氣好,可能就是昨天晚上的一個備份诚撵,從這個備份恢復(fù)到臨時庫缭裆;
??然后,從備份的時間點開始寿烟,將備份的binlog依次取出來澈驼,重放到中午誤刪表之前的那個時刻。
這樣你的臨時庫就跟誤刪之前的線上庫一樣了筛武,然后你可以把表數(shù)據(jù)從臨時庫取出來缝其,按需要恢復(fù)到線上庫去。好了徘六,說完了數(shù)據(jù)恢復(fù)過程内边,我們回來說說,為什么日志需要“兩階段提交”待锈。這里不妨用反證法來進行解釋漠其。由于redo log和binlog是兩個獨立的邏輯,如果不用兩階段提交竿音,要么就是先寫完redo log再寫binlog和屎,或者采用反過來的順序。我們看看這兩種方式會有什么問題谍失。
仍然用前面的update語句來做例子眶俩。假設(shè)當(dāng)前ID=2的行,字段c的值是0快鱼,再假設(shè)執(zhí)行update語句過程中在寫完第一個日志后冠王,第二個日志還沒有寫完期間發(fā)生了crash末秃,會出現(xiàn)什么情況呢?
??1.先寫redo log后寫binlog。假設(shè)在redo log寫完懒震,binlog還沒有寫完的時候,MySQL進程異常重啟燥爷。由于我們前面說過的惠窄,redo log寫完之后,系統(tǒng)即使崩潰袄琳,仍然能夠把數(shù)據(jù)恢復(fù)回來询件,所以恢復(fù)后這一行c的值是1。但是由于binlog沒寫完就crash了唆樊,這時候binlog里面就沒有記錄這個語句宛琅。因此,之后備份日志的時候逗旁,存起來的binlog里面就沒有這條語句嘿辟。然后你會發(fā)現(xiàn),如果需要用這個binlog來恢復(fù)臨時庫的話,由于這個語句的binlog丟失红伦,這個臨時庫就會少了這一次更新英古,恢復(fù)出來的這一行c的值就是0,與原庫的值不同昙读。
??2.先寫binlog后寫redo log召调。如果在binlog寫完之后crash,由于redo log還沒寫箕戳,崩潰恢復(fù)以后這個事務(wù)無效某残,所以這一行c的值是0。但是binlog里面已經(jīng)記錄了“把c從0改成1”這個日志陵吸。所以玻墅,在之后用binlog來恢復(fù)的時候就多了一個事務(wù)出來,恢復(fù)出來的這一行c的值就是1壮虫,與原庫的值不同澳厢。
可以看到,如果不使用“兩階段提交”囚似,那么數(shù)據(jù)庫的狀態(tài)就有可能和用它的日志恢復(fù)出來的庫的狀態(tài)不一致剩拢。
你可能會說,這個概率是不是很低饶唤,平時也沒有什么動不動就需要恢復(fù)臨時庫的場景呀徐伐?
其實不是的,不只是誤操作后需要用這個過程來恢復(fù)數(shù)據(jù)募狂。當(dāng)你需要擴容的時候办素,也就是需要再多搭建一些備庫來增加系統(tǒng)的讀能力的時候,現(xiàn)在常見的做法也是用全量備份加上應(yīng)用binlog來實現(xiàn)的祸穷,這個“不一致”就會導(dǎo)致你的線上出現(xiàn)主從數(shù)據(jù)庫不一致的情況性穿。
簡單說,redo log和binlog都可以用于表示事務(wù)的提交狀態(tài)雷滚,而兩階段提交就是讓這兩個狀態(tài)保持邏輯上的一致需曾。
問題:
什么是redog和binlog?
redolog是對記錄修改之后的物理日志,物理日志就是說redolog保存的是某一行數(shù)據(jù)修改之后的值祈远,比如把id=1這行的某個屬性由1改成2呆万,redolog記錄的就是這個2.redolog是InnoDB引擎層的。
相比于redolog车份,binlog是邏輯日志谋减。其中一種形式是記錄的原始sql語句,比如update t set c = c +1 where id = 1; binlog是數(shù)據(jù)庫server層的躬充。
為什么需要redolog?
由于更新數(shù)據(jù)的時候引擎并不是按條更新的逃顶,而是以頁為最小單位更新。如果沒有redolog充甚,每次更新一條數(shù)據(jù)都要把整頁的數(shù)據(jù)刷新到磁盤以政。如果更新多條數(shù)據(jù),很可能一次就要更新多頁伴找,而且這些io是隨機io盈蛮,磁盤i/o就會多且慢。有了redolog技矮,就不需要每次直接按頁更新磁盤抖誉,而是把更新寫到redolog中,然后等空閑時間再把redolog中的更新寫入到磁盤衰倦。這樣做的好處是redolog是順序?qū)懙奶宦沂前礂l不是按頁寫。所以雖然多了一步樊零,實際上是比直接更新快的我磁。
為什么binlog不能做到crash-safe
假如只有binlog,有可能先提交事務(wù)再寫binlog驻襟,有可能事務(wù)提交數(shù)據(jù)更新之后數(shù)據(jù)庫崩了夺艰,還沒來得及寫binlog。我們都知道binlog一般用來做數(shù)據(jù)庫的主從復(fù)制或恢復(fù)數(shù)據(jù)庫沉衣,這樣就導(dǎo)致主從數(shù)據(jù)庫不一致或者無法恢復(fù)數(shù)據(jù)庫了郁副。同樣即使先寫binlog再提交事務(wù)更新數(shù)據(jù)庫,還是有可能寫binlog成功之后數(shù)據(jù)庫崩掉而導(dǎo)致數(shù)據(jù)庫更新失敗豌习,這樣也會導(dǎo)致主從數(shù)據(jù)庫不一致或者無法恢復(fù)數(shù)據(jù)庫存谎。所以只有binlog做不到crash-safe。為了支持crash-safe斑鸦,需要redolog愕贡,而且為了保證邏輯一致,事務(wù)提交需要兩個階段:prepare階段和commit階段巷屿。寫redolog并落入磁盤(prepare狀態(tài))-->寫binlog-->commit固以。commit的時候是不會落盤的。
那么為什么要在prepare階段就落盤呢嘱巾?
如果binlog寫成功之后憨琳,將redolog置成commit的時候數(shù)據(jù)庫崩了,如果在commit的時候redolog才落盤旬昭,由于事務(wù)是否成功以binlog為依據(jù)篙螟,上面的情況下事務(wù)是成功的,但是redolog沒有寫到磁盤问拘,丟了遍略【逅恢復(fù)之后數(shù)據(jù)庫與binlog就不一致了。如果在prepare階段落盤绪杏,上面的情況下redolog已經(jīng)寫入到文件了(在prepare階段已經(jīng)寫盤了)下愈,恢復(fù)的時候不會丟數(shù)據(jù)。
同樣的蕾久,如果不分兩個階段势似。假如redolog和binlog獨立,那么還是會出現(xiàn)“為什么binlog不能做到crash-safe”里面描述的問題:數(shù)據(jù)庫與binlog不一致僧著。
參考連接:
1.http://www.reibang.com/p/2f1585c7f2f3
2.https://blog.csdn.net/shaochenshuo/article/details/73239949