1. 程式人生 > 其它 >不小心刪庫了怎麼辦,用binlog可以恢復

不小心刪庫了怎麼辦,用binlog可以恢復

https://zhuanlan.zhihu.com/p/384906854

資料找回

我們找到刪庫的研發人員詢問他有沒有備份,他的回答是沒有

我們又去諮詢運維的同事,看看生產環境有沒有開啟資料庫定期自動備份,運維的回答也是沒有

事情比較難辦了,只能把希望寄託於mysql的binlog了。

binlog二進位制日誌檔案,資料庫的insert、delete、update、create、alter、drop等寫入操作都會被binlog記錄(下文對binlog有詳細介紹

binlog記錄日誌是需要開啟配置的,希望生產環境的mysql資料庫開啟了binlog日誌,否則只能找專業的磁碟資料恢復的第三方公司了

登入生產環境資料庫,檢視binlog是否開啟

SHOW VARIABLES LIKE 'LOG_BIN%';

從圖中可以看到log_bin是處於ON的狀態,說明binlog是開啟的

懸著的心終於放下了一大半,接下來就是想辦法從binlog中把資料恢復就行了

從上圖中也可以看到log_bin_basename/var/lib/mysql/bin-log,說明binlog是存放在mysql所在的伺服器的/var/lib/mysql目錄下,檔案是以bin-log開頭,比如:bin-log.000001

登入mysql所在的伺服器,進入到binlog所在的目錄

cd /var/lib/mysql

檢視binlog日誌檔案

binlog日誌檔案是滾動生成的,從圖中看到現在已經有4個檔案了。

通常情況下,生產環境的binlog會有成百上千個,這時候就需要確認我們需要的資料是在第幾個binlog中了,下文也會講怎麼確定我們需要的是第幾個

因為我們刪庫是剛剛發生的事情,所以我們需要的資料大概率是在第4個檔案中

直接去檢視第4個binlog檔案,看到的全都是亂碼,就像下面這樣,這是因為binlog檔案是二進位制的

我們需要藉助mysql官方提供的mysqlbinlog命令去才能正確解析binlog檔案

用mysqlbinlog命令可以開啟binlog檔案,但是一個binlog檔案的大小可能有幾百兆,要從幾百兆日誌中找到我們需要的日誌,還是比較麻煩的

還好mysqlbinlog命令提供一些引數選項可以讓我們對binlog檔案進行篩選,最常用的引數就是時間引數(下文也會對mysqlbinlog的詳細用法進行說明)

經過和刪庫的研發人員確定,刪庫的時間大概是10:40,那我們就以這個時間點為參考,找前後5分鐘的日誌

mysqlbinlog -v --start-datetime='2021-06-10 10:35:00' --stop-datetime='2021-06-10 10:45:00' bin-log.000004 | grep t_user

從圖中可以看到,這個時間點的日誌確實包含我們刪除資料的日誌

接下來我們就需要把這些日誌整理一下,然後想辦法恢復到資料庫就可以了。

首先,把我們需要的日誌單獨儲存到tmp.log檔案中,方便下載到本地

mysqlbinlog -v --start-datetime='2021-06-10 10:35:00' --stop-datetime='2021-06-10 10:45:00' bin-log.000004 > tmp.log

把tmp.log下載到本地,用文字編輯工具開啟看一下,可以看到一堆偽sql

在上圖的偽sql中
@1表示第一個欄位
@2表示第二個欄位
其他的以此類推

日誌中包含的sql是一些偽sql,並不能直接在資料庫執行,我們需要想辦法把這些偽sql處理成可在資料庫執行的真正的sql

我們使用的文字編輯工具的批量替換功能,就像下面這樣

最終處理好的sql就像是這樣

把處理好的sql在測試資料庫驗證一下沒問題後直接在生產庫執行

sql執行完以後,被誤刪除的資料就恢復回來了。

我們和刪庫的研發一起,把客戶要求刪除的6萬多條資料重新給刪除,算是完成了客戶的要求

至此,刪庫事件就暫時告一段落。不要問刪庫的研發受到了什麼處分,問就是什麼處分都沒有

幾點建議

刪庫跑路真的不只是一句玩笑話,如果真的不小心刪庫了而又無法找回資料的話,不僅僅是簡單的罰款、扣績效就完事了,甚至有可能會面臨牢獄之災

對於公司來說,一個不小心的刪庫操作,就有可能把公司刪沒了。畢竟刪庫造成的資料損失、經濟損失不是所有公司都有能力承擔的

所以,生產環境的資料安全一定是重中之重。根據我多年的刪庫經歷,也總結了一些經驗分享給你們,希望對你們有所幫助

1、研發人員不能直連生產庫

生產庫一般由DBA或者運維來維護,研發人員很少有需要登入生產資料庫檢視資料的需求,就算資料真的有問題,一般情況下DBA或運維人員也能解決

如果一個系統需要研發人員頻繁的登入資料庫去維護資料,這時就該考慮在系統中增加一個管理功能來使用,而不是頻繁登入資料庫

所以,研發就不應該具有生產庫的登入許可權。如果偶爾的需要登入生產庫檢視資料,可以找DBA開一個臨時賬號

2、登入生產庫使用只讀賬號

大部分人使用資料庫都會使用連線工具,比如Navicat、SQLyog等

每個人的電腦上,大概率也只有一個連線工具。開發庫、測試庫、生產庫都在同一個連線工具中開啟,有時只是想在開發庫中修改一條資料,卻不小心修改了生產庫

而MySql的事務是自動提交的,在連線工具中,正在修改的當前行失去游標後就會自動提交事務,極其容易操作失誤

所以,如果確實的需要登入生產庫,儘量使用具有隻讀許可權的賬號登入

3、關閉autocomit、多人複核

如果確實需要在生產庫進行資料的增加、修改或刪除,在執行sql之前最好先關閉事務的自動提交

在需要登入生產庫修改資料的情況下,想必問題也比較複雜,一條sql語句應該是完成不了,可能需要寫N多個sql才能完成資料的修改

這麼多的sql,很有可能在執行的時候會選錯。有時你只是想執行一個select語句,結果發現執行的是delete

更坑爹的是,大部分的資料庫連線工具有執行當前選中內容的功能。有時候你只想執行當前選中的內容,結果發現執行的是全部內容

如果關閉了自動提交,就算出現上面的情況,也還有機會挽回。比如下面這樣

-- 關閉事務自動提交
set @@autocommit=0;


-- 檢視需要刪除的資料,共65600條
select * from t_user where age>18 and deptid=100;
-- 刪除
delete from t_user where age>18;


-- 發現有問題,回滾
select * from t_user where age>18 and deptid=100;
rollback ;

-- 確認沒問題,提交
-- commit;

另外,在commit之前需要至少再找一個同事進行確認。所謂當局者迷,自己有時可能處於一個錯誤的思路上,就想當然的認為結果沒問題,這時就需要一個旁觀者來指點迷津

兩個人都確認沒問題之後再提交,出錯的機率也會小很多

4、修改資料之前先備份

備份、備份、備份,重要的事情說三遍

備份雖然會麻煩一點,但它是保證資料準確性最有效的手段

況且,掌握一些技巧後,備份也不是很麻煩的事情

比如,我們刪除資料之前可以先這樣備份

-- 建立一個和原表一樣的備份表(包含索引)
create table t_user_bak like t_user;

-- 拷貝資料到備份表
INSERT into t_user_bak select * from t_user;

-- 確認資料拷貝完成
select * from t_user_bak;

這樣備份的資料,就算原表資料誤刪了,甚至都不用恢復資料,只需要把備份表的名字改成原表的名字直接使用就可以了

在生產庫修改資料之前,一定要記得備份,一旦資料修改出錯,這是成本最低並且最有效的恢復途徑

5、設定資料庫定期備份

生產環境,運維人員一定要設定資料庫定期備份。研發人員也有義務提醒運維同事編寫自動備份指令碼,因為生產庫一旦出現問題需要恢復資料,沒有定期備份的話,麻煩的不只是運維人員,研發人員也要跟著麻煩

備份週期可以根據業務需要來決定。如果業務對資料要求的實時行比較高,備份週期相對短一點,恢復資料時可以最大程度的避免資料丟失;反之,備份週期可以長一點,節省磁碟空間

如果有必要,可以定期把備份檔案拷貝到異地伺服器,避免由於一些不可抗力因素導致的當前伺服器磁碟損壞,如地震、颱風等

binglog日誌

binlog即Binary Log,它是二進位制檔案,用來記錄資料庫寫操作的日誌

資料庫的insert、delete、update、create、alter、drop等寫入操作都會被binlog記錄

因此,資料庫的主從資料同步通常也是基於binlog完成的,本文只對binlog做一些簡單介紹,後期會單獨寫一篇文章講基於binlog的主從資料同步

binlog日誌需要配置開啟,可以通過指令碼檢視binlog是否開啟

SHOW VARIABLES LIKE 'LOG_BIN%';

如果log_bin引數顯示的是OFF說明binlog是關閉狀態,需要手動開啟

開啟binlog需要修改資料庫的my.cnf配置檔案,my.cnf檔案通常在伺服器的/etc目錄下

開啟/etc/my.cnf檔案,配置binlog的相關引數,下文配置binlog的常用引數

# 啟用binlog並設定binlog日誌的儲存目錄
log_bin = /var/lib/mysql/bin-log
# 設定binlog索引儲存目錄
log_bin_index = /var/lib/mysql/mysql-bin.index
# 30天之前的日誌自動刪除
expire_logs_days = 30
# 設定binlog日誌模式,共有3中模式:STATMENT、ROW、MIXED
 binlog_format = row

binlog的日誌有三種格式,分別是STATEMENT、ROW、MIXED。在mysql5.7.7版本之前預設使用的是STATEMENT,之後的版本預設使用的是ROW

ROW格式

ROW格式下,binlog記錄的是每一條資料被修改的詳細細節。

比如,執行delete語句,刪除的資料有多少條,binlog中就記錄有多少條偽sql

delete from t_user where age>18;

那麼row格式的日誌的缺點就很明顯,在發生批量操作時,日誌檔案中會記錄大量的偽sql,佔用較多的磁碟空間

尤其是當進行alter操作時,每條資料都發生變化,日誌檔案中就會有每一條的資料的日誌。此時,如果表中的資料量很大的話,日誌檔案也會非常大

在mysql5.6版本之後,針對ROW格式的日誌,新增了binlog_row_image引數。

binlog_row_image設定為minimal時,日誌中只會記錄發生改變的列,而不是全部的列,這在一定程度上能減少binlog日誌的大小

雖然記錄每行資料的變化會造成日誌檔案過大,但這也是它的優點所在

因為它記錄了每條資料修改細節,所以在一些極端情況下也不會出現資料錯亂的問題。在做資料恢復或主從同步時能很好的保證資料的真實性和一致性

STATEMENT格式

STATEMENT格式下,日誌中記錄的是真正的sql語句,就像是這樣

日誌中的sql是直接可以拿到資料庫執行的

STATEMENT格式的日誌的優缺點和ROW格式的正好相反,它記錄的是sql語句和執行語句時的上下文環境,而不是每一條資料。所以它的日誌檔案會比ROW格式的日誌檔案小一些

由於記錄的只是sql語句和上下文的環境,STATEMENT格式的日誌在進行主從資料同步時會有一些不可預估的情況出現,導致資料錯亂。比如sleep()、last_insert_id()等函式會出現問題

MIXED格式

MIXED格式是STATEMENT和ROW的結合,mysql會根據具體執行的sql語句,來選擇合適的日誌格式進行記錄

MIXED格式下,在執行普通的sql語句時會選STATEMENT來記錄日誌,在遇到複雜的語句或函式操作時會選擇ROW來記錄日誌

mysqlbinlog命令

mysql資料庫的binlog檔案是二進位制的,基本看不懂,使用資料庫自帶的mysqlbinlog命令可以把二進位制檔案轉換成能看懂的十進位制檔案

由於資料庫的binlog檔案可能會很大,檢視起來會很麻煩,所以mysqlbinlog命令也提供了一些引數可以用來篩選日誌

mysqlbinlog語法

mysqlbinlog [options] log-files
options:可選引數
log-files:檔名稱

options的常用值

-d: 根據資料庫的名稱篩選日誌
-o:跳過前N行日誌
-r, --result-fil: 把日誌輸出到指定檔案
--start-datetime: 讀取指定時間之後的日誌,時間格式:yyyy-MM-dd HH:mm:ss
--stop-datetime: 讀取指定時間之前的日誌,時間格式:yyyy-MM-dd HH:mm:ss
--start-position: 從指定位置開始讀取日誌
--stop-position: 讀取到指定位置停止
--base64-output:在row格式下,顯示偽sql語句
-v, --verbose:顯示偽sql語句,-vv可以為sql語句新增備註

常用寫法
檢視fusion資料庫的日誌

mysqlbinlog -d=fusion bin-log.000001

檢視某個時間段內的日誌

mysqlbinlog  --start-datetime='2021-06-09 19:30:00' --stop-datetime='2021-06-09 19:50:00' bin-log.000001

恢復資料,事件的開始位置是4300,結束位置是10345

mysqlbinlog --start-position 4300 --stop-position 10345 bin-log.000001 | mysql -uroot -p123456 fusion

-- 以上內容來自公眾號赫連小伍,轉載請註明出處