MySQL【Update誤操作】回滾(轉)


前言:
      繼上一篇MySQL【Delete誤操作】回滾之后,現在介紹下Update回滾,操作數據庫時候難免會因為“大意”而誤操作,需要快速恢復的話通過備份來恢復是不太可能的,因為需要還原和binlog差來恢復,等不了,很費時。這里說明因為Update 操作的恢復方法:主要還是通過binlog來進行恢復,前提是binlog_format必須是Row格式,否則只能通過備份來恢復數據了。和上一篇的條件一樣。
方法:
     條件:開啟Binlog,Format為Row。
     步驟:
1.通過MySQL自帶工具mysqlbinlog 指定導出操作的記錄:
表結構和記錄數:

復制代碼
root@localhost : test 10:06:16>select count(*) from me_info; +----------+ | count(*) | +----------+ | 84183 | +----------+ 1 row in set (0.00 sec) root@localhost : test 10:12:14>select id,realName,contactAddress from me_info limit 3; +---------+--------------------------+--------------------+ | id | realName | contactAddress | +---------+--------------------------+--------------------+ | 2123269 | 數據庫管理員 | 浙江杭州濱江 | | 2123270 | 中級數據庫管理員 | 浙江杭州西湖 | | 2123271 | 高級數據庫管理員 | 浙江杭州余杭 | +---------+--------------------------+--------------------+  root@localhost : test 10:12:18>desc me_info; +-----------------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-----------------+--------------+------+-----+---------+-------+ | id | int(11) | NO | MUL | 0 | | | birthTime | date | YES | | NULL | | | enName | varchar(255) | YES | | NULL | | | gender | tinyint(2) | YES | | 0 | | | identity | varchar(255) | YES | | | | | identitylType | tinyint(2) | YES | | 0 | | | interest | varchar(255) | YES | | | | | jobYear | int(11) | YES | | 0 | | | livePlace | mediumint(6) | YES | | 0 | | | location | mediumint(6) | YES | | 0 | | | married | tinyint(2) | YES | | 0 | | | mdCerti | tinyint(2) | YES | | 0 | | | mdCertiNum | varchar(255) | YES | | | | | photo | varchar(255) | YES | | | | | posit | mediumint(6) | YES | | NULL | | | graduateMajor | int(11) | YES | | 0 | | | realName | varchar(255) | YES | | NULL | | | userPublic | tinyint(2) | YES | | NULL | | | email | varchar(255) | YES | | NULL | | | contactCell | varchar(255) | YES | | NULL | | | contactPhone | varchar(255) | YES | | NULL | | | contactZip | varchar(6) | YES | | NULL | | | contactWebsite | varchar(255) | YES | | NULL | | | contactLocation | mediumint(6) | YES | | 0 | | | contactAddress | varchar(255) | YES | | NULL | | | userName | varchar(50) | YES | | NULL | | | education | int(11) | YES | | 0 | | | workName | varchar(255) | YES | | NULL | | | workCategory | varchar(255) | YES | | NULL | | | nowSalary | int(11) | YES | | 0 | | | grade | int(11) | YES | | 0 | | | userId | int(11) | YES | | 0 | | | jobApplyStatus | tinyint(4) | YES | | NULL | | | source | tinyint(4) | YES | | NULL | | | englishLevel | tinyint(4) | YES | | 0 | | | modifyTime | datetime | YES | | NULL | | +-----------------+--------------+------+-----+---------+-------+
復制代碼

更新表:

復制代碼
root@localhost : test 10:15:09>update me_info set realName='周吳鄭王',contactAddress='浙江vv杭州vv北京'; Query OK, 84183 rows affected (1.56 sec) Rows matched: 84183 Changed: 84183 Warnings: 0 root@localhost : test 11:11:08>select id,realName,contactAddress from me_info limit 3; +---------+--------------+------------------------+ | id | realName | contactAddress | +---------+--------------+------------------------+ | 2123269 | 周吳鄭王 | 浙江vv杭州vv北京 | | 2123270 | 周吳鄭王 | 浙江vv杭州vv北京 | | 2123271 | 周吳鄭王 | 浙江vv杭州vv北京 | +---------+--------------+------------------------+ 3 rows in set (0.00 sec)
復制代碼

最后通過mysqlbinlog 取出:

root@zhoujy:/var/log/mysql# mysqlbinlog --no-defaults --start-datetime='2012-12-26 22:15:05' --stop-datetime='2012-12-26 22:17:00' -vv mysql-bin.000001 > /home/zhoujy/restore/me_info.txt

原始數據:

復制代碼
### UPDATE test.me_info ### WHERE ### @1=2123269 /* INT meta=0 nullable=0 is_null=0 */ ### @2='1990:11:12' /* DATE meta=0 nullable=1 is_null=0 */ ### @3=NULL /* DATE meta=765 nullable=1 is_null=1 */ ### @4=2 /* TINYINT meta=0 nullable=1 is_null=0 */ ### @5='' /* VARSTRING(765) meta=765 nullable=1 is_null=0 */ ### @6=0 /* TINYINT meta=0 nullable=1 is_null=0 */ ### @7='' /* VARSTRING(765) meta=765 nullable=1 is_null=0 */ ### @8=-1 (4294967295) /* INT meta=0 nullable=1 is_null=0 */ ### @9=0 /* MEDIUMINT meta=0 nullable=1 is_null=0 */ ### @10=340800 /* MEDIUMINT meta=0 nullable=1 is_null=0 */ ### @11=1 /* TINYINT meta=0 nullable=1 is_null=0 */ ### @12=0 /* TINYINT meta=0 nullable=1 is_null=0 */ ### @13='' /* VARSTRING(765) meta=765 nullable=1 is_null=0 */ ### @14='' /* VARSTRING(765) meta=765 nullable=1 is_null=0 */ ### @15=0 /* MEDIUMINT meta=0 nullable=1 is_null=0 */ ### @16=32071 /* INT meta=0 nullable=1 is_null=0 */ ### @17='數據庫管理員' /* VARSTRING(765) meta=765 nullable=1 is_null=0 */ ### @18=NULL /* VARSTRING(765) meta=0 nullable=1 is_null=1 */ ### @19='123456@qq.comx0a' /* VARSTRING(765) meta=765 nullable=1 is_null=0 */ ### @20='123456' /* VARSTRING(765) meta=765 nullable=1 is_null=0 */ ### @21='0571-123456' /* VARSTRING(765) meta=765 nullable=1 is_null=0 */ ### @22=NULL /* VARSTRING(765) meta=18 nullable=1 is_null=1 */ ### @23='' /* VARSTRING(765) meta=765 nullable=1 is_null=0 */ ### @24=340100 /* MEDIUMINT meta=0 nullable=1 is_null=0 */ ### @25='浙江杭州濱江' /* VARSTRING(765) meta=765 nullable=1 is_null=0 */ ### @26=NULL /* VARSTRING(765) meta=150 nullable=1 is_null=1 */ ### @27=1 /* INT meta=0 nullable=1 is_null=0 */ ### @28=NULL /* INT meta=765 nullable=1 is_null=1 */ ### @29=NULL /* INT meta=765 nullable=1 is_null=1 */ ### @30=0 /* INT meta=0 nullable=1 is_null=0 */ ### @31=0 /* INT meta=0 nullable=1 is_null=0 */ ### @32=1700671 /* INT meta=0 nullable=1 is_null=0 */ ### @33=NULL /* INT meta=0 nullable=1 is_null=1 */ ### @34=3 /* TINYINT meta=0 nullable=1 is_null=0 */ ### @35=0 /* TINYINT meta=0 nullable=1 is_null=0 */ ### @36=NULL /* TINYINT meta=0 nullable=1 is_null=1 */ ### SET ### @1=2123269 /* INT meta=0 nullable=0 is_null=0 */ ### @2='1990:11:12' /* DATE meta=0 nullable=1 is_null=0 */ ### @3=NULL /* DATE meta=765 nullable=1 is_null=1 */ ### @4=2 /* TINYINT meta=0 nullable=1 is_null=0 */ ### @5='' /* VARSTRING(765) meta=765 nullable=1 is_null=0 */ ### @6=0 /* TINYINT meta=0 nullable=1 is_null=0 */ ### @7='' /* VARSTRING(765) meta=765 nullable=1 is_null=0 */ ### @8=-1 (4294967295) /* INT meta=0 nullable=1 is_null=0 */ ### @9=0 /* MEDIUMINT meta=0 nullable=1 is_null=0 */ ### @10=340800 /* MEDIUMINT meta=0 nullable=1 is_null=0 */ ### @11=1 /* TINYINT meta=0 nullable=1 is_null=0 */ ### @12=0 /* TINYINT meta=0 nullable=1 is_null=0 */ ### @13='' /* VARSTRING(765) meta=765 nullable=1 is_null=0 */ ### @14='' /* VARSTRING(765) meta=765 nullable=1 is_null=0 */ ### @15=0 /* MEDIUMINT meta=0 nullable=1 is_null=0 */ ### @16=32071 /* INT meta=0 nullable=1 is_null=0 */ ### @17='周吳鄭王' /* VARSTRING(765) meta=765 nullable=1 is_null=0 */ ### @18=NULL /* VARSTRING(765) meta=0 nullable=1 is_null=1 */ ### @19='123456@qq.comx0a' /* VARSTRING(765) meta=765 nullable=1 is_null=0 */ ### @20='123456' /* VARSTRING(765) meta=765 nullable=1 is_null=0 */ ### @21='0571-123456' /* VARSTRING(765) meta=765 nullable=1 is_null=0 */ ### @22=NULL /* VARSTRING(765) meta=18 nullable=1 is_null=1 */ ### @23='' /* VARSTRING(765) meta=765 nullable=1 is_null=0 */ ### @24=340100 /* MEDIUMINT meta=0 nullable=1 is_null=0 */ ### @25='浙江vv杭州vv北京' /* VARSTRING(765) meta=765 nullable=1 is_null=0 */ ### @26=NULL /* VARSTRING(765) meta=150 nullable=1 is_null=1 */ ### @27=1 /* INT meta=0 nullable=1 is_null=0 */ ### @28=NULL /* INT meta=765 nullable=1 is_null=1 */ ### @29=NULL /* INT meta=765 nullable=1 is_null=1 */ ### @30=0 /* INT meta=0 nullable=1 is_null=0 */ ### @31=0 /* INT meta=0 nullable=1 is_null=0 */ ### @32=1700671 /* INT meta=0 nullable=1 is_null=0 */ ### @33=NULL /* INT meta=0 nullable=1 is_null=1 */ ### @34=3 /* TINYINT meta=0 nullable=1 is_null=0 */ ### @35=0 /* TINYINT meta=0 nullable=1 is_null=0 */ ### @36=NULL /* TINYINT meta=0 nullable=1 is_null=1 */
復制代碼

Row格式的binlog記錄的格式如上面所示,需要做的工作就是把Update的操作的WHERE好SET對調,上面的都是有一定規律的,並且需要注意的是:
除了MySQL【Delete誤操作】回滾事項外,還有
①:需要把@表示的“虛列”換成“實列”。
②:更新NULL值的時候,WHERE 后面的字段有NULL的,不能用“=”號,需要用“is”。
清楚里之后,可以用腳本來還原剛才update的值:

復制代碼
#!/bin/env python # -*- encoding: utf-8 -*- #------------------------------------------------------------------------------- # Name: restore_update.py # Purpose: 通過Binlog恢復Update誤操作數據 # Author: zhoujy # Created: 2012-12-26 # update: 2012-12-26 # Copyright: (c) Mablevi 2012 # Licence: zjy # Usage: python restore_update.py binlog.txt tablename #------------------------------------------------------------------------------- import MySQLdb import sys reload(sys) sys.setdefaultencoding("utf-8") def get_column(conn,tbname): #從庫中取字段"實名",代替@字段名。 query = "select group_concat(COLUMN_NAME) from information_schema.COLUMNS where TABLE_NAME='%s'" %tbname cursor = conn.cursor() cursor.execute(query) items = cursor.fetchone() return items def read_binlog(file,tbname,column): f = open(file) columns = column.split(',') num = '@'+str(len(columns)) #取字段數 while True: lines = f.readline() if lines.strip()[0:3] == '###': lines=lines.split(' ',3) if lines[1].strip() == 'WHERE': #SET和WHERE對調。 lines[1] = "SET" sep1 = ',' sep2 = '' #如果是SET 后面的字段,則用這些定義的分隔符。 sep3 = ' = ' lines[-1] = lines[-1].strip() elif lines[1].strip() == 'SET': #SET和WHERE對調。 lines[1] = "WHERE" lines[-1] = lines[-1].strip() sep1 = '\nand' sep2 = ';\n' #WHERE 后面的字段,則用這些定義的分隔符。 sep3 = ' is ' else: lines[-1] = lines[-1].strip() if ''.join(lines).find('@') <> -1 and lines[3].split('=',1)[0] <> num: c = int(lines[3].split('=',1)[0].split('@')[1]) col = columns[c-1] #取實名字段。 lines[3] = lines[3].split('=',1)[-1].strip() if lines[3].strip('\'').strip().find('\'') <> -1: lines[3] = lines[3].split('/*')[0].strip('\'').strip().strip('\'').replace('\\','').replace('\'','\\\'') lines[3] = col + " = " + '\'' + lines[3] + '\'' + sep1 elif lines[3].find('INT meta') <> -1 and lines[3].find('NULL') == -1: lines[3] = lines[3].split('/*')[0].strip() lines[3] = col + " = " + lines[3].split()[0] + sep1 elif lines[3].find('NULL') <> -1: #和Delete腳本一樣,不一樣的是分隔符。 lines[3] = lines[3].split('/*')[0].strip() lines[3] = col + sep3 + lines[3] + sep1 else: lines[3] = lines[3].split('/*')[0].strip('\'').strip().strip('\'').replace('\\','').replace('\'','\\\'') lines[3] = col + " = " + '\'' + lines[3].strip('\''' ') + '\'' + sep1 if ''.join(lines).find('@') <> -1 and lines[3].split('=',1)[0] == num: c = int(lines[3].split('=',1)[0].split('@')[1]) col = columns[c-1] lines[3] = lines[3].split('=',1)[-1].strip() if lines[3].strip('\'').strip().find('\'') <> -1: lines[3] = lines[3].split('/*')[0].strip('\'').strip().strip('\'').replace('\\','').replace('\'','\\\'') lines[3] = col + " = " + '\'' + lines[3] + '\'' + sep2 elif lines[3].find('INT meta') <> -1 and lines[3].find('NULL') == -1: lines[3] = lines[3].split('/*')[0].strip() lines[3] = col + " = " + lines[3].split()[0] + sep2 elif lines[3].find('NULL') <> -1: #和Delete腳本一樣,不一樣的是分隔符。 lines[3] = lines[3].split('/*')[0].strip() lines[3] = col + sep3 + lines[3] + sep2 else: lines[3] = lines[3].split('/*')[0].strip('\'').strip().strip('\'').replace('\\','').replace('\'','\\\'') lines[3] = col + " = " + '\'' + lines[3].strip('\''' ') + '\'' + sep2 print ' '.join(lines[1:]) if lines == '': break if __name__=='__main__': conn = MySQLdb.connect(host='localhost',user='root',passwd='123456',charset='utf8',db='test') col = get_column(conn,sys.argv[2]) for column in col: read_binlog(sys.argv[1],sys.argv[2],column)
復制代碼

 執行腳本:格式:python 腳本名 binlog文本 表名

zhoujy@zhoujy:~/restore$ python restore_update.py me_info.txt me_info > me_info.sql

效果:

復制代碼
PDATE test.me_info
SET id = 2123269, birthTime = '1990:11:12', enName = NULL, gender = 2, identity = '', identitylType = 0, interest = '', jobYear = -1, livePlace = 0, location = 340800, married = 1, mdCerti = 0, mdCertiNum = '', photo = '', posit = 0, graduateMajor = 32071, realName = '數據庫管理員', userPublic = NULL, email = '123456@qq.comx0a', contactCell = '123456', contactPhone = '0571-123456', contactZip = NULL, contactWebsite = '', contactLocation = 340100, contactAddress = '浙江杭州濱江', userName = NULL, education = 1, workName = NULL, workCategory = NULL, nowSalary = 0, grade = 0, userId = 1700671, jobApplyStatus = NULL, source = 3, englishLevel = 0, modifyTime = NULL WHERE id = 2123269 and birthTime = '1990:11:12' and enName is NULL and gender = 2 and identity = '' and identitylType = 0 and interest = '' and jobYear = -1 and livePlace = 0 and location = 340800 and married = 1 and mdCerti = 0 and mdCertiNum = '' and photo = '' and posit = 0 and graduateMajor = 32071 and realName = '周吳鄭王' and userPublic is NULL and email = '123456@qq.comx0a' and and contactCell = '123456' and contactPhone = '0571-123456' and contactZip is NULL and contactWebsite = '' and contactLocation = 340100 and contactAddress = '浙江vv杭州vv北京' and userName is NULL and education = 1 and workName is NULL and workCategory is NULL and nowSalary = 0 and grade = 0 and userId = 1700671 and jobApplyStatus is NULL and source = 3 and englishLevel = 0 and modifyTime is NULL;
復制代碼

還原:

zhoujy@zhoujy:~/restore$ mysql test < me_info.sql

結果,表結果和記錄數:

復制代碼
root@localhost : test 11:11:14>select count(*) from me_info; +----------+ | count(*) | +----------+ | 84183 | +----------+ 1 row in set (0.00 sec) root@localhost : test 11:12:36>select id,realName,contactAddress from me_info limit 3; +---------+--------------------------+--------------------+ | id | realName | contactAddress | +---------+--------------------------+--------------------+ | 2123269 | 數據庫管理員 | 浙江杭州濱江 | | 2123270 | 中級數據庫管理員 | 浙江杭州西湖 | | 2123271 | 高級數據庫管理員 | 浙江杭州余杭 | +---------+--------------------------+--------------------+ 3 rows in set (0.00 sec)
復制代碼

總結:

【更新於 20160504】

開啟Row模式的回滾操作,特別要注意表字符集的問題,保證表的字符集一致,否則出現亂碼問題,如:

復制代碼
表結構:
CREATE TABLE `tmp_1` ( `id` int(11) DEFAULT NULL, `name` varchar(10) DEFAULT NULL, `address` varchar(20) CHARACTER SET gbk DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8 操作: insert into tmp_1 values(1,'UTF8字符串', 'GKB字符串'); insert into tmp_1 values(2,'我們a', '你們a'); binlog記錄: '/*!*/; ### INSERT INTO `dba_test`.`tmp_1` ### SET ### @1=1 /* INT meta=0 nullable=1 is_null=0 */ ### @2='UTF8字符串' /* VARSTRING(30) meta=30 nullable=1 is_null=0 */ ### @3='GKB?ַ???' /* VARSTRING(40) meta=40 nullable=1 is_null=0 */ #出現亂碼,復制是沒有問題的,要是反轉回滾就會出現亂碼!
復制代碼

更多的信息見:

http://www.gpfeng.com/?p=259


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM