mysql的"雙1設定"-資料安全的關鍵引數(案例分享)

散盡浮華發表於2019-02-27

 

mysql的"雙1驗證"指的是innodb_flush_log_at_trx_commitsync_binlog兩個引數設定,這兩個是是控制MySQL 磁碟寫入策略以及資料安全性的關鍵引數。下面從引數含義,效能,安全形度闡述兩個引數為不同的值時對db 效能,資料的影響。

一、引數意義

innodb_flush_log_at_trx_commit
如果innodb_flush_log_at_trx_commit設定為0:log buffer將每秒一次地寫入log file中,並且log file的flush(刷到磁碟)操作同時進行.該模式下,在事務提交的時候,不會主動觸發寫入磁碟的操作;
如果innodb_flush_log_at_trx_commit設定為1:每次事務提交時MySQL都會把log buffer的資料寫入log file,並且flush(刷到磁碟)中去;
如果innodb_flush_log_at_trx_commit設定為2:每次事務提交時MySQL都會把log buffer的資料寫入log file,但是flush(刷到磁碟)操作並不會同時進行。該模式下,MySQL會每秒執行一次 flush(刷到磁碟)操作。

注意:由於程式排程策略問題,這個"每秒執行一次 flush(刷到磁碟)操作"並不是保證100%的"每秒"。

sync_binlog
sync_binlog 的預設值是0,像作業系統刷其他檔案的機制一樣,MySQL不會同步到磁碟中去而是依賴作業系統來重新整理binary log。
當sync_binlog =N (N>0) ,MySQL 在每寫 N次 二進位制日誌binary log時,會使用fdatasync()函式將它的寫二進位制日誌binary log同步到磁碟中去。

注意:如果啟用了autocommit,那麼每一個語句statement就會有一次寫操作;否則每個事務對應一個寫操作。

二、效能

兩個引數在不同值時對db的純寫入的影響表現如下:
測試場景1
innodb_flush_log_at_trx_commit=2
sync_binlog=1000

測試場景2
innodb_flush_log_at_trx_commit=1
sync_binlog=1000

測試場景3
innodb_flush_log_at_trx_commit=1
sync_binlog=1

測試場景4
innodb_flush_log_at_trx_commit=1
sync_binlog=1000

測試場景5
innodb_flush_log_at_trx_commit=2
sync_binlog=1000

在以上5個場景下的TPS分別為:
場景1            41000
場景2           33000
場景3           26000
場景4           33000

由此可見,當兩個引數設定為雙1的時候,寫入效能最差,sync_binlog=N (N>1 ) innodb_flush_log_at_trx_commit=2 時,(在當前模式下)MySQL的寫操作才能達到最高效能。

三、安全

當innodb_flush_log_at_trx_commit和sync_binlog 都為 1 時是最安全的,在mysqld 服務崩潰或者伺服器主機crash的情況下,binary log 只有可能丟失最多一個語句或者一個事務。但是魚與熊掌不可兼得,雙11 會導致頻繁的io操作,因此該模式也是最慢的一種方式。
當innodb_flush_log_at_trx_commit設定為0,mysqld程式的崩潰會導致上一秒鐘所有事務資料的丟失。
當innodb_flush_log_at_trx_commit設定為2,只有在作業系統崩潰或者系統掉電的情況下,上一秒鐘所有事務資料才可能丟失。

"雙1設定"適合資料安全性要求非常高,而且磁碟IO寫能力足夠支援業務,比如訂單,交易,充值,支付消費系統。雙1模式下,當磁碟IO無法滿足業務需求時 比如11.11 活動的壓力。推薦的做法是 innodb_flush_log_at_trx_commit=2 ,sync_binlog=N (N為500 或1000) 且使用帶蓄電池後備電源的快取cache,防止系統斷電異常。

四、小結

系統效能和資料安全是業務系統高可用穩定的必要因素。我們在對系統的優化需要尋找一個平衡點,合適的才是最好的,根據不同的業務場景需求,可以將兩個引數做組合調整,以便是db系統的效能達到最優化。

                                                 案例分享1:一條insert語句的執行,耗時40ms原因剖析                                             

背景:一個簡單的帶有主鍵的insert語句,執行起來居然要耗時40ms ,實在是難以忍受!排查分析過程如下:

因此需要關注的是資料從插入落地的IO中間都幹了什麼?

一、MySQL的檔案
首先簡單介紹一下MySQL的資料檔案,MySQL 資料庫包含如下幾種檔案型別:
1)資料檔案 (datafile)
存放表中的具體資料的檔案。
2)資料字典
記錄資料庫中所有innodb表的資訊。
3)重做日誌 (redolog)
記錄資料庫變更記錄的檔案,用於系統異常crash(掉電)後的恢復操作,可以配置多個(配置這個引數inodb_log_files_in_group)比如 ib_logfile0、 ib_logfile1。
4)回滾日誌 (undolog)
也存在於mysql 的ibdata檔案,使用者記錄事務的回滾操作。注在mysql5.6以上版本可以拆開出來,單獨資料夾存在。
5)歸檔日誌 (binlog)
事務提交之後,記錄到歸檔日誌中。
6)中繼日誌 (relaylog)
從master獲取到slave的中轉日誌檔案,sql_thread則會應用relay log並重放於從機器。
7)其他日誌slowlolg, errorlog, querylog
這裡慢日誌也經常用。可以結合pt-query-digest工具和anemometer一起展示出來。
對於以上檔案的IO訪問順序可以分為順序訪問 比如binlog ,redolog ,relay log是順序讀寫,datafile,ibdata file是隨機讀寫,這些IO訪問的特點決定了在os 配置磁碟資訊時候,如何考慮分割槽 ,比如順序寫可以的log可以放到SAS盤 ,隨機讀寫的資料檔案可以放到ssd或者fio高效能的儲存。

二、寫操作
為了保證資料寫入操作的安全性,資料庫系統設定了 undo,redo 保護機制,避免因為os或者資料庫系統異常導致的資料丟失或者不一致的異常情況發生。

1)先寫undo log。
2)在記憶體更新資料,這步操作就在記憶體中形成了髒頁,如果髒頁過多,checkpoint機制進行重新整理,innodb_max_dirty_pages_pct決定了重新整理髒頁比例。innodb_io_capacity引數可以動態調整重新整理髒頁的數量,innodb_lru_scan_depth這個引數決定了重新整理每個innodb_buffer_pool的髒頁數量。
3)記錄變更到redo log,prepare這裡會寫事務id。innodb_flush_log_at_trx_commit決定了事務的刷盤方式。為0時,log buffer將每秒一次地寫入log file中,並且log file的flush(刷到磁碟)操作同時進行。該模式下,在事務提交的時候,不會主動觸發寫入磁碟的操作。為1,每次事務提交時MySQL都會把log buffer的資料寫入log file,並且flush(刷到磁碟)中去.為2,每次事務提交時MySQL都會把log buffer的資料寫入log file.但是flush(刷到磁碟)操作並不會同時進行。該模式下,MySQL會每秒執行一次 flush(刷到磁碟)操作。
4)寫入binlog這裡會寫入一個事務id這裡有個sync_binlog引數決定多個事務進行一次性提交
5)redo log第二階段,這裡會進行判斷前2步是否成功,成功則預設commit,否則rollback。刷入磁碟操作。這裡是先從髒頁資料刷入到記憶體2M大小的doublewrite buffer,然後是一次性從記憶體的doublewrite buffer重新整理到共享表空間的doublewrite buffer,這裡產生了一次IO。然後從記憶體的記憶體的doublewrite buffer重新整理2m資料到磁碟的ibd檔案中,這裡需要發生128次io。然後校驗,如果不一致,就由共享表空間的副本進行修復。這裡有個引數innodb_flush_method決定了資料重新整理直接重新整理到磁碟,繞過os cache。
6)返回給client
如果有slave,第4步之後經過slave服務執行緒io_thread寫到從庫的relay log ,再由sql thread應用relay log到從庫中。

三、關於效能
寫undo redo log ,binlog的過程中都是順序寫,都會很快的完成,隨機寫操作,inset_buffer功能。
對於非聚集類索引的插入和更新操作(5.5 版本及以上支援Update/Delete/Purge等操作的buffer功能),不是每一次都直接插入到索引頁中,而是先插入到記憶體中。具體做法是:如果該索引頁在緩衝池中,直接插入;否則,先將其放入插入緩衝區中,再以一定的頻率和索引頁合併,就可以將同一個索引頁中的多個插入合併到一個IO操作中,改隨機寫為順序寫,大大提高寫效能。

關於資料安全,這是資料庫寫入的重點?
1,2,3過程失敗就是事務失敗,因為此時還未寫入磁碟,對磁碟中的資料無影響,返回事務失敗給client,從庫也不會受到影響。 4,5過程失敗的時候或者已經將寫成功返回給客戶,可以根據redo log的記錄來進行恢復,如果出現部分寫失效請參考《double write》。
MySQL的寫redo log的第一個階段會把所有需要做的操作做完,記錄資料變更,第二階段的工作比較簡單 ,只做事務提交確認。如果寫入binlog成功,而第二階段失敗,MySQL啟動時也會將事務進行重做,最終更新到磁碟中。MySQL 5.5+的smei sync可以更好的保障主從的事務一致性。

四、檔案訪問方式
IO 訪問的方式分為兩種順序讀寫和隨機讀寫, 在MySQL的io過程中可以以此來將資料庫檔案分類。
順序讀寫:重做日誌ib_logfile*,binlog file。
隨機讀寫:innodb表資料檔案,ibdata檔案。
根據系統的訪問型別,對硬體做如下分類:讀多(SSD+RAID)、寫多FIO(flashcache)、容量密集(fio + flashcache)。
由於隨機io會嚴重降低系統的效能,在當前的硬體水平下,可以考慮選擇獎資料庫伺服器配置ssd/fusionio。

五、影響IO的引數和策略
影響mysql io的引數有很多個,這裡羅列幾個重要的引數。
innodb_buffer_pool_size
該引數控制innodb快取大小,用於快取應用訪問的資料,推薦配置為系統可用記憶體的80%。
binlog_cache_size
該引數控制二進位制日誌緩衝大小,當事務還沒有提交時,事務日誌存放於cache,當遇到大事務cache不夠用的時,mysql會把uncommitted的部分寫入臨時檔案,等到committed的時候才會寫入正式的持久化日誌檔案。
innodb_max_dirty_pages_pct
該引數可以直接控制Dirty Page在BP中所佔的比率,當dirty page達到了該引數的閾值,就會觸發MySQL系統重新整理資料到磁碟。
innodb_flush_log_at_trx_commit
該引數確定日誌檔案何時write、flush。
為0,log buffer將每秒一次地寫入log file中,並且log file的flush(刷到磁碟)操作同時進行.該模式下,在事務提交的時候,不會主動觸發寫入磁碟的操作。
為1,每次事務提交時MySQL都會把log buffer的資料寫入log file,並且flush(刷到磁碟)中去.
為2,每次事務提交時MySQL都會把log buffer的資料寫入log file.但是flush(刷到磁碟)操作並不會同時進行。該模式下,MySQL會每秒執行一次 flush(刷到磁碟)操作。
注意:由於程式排程策略問題,這個“每秒執行一次flush(刷到磁碟)操作”並不是保證100%的“每秒”。
sync_binlog
sync_binlog的預設值是0,像作業系統刷其他檔案的機制一樣,MySQL不會同步到磁碟中去而是依賴作業系統來重新整理binary log。
當sync_binlog =N (N>0) ,MySQL 在每寫 N次 二進位制日誌binary log時,會使用fdatasync()函式將它的寫二進位制日誌binary log同步到磁碟中去。
innodb_flush_method
該引數控制日誌或資料檔案如何write、flush。可選的值為fsync,o_dsync,o_direct,littlesync,nosync。
資料庫的I/O是一個很複雜和細緻的知識層面,涉及資料庫層和OS層面的IO寫入策略,也和硬體的配置有關。

                                                   案例分享2: 同一條sql語句,有時插入塊,有時插入慢原因剖析                                                  

背景:同一條sql ,有時插入時間幾毫秒,有時插入時間卻是幾十毫秒!為什麼呢? 分析過程如下:

Sql角度:簡單insert

表角度: 一個主鍵

系統引數角度:
開啟了雙1 策略。
也就意味著每次事物就會有重新整理磁碟
關閉雙1 ,設定為 0 100 ,或者 2 100 ,會極大提升效能。這是因為不刷硬碟了,但不能解決為什麼時快時慢問題。

作業系統角度
使用"iostat -xmd 1"命令看磁碟使用情況

以上看出磁碟明顯不夠快,讀寫0.15M就使用了7%

上面可以看出,來個順序檔案拷貝操作,30M使用就使用了100%,離散讀寫更慢了!

使用"sar -B 1"命令可以檢視頁面交換

pgpgin/s:   表示每秒從磁碟或SWAP置換到記憶體的位元組數(KB)
pgpgout/s:  表示每秒從記憶體置換到磁碟或SWAP的位元組數(KB)
fault/s:  每秒鐘系統產生的缺頁數,即主缺頁與次缺頁之和(major + minor)
majflt/s:  每秒鐘產生的主缺頁數.
pgfree/s:  每秒被放入空閒佇列中的頁個數
pgscank/s:  每秒被kswapd掃描的頁個數
pgscand/s:  每秒直接被掃描的頁個數
pgsteal/s:  每秒鐘從cache中被清除來滿足記憶體需要的頁個數
%vmeff:  每秒清除的頁(pgsteal)佔總掃描頁(pgscank+pgscand)的百分比

以上表示記憶體和swap進行了頻繁的資料交換!

那個程式在使用swap呢?

下面截圖中命令是for i in $(ls /proc | grep "^[0-9]" | awk '$0>100'); do awk '/Swap:/{a=a+$2}END{print '"$i"',a/1024"M"}' /proc/$i/smaps;done| sort -k2nr | head

在經過幾個小時後 ,mysql 使用swap由88M變成了104M ,這說明一直在使用和增加的。

問題基本定位
1)首先是磁碟效能不高,順序寫才30M ,離散寫會降低10倍
2)其次是mysql又使用了swap 空間,這就使得效能更差
3) Mysql 開啟了雙1 驗證,就會等待資料刷磁碟,

磁碟使用頻率不穩定,導致了mysql的插入時間會時快時慢

如何解決?
1)減少mysql使用swap方式。即把swapness設定為1。
即執行"sysctl vm.swappiness=1",並且在/etc/sysctl.conf檔案中也要設定為1;
2)降低記憶體,比如設定innodb_buffer_pool_size =4G,原來設定的是6G ,這樣可以節約一部分記憶體空間;
3)開啟innodb_numa_interleave = ON 來操作numa;
4)更換SSD 或者不用開啟雙1,改成 2 100

只調整作業系統引數,不更換硬體,依然開啟雙一,重啟mysql之後呢?

可以看到mysql已經不再使用swap空間了。但是因為雙一引數的使用,每次事物都會刷磁碟,而這個機械磁碟的效能在隨機讀寫的情況下不穩定。會依然存在時快時慢的問題。 

相關文章