1. 程式人生 > >MySQL-記一次備份失敗的排查過程

MySQL-記一次備份失敗的排查過程

          山竹來臨,窩在家裡整理個人文件。        本篇文章主要講解排查問題的思路,涉及linux 刪除檔案的原理、例項誤刪資料恢復、MySQL例項初始化引數優先級別等,雖然涉及知識點比較淺,但是個人覺得挺有意思的,所以翻出筆記釋出出來。

 1 備份出錯咯

    測試環境測試 xtrabackup 相關效能的時候,備份失敗!     備份指令如下:
1 innobackupex --defaults-file=/apps/conf/mysql/mysql5_3306.cnf --socket=/tmp/mysql3306.sock --user=[*]--password=[*] --no-timestamp /apps/mysql_backup/test_backup_1 > /apps/mysql_backup/backup.log 2>&1

    備份錯誤日誌如下:

xtrabackup: The latest check point (for incremental): '1209962'
xtrabackup: Stopping log copying thread.
.xtrabackup: warning: Log block checksum mismatch (block no 2364 at lsn 1209856):
expected 1161267116, calculated checksum 1312610971
xtrabackup: warning: this is possible when the log block has not been fully written by the server, will retry later.
180912 15:30:11 >> log scanned up to (1209856) 180912 15:30:11 Executing UNLOCK TABLES 180912 15:30:11 All tables unlocked 180912 15:30:11 Backup created in directory '/apps/mysql_backup/test_backup_1/' MySQL binlog position: filename 'mysql-bin.000001', position '26708128' 180912 15:30:11 [00] Writing /apps/mysql_backup/test_backup_1/backup-my.cnf
180912 15:30:11 [00] ...done 180912 15:30:11 [00] Writing /apps/mysql_backup/test_backup_1/xtrabackup_info 180912 15:30:11 [00] ...done xtrabackup: Transaction log of lsn (1209962) to (1209856) was copied. xtrabackup: error: last checkpoint LSN (1209962) is larger than last copied LSN (1209856).
    Tips:      資料庫例項執行正常的情況,在各個log buffer中,會存有 各個LSN,可以通過 show engine innodb status 檢視,但是注意,這個lsn並非是直接從磁碟檔案獲取,而是從buffer 中獲取。說明如下:
dba@localhost : (none) 17:00:21> show engine innodb status \G
......
---
LOG
---
Log sequence number 4158179984
Log flushed up to   4158179984
Pages flushed up to 4158179984
Last checkpoint at  4158179975
0 pending log flushes, 0 pending chkp writes
128445 log i/o's done, 0.00 log i/o's/second
......

# Log sequence number: 當前系統最大的LSN號
# log flushed up to: 當前已經寫入redo日誌檔案的LSN
# pages flushed up to:已經將更改寫入髒頁的lsn號
# Last checkpoint at:系統最後一次重新整理buffer pool髒中頁資料到磁碟的checkpoint
# LSN1 = Log sequence number
# LSN2 = log flushed up to
# LSN3 = pages flushed up to
# LSN4 = Last checkpoint at
# LSN1
>= LSN2 >= LSN3 >= LSN4

2 備份錯誤解讀

    xtrabackup: error: last checkpoint LSN (1209962) is larger than last copied LSN (1209856).

說明xtrabckup 同步 redo log 到完成的時候,自身的 redo log 最後的 lsn 跟 當前資料庫的 ib_logfile檔案最後的 lsn對比,發現不匹配。

    先檢查備份失敗例項內的 ib_logfile 檔案:

    正常 ib_logfile 檔案的change time 應該是跟資料庫的初始化時間一致,因為沒有修改 檔案的許可權,但是顯示的change time 明顯晚與例項的執行時間,懷疑是否這幾個ib_logfile[*] 檔案被覆蓋!     為驗證ib_logfile[*] 檔案被覆蓋,檢查 mysql 程序下是否有 deleted 控制代碼,截圖如下,發現 ib_logfile[*]檔案確實被覆蓋。

3 排查問題

哇,原來 檔案被刪除,這裡有幾個疑問 :
  1. 為何檔案被刪除,不影響3306 例項 mysqld 的執行,3306 仍支援正常的 dml及ddl操作?
  2. 306的redo log 是寫入到哪些地方?
  3. 3306例項丟失的檔案能否恢復,能否正常使用?
  4. 為何 只有 redo log 檔案被刪除,是什麼操作導致?

3.1 檔案被刪,例項為何能正常執行

        在linux中,每個檔案都有兩個 link 計數器:
  • i_count:檔案使用者或者被呼叫的數量,理解為記憶體引用的計數器。檔案被程序開啟使用的時候,自增+1。
  • i_nlink:硬連結的數量,理解為磁碟引用計數器。建立檔案的硬連結的時候,自增+1。
        當我們執行rm操作的時候,實際是 i_nlink-1,不一定真正刪除檔案,只有當 i_nlink=0 & i_count=0 時,檔案才會被真正刪除。案例中的 ib_logfile[*] 由於沒有新建立硬連結,所以 i_nlink = 1,加上此時 3306例項處於執行中,需要呼叫 到 ib_logfile[*]檔案,所以 i_count = 1( 當前無其他程序使用到ib_logfile[*] ),當檔案被刪除的時候,i_nlink =0 但是 i_count=0,故檔案不會被真正刪除,僅刪除 inode 連線,並沒有刪除 磁碟的資料塊。         那麼被程序呼叫的檔案,遭遇 rm 操作,那麼它將何去何從呢?         首先,該檔案的 i_nlink被刪除,剩下 i_count,故僅刪除磁碟硬連結,內容未刪除。可以通過 proc 檔案系統查詢檔案。每個程序都有程序id,可以通過 proc檔案系統查詢到該程序開啟及呼叫的檔案的連結。  測試1:在執行的新例項 3006 上,刪除3個檔案後檢查  

3.2 資料實際寫入到哪裡,原被覆蓋檔案?proc檔案系統檔案?

測試2:刪除表格檔案,檢視控制代碼size是否變化? 測試內容:刪除ib_logfile0檔案及tbb.ibd檔案,往 tbb 表格插入資料,檢視 ib_logfile0大小及tbb.ibd大小 測試說明:為何是檢視大小,而不是檢視change time呢?因為node連結儲存了檔案的屬性,刪除了該連結,則無法檢視檔案屬性,僅能通過lsof檢視檔案大小來判斷寫入情況。/proc檔案系統中的  deleted 軟連線,stat檢視也是檢視軟連線的屬性,並非 真實檔案資料塊。無論是root還是其他使用者刪除,這個檔案都能寫入。 測試過程:見截圖 測試結果:因為ib_logfile 是固定大小1G,無法檢視到change時間,故不能驗證;但是可以從 tbb.ibd 檔案大小得知,實際是 redo log 是寫入到 被刪除的檔案的

3.3 恢復被刪除例項資料檔案

  1. 若是主庫檔案被刪
    • 檢查從庫是否正常、是否無刪除檔案
      1. 從庫正常
        1. 方案1
          1. 主從切換
          2. 舊主上,根據deleted控制代碼重定向被刪除的資料塊到原先的磁碟位置,例: cat /proc/16979/fd/36 > /apps/dbdat/mysql5_data3306/sophia/tba.ibd
        2. 方案 2
          1. 重做 舊主庫,重做後恢復從庫使用
      2. 從庫不正常,檔案也被刪
        1. 從庫,停止複製
        2. 從庫,根據deleted控制代碼重定向被刪除的資料塊到原先的磁碟位置,例: cat /proc/16979/fd/36 > /apps/dbdat/mysql5_data3306/sophia/tba.ibd
        3. 從庫,恢復複製
        4. 檢查從庫追上主庫
        5. 主從切換
        6. 舊主新從,停止複製,重定向檔案內容 或者重做 從庫
  2. 若是從庫檔案被刪
    1. 方案1
      1. 從庫停止複製
      2. 從庫上,根據deleted控制代碼重定向被刪除的資料塊到原先的磁碟位置,例: cat /proc/16979/fd/36 > /apps/dbdat/mysql5_data3306/sophia/tba.ibd
    2. 方案2
      1. 重做 舊主庫,重做後恢復從庫使用

3.4 為何僅ib_logfile[*]檔案被刪除,為何被刪

    檢視 mysql 3306 的error 看看有啥苗頭:
180912 15:09:51 [Note] Plugin 'FEDERATED' is disabled.
180912 15:09:51 InnoDB: The InnoDB memory heap is disabled
180912 15:09:51 InnoDB: Mutexes and rw_locks use GCC atomic builtins
180912 15:09:51 InnoDB: Compressed tables use zlib 1.2.3
180912 15:09:51 InnoDB: Using Linux native AIO
180912 15:09:51 InnoDB: Initializing buffer pool, size = 1.0G
180912 15:09:51 InnoDB: Completed initialization of buffer pool
InnoDB: The first specified data file /apps/dbdat/mysql5_data3306/ibdata1 did not exist:
InnoDB: a new database to be created!
180912 15:09:51  InnoDB: Setting file /apps/dbdat/mysql5_data3306/ibdata1 size to 1000 MB
InnoDB: Database physically writes the file full: wait...
InnoDB: Progress in MB: 100 200 300 400 500 600 700 800 900 1000
180912 15:09:55  InnoDB: Log file /apps/dbdat/mysql5_data3306/ib_logfile0 did not exist: new to be created
InnoDB: Setting log file /apps/dbdat/mysql5_data3306/ib_logfile0 size to 1000 MB
InnoDB: Database physically writes the file full: wait...
InnoDB: Progress in MB: 100 200 300 400 500 600 700 800 900 1000
180912 15:09:58  InnoDB: Log file /apps/dbdat/mysql5_data3306/ib_logfile1 did not exist: new to be created
InnoDB: Setting log file /apps/dbdat/mysql5_data3306/ib_logfile1 size to 1000 MB
InnoDB: Database physically writes the file full: wait...
InnoDB: Progress in MB: 100 200 300 400 500 600 700 800 900 1000
180912 15:10:01  InnoDB: Log file /apps/dbdat/mysql5_data3306/ib_logfile2 did not exist: new to be created
InnoDB: Setting log file /apps/dbdat/mysql5_data3306/ib_logfile2 size to 1000 MB
    看log,是 新建例項的時候,覆蓋了 3306例項的 ib_logfile跟ibdata1,但是實際上為何只有 ib_logfile被覆蓋呢?檢視歷史 操作命令,找到了 初始化 例項的指令如下: history | grep mysql    368  /apps/svr/mysql57/bin/mysqld  --initialize  --datadir=/apps/dbdat/mysql57_data3307 --user=apps --innodb-data-file-path=ibdata1:1000M:autoextend --innodb-data-home-dir=/apps/dbdat/mysql57_data3307 --innodb-log-file-size=1000M --innodb-log-files-in-group=4 

3.5 為何使用命令指定引數執行新建例項,會覆蓋其他例項的的檔案呢?

    檢視history 操作時,發現 之前執行了了 初始化例項的指令,沒有指定 配置檔案,而是指定了 datadir,另起伺服器,執行該命令,是正常,截圖如下,那麼這裡又 引申了 第五個 問題:為何使用 命令執行新建例項,會覆蓋其他例項的的檔案呢?     靈機一動的我,聯想到了配置檔案的預設讀取順序,mysql安裝的時候,讀取配置檔案的優先順序:https://dev.mysql.com/doc/refman/5.7/en/option-files.html 。檢視 /etc/my.cnf ,發現確實是 有檔案存在,並且這個檔案是 3306的配置檔案內容。
File Name Purpose
/etc/my.cnf m
/etc/mysql/my.cnf Global options
SYSCONFDIR/my.cnf Global options
$MYSQL_HOME/my.cnf Server-specific options (server only)
defaults-extra-file
~/.my.cnf User-specific options
~/.mylogin.cnf User-specific login path options (clients only)
    這個時候,可以初步確認,mysql 3307 初始化的時候,除了使用 指定的指令外,其他需要的啟動引數會從 預設的配置檔案路徑依次讀取。那麼,mysql 初始化需要讀取哪些 必備引數呢 ?     mysql 5.7 ,無預設配置檔案,僅指定引數 datadir basedir 初始化後生成 :mysql\sys\performance_schema 3個數據庫目錄及表格檔案,同時生成 ibdata1、ib_logfile[*]等檔案,是否可以判斷,初始化過程中,需要指定跟 ibdata1、ib_logfile【*】相關引數、error log位置、user 則正常呢?
 /apps/svr/mysql57/bin/mysqld  --initialize  --datadir=/apps/dbdat/mysql57_data3307 --user=apps --innodb-data-file-path=ibdata1:1000M:autoextend --innodb-data-home-dir=/apps/dbdat/mysql57_data3307 --innodb-log-file-size=1000M --innodb-log-files-in-group=4 --basedir=/apps/svr/mysql57 --innodb_log_group_home_dir=/apps/dbdat/mysql57_data3307  --innodb-log-files-in-group=4 --log-error=/apps/logs/mysql/error3307.log     你以為這樣,就可以安裝了嗎?     並不,當存在預設路徑的配置檔案時,除了指定引數外,還是會去讀 預設路徑 上的配置檔案 為主。