程序員保命技能,Mysql bin_log數據恢復,你還不知道嗎?

大家好我是迷途,一個在互聯網行業,摸爬滾打的學子。熱愛學習,熱愛代碼,熱愛技術。熱愛互聯網的一切。無論你是正在路上的旅人,還是背上行囊,整裝待發的學子。 都可以點贊關注一下帥途的動態 當然如果帥途拿到比較好的學習資料或者看見比較好的文章也會第一時間跟大家分享。

前言

前兩天帥途在操作線上數據庫的時候,不小心幹掉了正式環境的用戶數據,沒有最新備份,沒有開啓事務,多麼痛的領悟。(ps:這裏是由於是週末加班,加上睡得迷迷糊糊,就偷了個懶,偷偷摸摸用root賬號進行操作,各位小夥伴千萬不要學帥途,一失足成千古恨。)還好帥途畢竟在互聯網混跡了有一段時間了,立馬就想到了使用bin_log日誌解決。
在這裏插入圖片描述

正文

1、什麼是bin_log?

bin_log是mysql的二級制日誌記錄文件,默認是關閉狀態,他記錄了我們對數據庫的所有增刪改查和事務等操作,一般來說我們主要用於數據恢復、和主備存儲等,它我們在成熟項目中不可或缺的一部分,現在大部分雲端數據庫,在我們失誤操作數據爲我們恢復數據時,其實底層應用也是該文件。

2、bin_log的三種模式

  • Row
    行模式,日誌記錄精確到行級別,例如我們一條update語句修改了三條數據,那麼在bin_log記錄中就會生成三條信息(我們的update和delete等操作事務的開啓和提交也會記錄)
  • Statement
    語句模式,他不會記錄我們每一行數據的邊動,只會記錄每次我們執行的sql
  • Mixed
    混合模式,row和statement的混合使用,目前大家對Mixed的切換模式還有點模糊,帥途查閱了大量資料和操作簡單了總結了一下,bin_log的模式切換主要跟事務隔離級別有關,在我們使用事務的時候,使用innodb.lock的時候bin_log模式都會使用row模式記錄。(感興趣的小夥伴可以自己去測試一下,分享一下經驗)

3、數據準備

測試環境:Mysql 5.7 , navicat 12 , centos 7

測試數據:數據庫(test),數據表(person) 字段(name,age,gender,school)
準備三條測試數據
1 張三 18 男 清華大學
2 李四 20 女 北京大學
3 王五 25 男 斯坦福大學

在這裏插入圖片描述

4、開啓bin_log日誌

首先我們需要查看我們的mysql服務器是否開啓了bin_log日誌,在mysql中執行命令:

show variables like '%log_bin%'  -- 查詢是否開啓bin_log日誌

在這裏插入圖片描述
ON:標識開啓日誌
OFF:標識未開啓日誌

沒有開啓的小夥伴找到mysql的my.cnf文件末尾添加配置,默認在/etc/my.chf:

	server_id=1   --服務節點id 區間2^32

	log_bin = mysql-bin --日誌文件存儲位置和名稱,這裏使用默認配置

	binlog_format = ROW --日誌級別

	expire_logs_days = 30 --log日誌留存時間 單位Day

配置完成後重啓mysql即可生效

service mysqld restart --重啓mysql

5、模擬誤修改數據

這裏模擬一下帥途執行了一個update語句但是忘記寫update語句,執行成功之後三條數據的學校數據都被修改了。

update person set school = '家裏蹲大學'

在這裏插入圖片描述
發現誤操作之後立即執行:

-- 查看所有bin_log日誌
show master logs;
-- 刷新日誌,生成新的日誌
flush logs; 
-- 然後執行,查詢我們執行操作的pos_end節點,可以理解爲動作節點
show binlog events in 'mysql-bin.000002'; 
-- 然後根據我們查詢到的pos節點進行恢復,例如帥途這裏的end_pos節點是1703

這裏立即刷新日誌是因爲,如果我們誤操作正式環境的數據,toB稍微好一點,實時操作不會很多,如果我門做的是toC的系統,那麼每過一分鐘用戶的操作量都是極其龐大的,爲了防止我們找不到動作節點,所以這裏刷新一下

在這裏插入圖片描述

這裏可以看出,帥途執行完update後找到的對應這條update語句的動作節點,然後根據他的動作節點進行恢復即可。(如果開始執行是1775則我們恢復到1703動作節點即可)

在我們的mysql服務器上執行

根據結束動作點進行恢復,如果我們沒有指定開始節點,那麼默認會從當前bin_log文件開頭執行到結束點
/usr/bin/mysqlbinlog --stop-position=1703 --database=test /var/lib/mysql/mysql-bin.000002 | /usr/bin/mysql -uroot -p123456 -v test

恢復某一個動作節點之間的數據

從1023動作開始執行到1703動作
/usr/bin/mysqlbinlog --start-position=1023 --stop-position=1703 --database=goteam_test /var/lib/mysql/mysql-bin.000002 | /usr/bin/mysql -uroot -p123456 -v goteam_test

解析版

#{mysqlbinlog} --stop-position=#{end_pos} --database=#{數據庫名稱} #{日誌文件位置} | /#{mysql數據庫所在位置} -u#{賬號} -p#{密碼} -v #{數據庫}

然後我們的數據就成功恢復成執行update之前的樣子了,感覺很酷的樣子~
在這裏插入圖片描述
注意:有些同學到了這一步可能發現,我明明恢復到了執行update語句之前,爲什麼我數據還是修改之後的數據?或者我數據只有部分進行了恢復,還有一部分並沒有恢復?甚至報錯?

6、Mysql執行流程

其實到了這一步,我們就需要先了解bin_log日誌對我們數據進行恢復的原理,首先我們來看看一條sql語句的執行流程

在這裏插入圖片描述

通過上面我們可以看出,我們日誌的寫入等操作是通過我們的執行器,並且如果我們的引擎是innodb的話,執行器會爲我們默認加上事務操作。(注意:這裏的事務只有增刪改會爲我們默認開啓,select、drop等都是不會開啓的

redo_log:redo log通常是物理日誌,記錄的是數據頁的物理修改,可以將他看爲一個臨時的日誌記錄,mysql的innodb引擎中默認爲我們開啓,在我們執行update語句的時候,特別是高併發情況下,我們每執行一次修改語句,都會先從磁盤中找到這條需要修改的數據,執行修改動作,然後再寫入磁盤,在這一系列的操作過程中,消耗的io是極其恐怖的,所以mysql的研發工程師爲了避免這一問題,就用到了redo log,也是對應MySQL裏經常說到的WAL技術,WAL的全稱是Write-Ahead Logging,在我們執行update的時候,會將我們的數據預先寫入到redo log中,等待我們的server空閒的時候,再寫入到磁盤當中。

下面我們來看看一條update語句在執行器中的流程
在這裏插入圖片描述

在我們寫入日誌文件的時候,我們的執行器會將執行語句進行拆分,也就是bin_log的row級別,例如在我們上面的數據中,有三條數據源,我們執行 update person set school=‘家裏蹲大學’,那麼我們的執行器會爲我們將這條update解析成 update person set 列1=值1,列2=值2…… where 列1=值1 and 列二=值2……,由下圖我們可以看出,一條多行操作的sql會被解析成多條語句,這裏需要注意一下,如果我們執行的是drop操作,那麼是不會被解析成行級的。

在這裏插入圖片描述

7、bin_log數據恢復原理

其實從上面MySQL的執行流程我們就可以看出來,bin_log日誌是將我們操作的每一條sql,根據我們配置的log級別,解析,寫入到二進制文件中,然後在我們使用mysqlbinlog控件進行恢復的時候,相當於,將我們定義的begin_pos和end_pos之間的語句再次執行一遍。所以很多小夥伴可能發現,明明我恢復到執行(刪除、修改操作等)之前的動作節點或者時間了,爲什麼數據沒有恢復成功,或者部分恢復,甚至報錯?那是因爲,在我們執行的這段動作或時間節點中,並沒有數據操作動作,或者有操作動作,但是我們的數據結構已經被幹掉了,所以一般當我們誤操作數據庫的時候,需要找到和我們當前時間線最近的備份數據,然後匹配bin_log日誌裏面的時間節點,匹配到我們的數據備份時刻,然後執行bin_log日誌,進行恢復

8、根據時間節點恢復數據

-- 模擬我們在 2020-05-26 11:22:00 執行了該語句,數據庫所有數據被篡改
update person set school='家裏蹲大學'

執行恢復語句

根據時間節點進行恢復
/usr/bin/mysqlbinlog --stop-datetime=‘2020-05-26 11:21:00’ --database=test /var/lib/mysql/mysql-bin.000001 | /usr/bin/mysql -uroot -p123456 -v test

由於帥途這裏是新創建的數據庫和文件,所以直接根據結束時間執行操作即可,如果我們只想恢復某一個時間節點操作的數據,則執行

從2020-05-26 00:00:00開始恢復到2020-05-26 11:20:00
/usr/bin/mysqlbinlog --start-datetime=“2020-05-26 00:00:00” --stop-datetime=“2020-05-26 11:20:00” --database=test /var/lib/mysql/mysql-bin.000001 | /usr/bin/mysql -uroot -p123456 -v test

查看恢復之後的數據,可以看出這裏的數據已經恢復成修改之前的數據了。

在這裏插入圖片描述

9、將bin-log導出成sql文件

看到這裏,可能有些同學會問,帥途帥途!我是做toC產品的,我今天誤操作了一張表,我把一張表的數據全部清掉了,但是同一個時間段內有用戶在操作,我不想影響其他用戶數據怎麼辦?

沒有問題,帥途來幫你。我們這裏直接將bin_log轉成sql文件,來進行手動恢復。

執行命令
/usr/bin/mysqlbinlog --base64-output=DECODE-ROWS -v /var/lib/mysql/mysql-bin.000003 > /logs/mysqlLogs/1.sql

如果使用的默認配置,那麼日誌文件的地址在/var/lib/mysql/中,具體查看etc/my.cnf文件配置
/usr/bin/mysqlbinlog --base64-output=DECODE-ROWS -v #{日誌存儲位置} > #{輸出位置}

導出之後,查看我們生成的sql文件
在這裏插入圖片描述
由上圖我們可以看出,bin_log日誌記錄的delete和update方式都是全匹配方式,所以我們可以通過匹配的數據,即可手動恢復

10、恢復drop文件

哎呀帥途!我一不小心把我的表(數據庫)給drop了,怎麼恢復?

咳咳,這又啥可難的,帥途來教你,例如這裏帥途不小心把person表給幹掉了

  • 首先找到我們最近的備份數據,將表結構恢復,(注意:如果在我們當前bin_log文件裏面沒有某個數據的結構或者修改之前的數據行會報錯)

帥途這裏模擬一下,例如這裏帥途備份是2020.05.25 12:00:00備份,然後在2020.05.26 19:00:00誤操作將表給幹掉了,備份時數據如下。

在這裏插入圖片描述

  • 根據我們備份的時間節點,執行bin_log文件,我們備份時間是 2020.05.25 12:00:00那麼我們的開始執行時間節點就是2020.05.25 12:00:00,結束時間放到誤操作之前一分鐘2020.05.26 18:59:00,執行語句的時間可以通過上面的命令導成sql文件查看,下圖是恢復之後的數據

執行命令
/usr/bin/mysqlbinlog --start-datetime=“2020.05.25 12:00:00” --stop-datetime=“2020.05.26 18:59:00” --database=test /var/lib/mysql/mysql-bin.000002 | /usr/bin/mysql -uroot -p8856769abcd -v test

在這裏插入圖片描述
如果經過一天的操作,我們的bin_log日誌超過max_binlog_size配置,重新創建了新的日誌文件,則我們將日誌文件依次執行即可。

那帥途,我從來沒有備份怎麼辦?

在這裏插入圖片描述

彆着急!先看看下面兩個問題。

  • 我的bin_log文件全嗎?

如果全,那麼恭喜你,問題不大,我們直接從第一個bin_log依次執行即可,因爲bin_log裏面存儲了我們所以的操作,無論是DML還是DML,相當於將我們這個數據庫創建之初到目前,所以操作重寫一遍。如果不全,不要慌,接下來看下面一個問題。

  • 數據結構,總還有吧!

先將我們誤刪除的數據結構恢復(也就是表結構,數據庫結構等),根據數據丟失情況,定位到對應bin_log,例如這張表是我半個月之前創建的,但是我最近的bin_log是一週之前的,那就拿到最早的那個bin_log,然後不要管,直接執行。帥途下面模擬一下。

在這裏插入圖片描述
這是帥途目前剩下的數據,丟失了一條數據

丟失數據:
4 趙六 30 女 廈門大學

執行bin_log發現報錯
在這裏插入圖片描述
報錯提示:
ERROR 1032 (HY000) at line 37: Can’t find record in ‘person’
然後我們導出sql文件,根據提示,找到sql文件對應報錯位置
在這裏插入圖片描述
發現,原來是我們修改的時候,由於我們這條數據已經丟失,但是進行修改報錯。我們根據update語句的條件進行手動恢復即可。

以上方法雖然可以恢復部分數據,但是由於我們bin_log文件和備份文件的丟失,但是還是會有一部分日誌記錄時未操作數據無法恢復,so 各位小夥伴一定要慎重!

總結

最後帥途在這裏總結一下,希望大家在操作正式數據的時候一定要慎重!畢竟雖然壓箱底的保命技能雖然我們學到了,但是還是有一定的侷限性。bin_log數據恢復並不適用於所有場景,例如在我們數據結構缺失,bin_log文件不全的情況下。畢竟在電視劇裏所有的江湖大蝦在使用壓箱底的絕技時,一般都離領盒飯不遠了。如果是特別重要的數據丟失,照成的損失非常龐大的話,那麼還是建議各位找專業的第三方數據恢復公司,自己不要瞎搞

  • 誤操作數據庫之後的恢復流程

1、flush logs 刷新bin_log日誌,防止日誌刷新過於頻繁,找不到我們誤操作的時間動作節點(最好備份一下日誌)
2、找到我們最近的備份,一般來說在項目當中,都會備份一次數據。
3、根據我們最近的備份時間節點與誤操作時間節點,使用mysqlbinlog進行恢復
4、如果沒有備份,需要查看bin_log日文文件是否齊全,如果齊全則直接重新執行一邊即可
5、如果不齊全,則需要我們手動導出sql文件,根據數據缺失情況手動恢復
6、以上操作建議在不影響正式環境情況下操作,處理好數據在導入到正式

在這裏插入圖片描述

看到這了就快點贊關注一下吧

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章