update S S.c=S.c+1 where id=2
更新語句與查詢語句差不多灼伤,要經(jīng)過連接器,緩存在更新后會(huì)失效咪鲜,所以一般建議不用查詢緩存狐赡,然后經(jīng)過分析器,優(yōu)化器疟丙,執(zhí)行器颖侄,但更新語句重點(diǎn)是會(huì)涉及到兩個(gè)日志模塊:redo_log(重做日志)和binlog(歸檔日志)
如果每一次的更新操作都需要寫進(jìn)磁盤,然后磁盤也要找到對(duì)應(yīng)的那條記錄享郊,然后再更新览祖,整個(gè)過程 IO 成本、查找成本都很高炊琉。所以當(dāng)有一條記錄需要更新的時(shí)候展蒂,InnoDB 引擎就會(huì)先把記錄寫到 redo log里面,并更新內(nèi)存苔咪,這個(gè)時(shí)候更新就算完成了锰悼。同時(shí),InnoDB 引擎會(huì)在空閑的時(shí)候悼泌,再將這個(gè)操作記錄更新到磁盤里面松捉。有了 redo log,InnoDB 就可以保證即使數(shù)據(jù)庫發(fā)生異常重啟馆里,之前提交的記錄都不會(huì)丟失隘世,這個(gè)能力稱為 crash-safe。
那為什么要有兩份日志鸠踪?
這兩種日志有以下三點(diǎn)不同丙者。
- redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 層實(shí)現(xiàn)的营密,所有引擎都可以使用械媒。
- redo log 是物理日志,記錄的是“在某個(gè)數(shù)據(jù)頁上做了什么修改”评汰;binlog 是邏輯日志纷捞,記錄的是這個(gè)語句的原始邏輯,比如“給 ID=2 這一行的 c 字段加 1 ”被去。
- redo log 是循環(huán)寫的主儡,空間固定會(huì)用完;binlog 是可以追加寫入的惨缆∶又担“追加寫”是指 binlog 文件寫到一定大小后會(huì)切換到下一個(gè),并不會(huì)覆蓋以前的日志坯墨。
最開始 MySQL 里并沒有 InnoDB 引擎寂汇。MySQL 自帶的引擎是 MyISAM,但是 MyISAM 沒有 crash-safe 的能力捣染,binlog 日志只能用于歸檔骄瓣。
假如只有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ù)庫更新失敗示损,這樣也會(huì)導(dǎo)致主從數(shù)據(jù)庫不一致或者無法恢復(fù)數(shù)據(jù)庫渗磅。所以只有binlog做不到crash-safe。
而 InnoDB 是另一個(gè)公司以插件形式引入 MySQL 的检访,既然只依靠 binlog 是沒有 crash-safe 能力的始鱼,所以 InnoDB 使用另外一套日志系統(tǒng)——也就是 redo log 來實(shí)現(xiàn) crash-safe 能力
執(zhí)行流程大概如下:
為了保證邏輯一致,事務(wù)提交需要兩個(gè)階段:prepare階段和commit階段脆贵。更新內(nèi)存后緊接寫入redo_log(prepare狀態(tài))-->寫binlog-->commit(狀態(tài))表示事務(wù)已提交医清,這叫兩階段提交,在這兩個(gè)階段內(nèi)有任何一個(gè)環(huán)節(jié)出錯(cuò)都會(huì)導(dǎo)致事務(wù)回滾卖氨,這樣才能保證數(shù)據(jù)的一致性会烙。
如果不采用兩階段事務(wù)提交方式负懦,在兩份日志都可以提交事務(wù)的情況下,假設(shè)執(zhí)行 update 語句過程中在寫完第一個(gè)日志后柏腻,第二個(gè)日志還沒有寫完期間發(fā)生了 crash纸厉,會(huì)出現(xiàn)什么情況呢?
- 先寫 redo log 后寫 binlog五嫂。假設(shè)在 redo log 寫完颗品,binlog 還沒有寫完的時(shí)候,MySQL 進(jìn)程異常重啟沃缘。由于我們前面說過的躯枢,redo log 寫完之后,系統(tǒng)即使崩潰槐臀,仍然能夠把數(shù)據(jù)恢復(fù)回來锄蹂,所以恢復(fù)后這一行 c 的值是 1。但是由于 binlog 沒寫完就 crash 了峰档,這時(shí)候 binlog 里面就沒有記錄這個(gè)語句败匹。因此,之后備份日志的時(shí)候讥巡,存起來的 binlog 里面就沒有這條語句。然后你會(huì)發(fā)現(xiàn)欢顷,如果需要用這個(gè) binlog 來恢復(fù)臨時(shí)庫的話槽棍,由于這個(gè)語句的 binlog 丟失,這個(gè)臨時(shí)庫就會(huì)少了這一次更新抬驴,恢復(fù)出來的這一行 c 的值就是 0炼七,與原庫的值不同。
- 先寫 binlog 后寫 redo log布持。如果在 binlog 寫完之后 crash豌拙,由于 redo log 還沒寫,崩潰恢復(fù)以后這個(gè)事務(wù)無效题暖,所以這一行 c 的值是 0按傅。但是 binlog 里面已經(jīng)記錄了“把 c 從 0 改成 1”這個(gè)日志。所以胧卤,在之后用 binlog 來恢復(fù)的時(shí)候就多了一個(gè)事務(wù)出來唯绍,恢復(fù)出來的這一行 c 的值就是 1,與原庫的值不同枝誊。
參考文章:鏈接:http://www.reibang.com/p/2f1585c7f2f3
參考文章《MySql實(shí)戰(zhàn)45講》