不小心刪庫了怎麼辦,用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
-- 以上內容來自公眾號赫連小伍,轉載請註明出處