我們有一次mysql崩潰,重啟後發現有些已經提交的事務對資料的修改丟失了,不是說事務能保證acid特性麼,想問下什麼情況下可能導致「事務已經提交,資料卻丟失」呢?
事務已提交,資料卻丟了,趕緊檢查下這個配置
這個問題有點複雜,且容我系統性梳理下思路,先從redo log說起吧。
為什麼要有redo log?
事務提交後,必須將事務對資料頁的修改刷(fsync)到磁碟上,才能保證事務的acid特性。
這個刷盤,是乙個隨機寫,隨機寫效能較低,如果每次事務提交都刷盤,會極大影響資料庫的效能。
隨機寫效能差,有什麼優化方法呢?
架構設計中有兩個常見的優化方法:
(1)先寫日誌(write log first),將隨機寫優化為順序寫;
(2)將每次寫優化為批量寫;
這兩個優化,資料庫都用上了。
先說第乙個優化,將對資料的修改先順序寫到日誌裡,這個日誌就是redo log。
假如某一時刻,資料庫崩潰,還沒來得及將資料頁刷盤,資料庫重啟時,會重做redo log裡的內容,以保證已提交事務對資料的影響被刷到磁碟上。
一句話,redo log是為了保證已提交事務的acid特性,同時能夠提高資料庫效能的技術。
既然redo log能保證事務的acid特性,那為什麼還會出現,水友提問**現的「資料庫奔潰,丟資料」的問題呢?一起看下redo log的實現細節。
redo log的三層架構?
事務已提交,資料卻丟了,趕緊檢查下這個配置
畫了乙個醜圖,簡單說明下redo log的三層架構:
粉色,是innodb的一項很重要的記憶體結構(in-memory structure),日誌緩衝區(log buffer),這一層,是mysql應用程式使用者態
屎黃色,是作業系統的緩衝區(os cache),這一層,是os核心態
藍色,是落盤的日誌檔案
redo log最終落盤的步驟如何?
首先,事務提交的時候,會寫入log buffer,這裡呼叫的是mysql自己的函式writeredolog;
接著,只有當mysql發起系統呼叫寫檔案write時,log buffer裡的資料,才會寫到os cache。注意,mysql系統呼叫完write之後,就認為檔案已經寫完,如果不flush,什麼時候落盤,是作業系統決定的;
畫外音:有時候打日誌,明明printf了,tail -f卻看不到,就是這個原因,這個細節在《明明列印到檔案了,為啥tail -f看不到》一文裡說過,此處不再展開。
最後,由作業系統(當然,mysql也可以主動flush)將os cache裡的資料,最終fsync到磁碟上;
作業系統為什麼要緩衝資料到os cache裡,而不直接刷盤呢?
這裡就是將「每次寫」優化為「批量寫」,以提高作業系統效能。
資料庫為什麼要緩衝資料到log buffer裡,而不是直接write呢?
這也是「每次寫」優化為「批量寫」思路的體現,以提高資料庫效能。
畫外音:這個優化思路,非常常見,高併發的mq落盤,高併發的業務資料落盤,都可以使用。
redo log的三層架構,mysql做了一次批量寫優化,os做了一次批量寫優化,確實能極大提公升效能,但有什麼***嗎?
畫外音:有優點,必有缺點。
這個***,就是可能丟失資料:
(1)事務提交時,將redo log寫入log buffer,就會認為事務提交成功;
(2)如果寫入log buffer的資料,write入os cache之前,資料庫崩潰,就會出現資料丟失;
(3)如果寫入os cache的資料,fsync入磁碟之前,作業系統奔潰,也可能出現資料丟失;
畫外音:如上文所說,應用程式系統呼叫完write之後(不可能每次write後都立刻flush,這樣寫日誌很蠢),就認為寫成功了,作業系統何時fsync,應用程式並不知道,如果作業系統崩潰,資料可能丟失。
任何脫離業務的技術方案都是耍流氓:
(1)有些業務允許低效,但不允許一丁點資料丟失;
(2)有些業務必須高效能高吞吐,能夠容忍少量資料丟失;
mysql是如何折衷的呢?
mysql有乙個引數:
innodb_flush_log_at_trx_commit
能夠控制事務提交時,刷redo log的策略。
目前有三種策略:
事務已提交,資料卻丟了,趕緊檢查下這個配置
策略一:最佳效能(innodb_flush_log_at_trx_commit=0)
每隔一秒,才將log buffer中的資料批量write入os cache,同時mysql主動fsync。
這種策略,如果資料庫奔潰,有一秒的資料丟失。
策略二:強一致(innodb_flush_log_at_trx_commit=1)
每次事務提交,都將log buffer中的資料write入os cache,同時mysql主動fsync。
這種策略,是innodb的預設配置,為的是保證事務acid特性。
策略三:折衷(innodb_flush_log_at_trx_commit=2)
每次事務提交,都將log buffer中的資料write入os cache;
每隔一秒,mysql主動將os cache中的資料批量fsync。
畫外音:磁碟io次數不確定,因為作業系統的fsync頻率並不是mysql能控制的。
這種策略,如果作業系統奔潰,最多有一秒的資料丟失。
畫外音:因為os也會fsync,mysql主動fsync的週期是一秒,所以最多丟一秒資料。
事務已提交,資料卻丟了,趕緊檢查下這個配置
講了這麼多,回到水友的提問上來,資料庫崩潰,重啟後丟失了資料,有很大的可能,是將innodb_flush_log_at_trx_commit引數設定為0了,這位水友最好和dba一起檢查一下innodb的配置。
可能有水友要問,高併發的業務,innodb運用哪種刷盤策略最合適?
高併發業務,行業最佳實踐,是使用第三種折衷配置(=2),這是因為:
(1)配置為2和配置為0,效能差異並不大,因為將資料從log buffer拷貝到os cache,雖然跨越使用者態與核心態,但畢竟只是記憶體的資料拷貝,速度很快;
(2)配置為2和配置為0,安全性差異巨大,作業系統崩潰的概率相比mysql應用程式崩潰的概率,小很多,設定為2,只要作業系統不奔潰,也絕對不會丟資料。
總結
一、為了保證事務的acid特性,理論上每次事務提交都應該刷盤,但此時效率很低,有兩種優化方向:
(1)隨機寫優化為順序寫;
(2)每次寫優化為批量寫;
二、redo log是一種順序寫,它有三層架構:
(1)mysql應用層:log buffer
(2)os核心層:os cache
(3)os檔案:log file
三、為了滿足不用業務對於吞吐量與一致性的需求,mysql事務提交時刷redo log有三種策略:
(1)0:每秒write一次os cache,同時fsync刷磁碟,效能好;
(2)1:每次都write入os cache,同時fsync刷磁碟,一致性好;
(3)2:每次都write入os cache,每秒fsync刷磁碟,折衷;
四、高併發業務,行業內的最佳實踐,是:innodb_flush_log_at_trx_commit=2
事務已提交讀
事務已提交讀 在事務沒有結束之前,是不能讀取事務中操作的資料的。a begin tran update dbo.t1 set col2 version 2 where keycol 2 select col2 from dbo.t1 where keycol 2 b set transaction ...
TortoiseSVN 忽略檔案 忽略已提交檔案
主要以下兩種情況 1 首次提交就做好了忽略攔截 專案首次提交到svn伺服器的時候,把該刪的刪了,然後設定忽略規則,就沒問題了。實現步驟 1 設定svn忽略檔案 右鍵空白處 tortoisesvn settings general 右邊的subversion 輸入 忽略檔案如 dll 忽略資料夾如 b...
SVN修改已提交版本的Log
在工作中一直是使用svn進行專案的版本控制的,有時候由於提交匆忙,或是忘了新增log,或是log內容寫的有錯誤。今日遇到此類情況,想要在檢視專案的日誌時新增log或是修改log內容,遇到如下錯誤 repository has not been enabled to accept revision p...