mysqlbinlog結合sed命令恢復update時未加where條件以前的數據

一.環境說明

騰訊雲機器上自建MySQL 上update操做時,忘加where條件 ,使用mysqlbinlog搭配sed命令完美還原
MySQL版本號:5.6.39;
mysql必須開啓binlog,而且mysql的binlog最好是Row模式;
mysql數據庫指定字符集位utf8,同時表的字符集也得爲utf8,不然在mysqlbinlog 解析出來的sql文件對於中文漢字的會出現亂碼,致使最後恢復數據到線上的表中報錯。
知足以上條件這樣能夠極大的保證數據恢復的概率。
固然把控好數據庫的權限問題,禁止採用不加where條件的delete 和update語句,以及禁止採用drop,truncate纔是從根源保證數據安全行之有效的辦法。

前面的幾篇博文都有介紹採用第三方的工具binlog-rollback.pl,binlog2sql來還原和恢復數據,其實原理和思路都是一致的。同時在測試使用中發現,這2個工具要求必須是本地數據庫服務器安裝此2個工具,本地的數據庫開啓binlog,binlog格式爲Row模式而且都是針對本地數據庫數據進行恢復的。若是在其餘機器上安裝binlog-rollback.pl或binlog2sql,而且其把要恢復數據的binlog文件拿到已經安裝好binlog-rollback.pl或binlog2sql工具的機器上來恢復數據是會報錯的,致使數據恢復失敗。mysql

二.採用mysqlbinlog結合sed命令依據binlog日誌文件恢復數據

今天我們介紹update執行時忘加where 條件,致使全表更新,採用mysqlbinlog結合sed命令依據binlog日誌文件如何來恢復數據。採用此種方式恢復數據對mysql的服務和binlog文件所在的具體服務設備是沒有任何限制的,此種方式恢復數據時更靈活sql

2.1肯定binlog格式以及是否開啓binlog

查看mysql的binlog格式:
show variables like '%binlog_format%';數據庫

mysqlbinlog結合sed命令恢復update時未加where條件以前的數據
查看是否開啓了binlog
show variables like '%log_bin%';
mysqlbinlog結合sed命令恢復update時未加where條件以前的數據centos

咱們能夠看到log_bin的值爲ON,開啓狀態,OK, 確保了update誤操做前,個人數據庫 是開啓了binog而且binlog格式是row格式,個人數據庫數據是能夠還原的。
查看log文件:
show master logs;
mysqlbinlog結合sed命令恢復update時未加where條件以前的數據緩存

2.2建立測試表

CREATE TABLE `zx_scores` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `titles` char(15) NOT NULL,
  `icon` smallint(6) unsigned DEFAULT '0',
  `integral` int(10) NOT NULL DEFAULT '0',
  `isdefault` tinyint(1) unsigned NOT NULL DEFAULT '0',
`create_time` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
`day` date NOT NULL DEFAULT '0000-00-00' COMMENT '日期',
  PRIMARY KEY (`id`),
  KEY `integral` (`integral`)
) ENGINE=Innodb AUTO_INCREMENT=0 DEFAULT CHARSET=utf8

給測試表插入數據:安全

insert into zx_scores values(1,'列兵',1,0,1,now(),curdate());
insert into zx_scores values(2,'班長',2,1000,1,now(),curdate());
insert into zx_scores values(3,'少尉',3,2000,1,now(),curdate());
insert into zx_scores values(4,'中尉',4,3000,1,now(),curdate());
insert into zx_scores values(5,'上尉',5,4000,1,now(),curdate());
insert into zx_scores values(6,'少校',6,5000,1,now(),curdate());
insert into zx_scores values(7,'中校',7,6000,1,now(),curdate());
insert into zx_scores values(8,'上校',8,7000,1,now(),curdate());
insert into zx_scores values(9,'少將',9,12000,1,now(),curdate());
insert into zx_scores values(10,'中將',10,17000,1,now(),curdate());
insert into zx_scores values(11,'上將',11,22000,1,now(),curdate());
insert into zx_scores values(12,'大將',12,27000,1,now(),curdate());

select * from test.zx_scores ;
mysqlbinlog結合sed命令恢復update時未加where條件以前的數據服務器

update更新時忘記加where條件限制:
mysql> update zx_scores set titles='班長';
Query OK, 11 rows affected (0.00 sec)
Rows matched: 12 Changed: 11 Warnings: 0
mysql> select * from zx_scores ;
mysqlbinlog結合sed命令恢復update時未加where條件以前的數據
當前的binlog文件爲以下:ide

mysql> show master status\G
*************************** 1. row ***************************
             File: mysql-bin.000020
         Position: 16042
     Binlog_Do_DB: 
 Binlog_Ignore_DB: 
Executed_Gtid_Set: 
1 row in set (0.00 sec)

提示:把此binlog mysql-bin.000020文件移動到其餘的機器上也是能夠恢復的工具

三.騰訊雲機器上採用mysqlbinog+sed命令來恢復

採用find查找到此文件在服務器上的位置:學習

[root@VM_82_178_centos binlog]# find  / -name 'mysql-bin.000020' ;
/data/mysql/binlog/mysql-bin.000020

找到這個文件,咱們單獨能夠把他拷貝到tmp目錄下,然返回到mysqllogbin這個文件路徑下,再次以前須要確認一下你誤操做的大概時間,由於咱們要經過時間範圍來搜索日誌,執行命令以下:

mysqlbinlog  --base64-output=decode-rows   -v -v  --start-date='2018-10-07 15:25:00' --stop-date='2018-10-07 15:30:00'  /tmp/mysql-bin.000020 | grep -C 30 "UPDATE `test`.`zx_scores`"

找到咱們誤操做的update 語句,記錄下sql上面 # at 開頭後面的數字14739(這個標記應該是事務的行號吧),OK,繼續執行命令

/usr/local/mysql/bin/mysqlbinlog  --no-defaults  --base64-output=decode-rows  -v -v  /tmp/mysql-bin.000020|sed -n '/# at 14739/,/COMMIT/p'  >/tmp/update.sql

咱們將這串事務從# at 14739開始到COMMIT之間的行所有提取出來到update.sql裏。

到此處,咱們已經拿到了須要還原的sql語句,根據導出的sql語句進行sed命令替換,還原到修改以前sql語句,命令以下:

sed '/WHERE/{:a;N;/SET/!ba;s/\([^\n]*\)\n\(.*\)\n\(.*\)/\3\n\2\n\1/}' update.sql | sed 's/### //g;s/\/\*.*/,/g' | sed  /@7/s/,//g | sed '/WHERE/{:a;N;/@7/!ba;s/,/AND/g};s/#.*//g;s/COMMIT,//g' | sed '/^$/d'  >  rollback.sql

這裏sed命令乍一看起來比較複雜,咱們將它分紅塊來進行分析,由於sed命令是按順序來執行的,上述命令一共由五條sed命令組成,經過管道分隔,下面咱們來細說一下這些命令都作了什麼:

3.1.第一個sed命令做用:

sed '/WHERE/{:a;N;/SET/!ba;s/\([^\n]*\)\n\(.*\)\n\(.*\)/\3\n\2\n\1/}' update.sql

功能:將where 和set位置對調
命令剖析:
/WHERE/ #包含WHERE

:a;      #建立一個labela;
N;       #追加下一個輸入行到讀取行的末尾,讀入到模式空間
/SET/!ba;   # 若是不是/SET/,返回a,也就是重複讀,一直讀到/SET/以前(buffer的內容是WHERE\n.......\nSET)

s/([^\n])\n(.)\n(.*)/\3\n\2\n\1/ 這塊能夠分三部分來讀

第1步: 
        s  #替換命令,例如s/a/b  將a替換爲b
    第2步:
        \([^\n]*\)\n\(.*\)\n\(.*\) 
        \        #轉義字符
        [^\n]* == buffer中的where
        (.*\)    #單符號(.)匹配除換行符之外的單個字符,*同上;
         [^\n]*\  #表明非換行符(回車)開頭,*表示匹配零或多個字符
        \n       #換行

    第3步
        \3\n\2\n\1   
        \3  == 內存中的set,第三個括號中的內容
        \2  == 內存中原來where與set之間的內容,第二個括號中的內容
        \1  == 內存中的where,第一個括號中的內容
[root@VM_82_178_centos tmp]#cp update.sql  sed1-update.sql
[root@VM_82_178_centos tmp]# sed -i '/WHERE/{:a;N;/SET/!ba;s/\([^\n]*\)\n\(.*\)\n\(.*\)/\3\n\2\n\1/}' sed1-update.sql

將where 和set位置對調,update.sql文件中其餘內容不變

3.2.第二個sed 命令做用:

sed 's/### //g;s/\/\*.*/,/g'

功能:這句作了兩個事情1.把字符串### 替換成 空格 2.把/*日後的內容 替換成,

s/### //g        #將### 替換成空串,
    \                #轉義字符
    \/\*.*           #匹配/*以後出換行符外全部內容
[root@VM_82_178_centos tmp]#cp  sed1-update.sql   sed2-update.sql
[root@VM_82_178_centos tmp]# sed -i 's/### //g;s/\/\*.*/,/g' sed2-update.sql

內容以下:

[root@VM_82_178_centos tmp]# grep -C 8 'UPDATE `test`.`zx_scores`' sed2-update.sql
.............
[root@VM_82_178_centos tmp]# grep -C 8 'UPDATE `test`.`zx_scores`' sed2-update.sql|tail -26
UPDATE `test`.`zx_scores`
SET
  @1=11 ,
  @2='上將' ,
  @3=11 ,
  @4=22000 ,
  @5=1 ,
  @6='2018-10-07 15:20:30' ,
  @7='2018:10:07' ,
WHERE
  @1=11 ,
  @2='班長' ,
  @3=11 ,
  @4=22000 ,
  @5=1 ,
  @6='2018-10-07 15:20:30' ,
  @7='2018:10:07' ,
UPDATE `test`.`zx_scores`
SET
  @1=12 ,
  @2='大將' ,
  @3=12 ,
  @4=27000 ,
  @5=1 ,
  @6='2018-10-07 15:20:37' ,
  @7='2018:10:07' ,
[root@VM_82_178_centos tmp]#

3.3.第三個sed 命令做用:

sed  /@7/s/,//g

功能:這句把字符串包含@7的行中的所有(,)換成空格

/@7/     #匹配包含@7的行
 s/,//    #將,替換爲空串
    g        #所有替換
[root@VM_82_178_centos tmp]#cp  sed2-update.sql   sed3-update.sql
[root@VM_82_178_centos tmp]# sed -i /@7/s/,//g sed3-update.sql

替換前文件內容:

[root@VM_82_178_centos tmp]# grep '@7' sed2-update.sql |tail -5
  @7='2018:10:07' ,
  @7='2018:10:07' ,
  @7='2018:10:07' ,
  @7='2018:10:07' ,
  @7='2018:10:07' ,

替換後的文件內容:

[root@VM_82_178_centos tmp]# grep '@7' sed3-update.sql |tail -5
  @7='2018:10:07' 
  @7='2018:10:07' 
  @7='2018:10:07' 
  @7='2018:10:07' 
  @7='2018:10:07'

3.4.第四個sed 命令做用:

sed '/WHERE/{:a;N;/@7/!ba;s/,/AND/g};s/#.*//g;s/COMMIT,//g'

功能:這句作了三件事 1.就是把WHERE 至@7之間的全部逗號,替換成AND 2.#.* 就是把#在的行替換爲空格 3.就是把匹配到的COMMIT, 替換爲空格

/WHERE/{:a;N;/@7/!ba;s/,/AND/g}      #將WHERE至@7之間的行尾的(,)替換爲(AND)
        s/#.*//g                    #將#號開頭的整行字符替換爲空串。
        s/COMMIT,//g          #將(COMMIT,)替換爲空行;
[root@VM_82_178_centos tmp]#cp  sed3-update.sql   sed4-update.sql
[root@VM_82_178_centos tmp]# sed -i '/WHERE/{:a;N;/@7/!ba;s/,/AND/g};s/#.*//g;s/COMMIT,//g' sed4-update.sql

替換後的文件的內容:

[root@VM_82_178_centos tmp]# tail -20 sed4-update.sql 
UPDATE `test`.`zx_scores`
SET
  @1=12 ,
  @2='大將' ,
  @3=12 ,
  @4=27000 ,
  @5=1 ,
  @6='2018-10-07 15:20:37' ,
  @7='2018:10:07' 
WHERE
  @1=12 AND
  @2='班長' AND
  @3=12 AND
  @4=27000 AND
  @5=1 AND
  @6='2018-10-07 15:20:37' AND
  @7='2018:10:07'

3.5.第五個sed 命令:

sed '/^$/d'   >  sed5-update.sql
 /^$/      #查找緩存內容中全部的空行
    d         #刪除
 >  sed5-update.sql   #輸出緩存中的內容到sed5-update.sql
[root@VM_82_178_centos tmp]#cp  sed4-update.sql   sed5-update.sql
[root@VM_82_178_centos tmp]# sed -i '/^$/d' sed5-update.sql

提示:對於第3個sed語句中的@7和第四個sed語句中@7能夠替換成你當前表zx_score的最大列數@max。

3.6.第六個sed 命令:

sed  -i -r  '/WHERE/{:a;N;/@7/!ba;s/(@7=.*)/\1\;/g}' sed6-update.sql

這句是在where語句後@7最後一個字段加(;),若是後執行這句請將@7換成對應的列名便可。

[root@VM_82_178_centos tmp]#cp  sed5-update.sql   sed6-update.sql
sed  -i -r  '/WHERE/{:a;N;/@7/!ba;s/(@7=.*)/\1\;/g}' sed6-update.sql ###此處採用的是後執行此命令

將sed6-update.sql中的@1,@2,@3,@4,@5,@6,@7替換成對應的列名

sed -i 's/@1/列1/g;s/@2/列2/g;s/@3/列3/g;s/@4/列4/g;s/@5/列5/g;s/@6/列6/g;s/@7/列7/g'   sed6-update.sql

此處採用的是先把@1,@2.....@6,@7替換爲對應的zx_scores表的列名

[root@VM_82_178_centos tmp]#  sed -i 's/@1/id/g;s/@2/titles/g;s/@3/icon/g;s/@4/integral/g;s/@5/isdefault/g;s/@6/create_time/g;s/@7/day/g'   sed6-update.sql

換換後的內容以下:

[root@VM_82_178_centos tmp]# tail -17 sed6-update.sql 
UPDATE `test`.`zx_scores`
SET
  id=12 ,
  titles='大將' ,
  icon=12 ,
  integral=27000 ,
  isdefault=1 ,
  create_time='2018-10-07 15:20:37' ,
  day='2018:10:07' 
WHERE
  id=12 AND
  titles='班長' AND
  icon=12 AND
  integral=27000 AND
  isdefault=1 AND
  create_time='2018-10-07 15:20:37' AND
  day='2018:10:07'

而後將@7轉換爲對應的列名day

sed  -i -r  '/WHERE/{:a;N;/@7/!ba;s/(@7=.*)/\1\;/g}' sed6-update.sql
[root@VM_82_178_centos tmp]# sed  -i -r  '/WHERE/{:a;N;/day/!ba;s/(day=.*)/\1\;/g}' sed6-update.sql

轉換後的內容以下:

[root@VM_82_178_centos tmp]#  tail -17 sed6-update.sql 
UPDATE `test`.`zx_scores`
SET
  id=12 ,
  titles='大將' ,
  icon=12 ,
  integral=27000 ,
  isdefault=1 ,
  create_time='2018-10-07 15:20:37' ,
  day='2018:10:07' 
WHERE
  id=12 AND
  titles='班長' AND
  icon=12 AND
  integral=27000 AND
  isdefault=1 AND
  create_time='2018-10-07 15:20:37' AND
  day='2018:10:07' ;

到此處數據還原已經完成,直接把sed6-update.sql 數據導入到mysql中便可

四.數據格式化

至此,咱們的sql語句已經成功還原,美中不足的一條sql語句寫成不少行,看起來不順眼,來咱們再繼續優化下,執行語句:

[root@VM_82_178_centos tmp]# cat sed6-update.sql | tr  "\n" " " > rollback.sql
將全部的換行替換成空格,此處用tr命令,因個人數據量比較大,tr執行效率相對較高,也能夠用sed命令sed -i ':label;N;s/\n/ /;b label' rollback.sql,效果都是同樣的
sed -i 's/\;/ LIMIT 1\;\n/g'   rollback.sql

在每個;前面加上 LIMIT 1,後面加上換行符:

[root@VM_82_178_centos tmp]# sed -i 's/\;/ LIMIT 1\;\n/g' rollback.sql

mysqlbinlog結合sed命令恢復update時未加where條件以前的數據

恢復到MySQL
mysqlbinlog結合sed命令恢復update時未加where條件以前的數據
還原成功:
mysqlbinlog結合sed命令恢復update時未加where條件以前的數據到此處mysqlbinlog結合sed命令恢復數據庫數據介紹完畢,歡迎留言一塊兒探討交流學習

相關文章
相關標籤/搜索