在数据库日常维护中,开发人员是最让人头痛的,很多时候都会由于SQL语句写的有问题导致服务器出问题,导致资源耗尽。最危险的操作就是在做DML操作的时候忘加where条件,导致全表更新,这是作为运维或者DBA的我们改如何处理呢?下面我分别针对update和delete操作忘加where条件导致全表更新的处理方法。

一. update 忘加where条件误操作恢复数据(binglog格式必须是ROW)

1.创建测试用的数据表

mysql> create table t1 (
-> id int unsigned not null auto_increment,
-> name char(20) not null,
-> sex enum('f','m') not null default 'm',
-> address varchar(30) not null,
-> primary key(id)
-> );
Query OK, 0 rows affected (0.31 sec)
mysql>

2.插入测试数据

mysql> insert into t1 (name,sex,address)values('daiiy','m','guangzhou');
Query OK, 1 row affected (0.01 sec) mysql> insert into t1 (name,sex,address)values('tom','f','shanghai');
Query OK, 1 row affected (0.00 sec) mysql> insert into t1 (name,sex,address)values('liany','m','beijing');
Query OK, 1 row affected (0.00 sec) mysql> insert into t1 (name,sex,address)values('lilu','m','zhuhai');
Query OK, 1 row affected (0.05 sec) mysql>

3.现在需要将id等于2的用户的地址改为zhuhai,update时没有添加where条件

mysql> select * from t1;
+----+-------+-----+-----------+
| id | name | sex | address |
+----+-------+-----+-----------+
| 1 | daiiy | m | guangzhou |
| 2 | tom | f | shanghai |
| 3 | liany | m | beijing |
| 4 | lilu | m | zhuhai |
+----+-------+-----+-----------+
4 rows in set (0.01 sec) mysql> update t1 set address='zhuhai';
Query OK, 3 rows affected (0.09 sec)
Rows matched: 4 Changed: 3 Warnings: 0 mysql> select * from t1;
+----+-------+-----+---------+
| id | name | sex | address |
+----+-------+-----+---------+
| 1 | daiiy | m | zhuhai |
| 2 | tom | f | zhuhai |
| 3 | liany | m | zhuhai |
| 4 | lilu | m | zhuhai |
+----+-------+-----+---------+
4 rows in set (0.00 sec) mysql>

4.开始恢复,在线上的话,应该比较复杂,要先进行锁表,以免数据再次被污染。(锁表,查看正在写哪个二进制日志)

mysql> lock tables t1 read ;
Query OK, 0 rows affected (0.00 sec) mysql> show master status;
+------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000024 | 1852 | | |
+------------------+----------+--------------+------------------+
1 row in set (0.00 sec) mysql>

5.分析二进制日志,并且在其中找到相关记录,在更新时是address='zhuhai',我们可以在日志中过滤出来。

[root@localhost mysql]# mysqlbinlog --no-defaults -v -v --base64-output=DECODE-ROWS mysql-bin. | grep -B  'zhuhai'
# at 
# at

# :: server id end_log_pos Table_map: `db01`.`t1` mapped to number
# :: server id end_log_pos Update_rows: table id flags: STMT_END_F
### UPDATE db01.t1
### WHERE
### @= /* INT meta=0 nullable=0 is_null=0 */
### @='daiiy' /* STRING(60) meta=65084 nullable=0 is_null=0 */
### @= /* ENUM(1 byte) meta=63233 nullable=0 is_null=0 */
### @='guangzhou' /* VARSTRING(90) meta=90 nullable=0 is_null=0 */
### SET
### @= /* INT meta=0 nullable=0 is_null=0 */
### @='daiiy' /* STRING(60) meta=65084 nullable=0 is_null=0 */
### @= /* ENUM(1 byte) meta=63233 nullable=0 is_null=0 */
### @='zhuhai' /* VARSTRING(90) meta=90 nullable=0 is_null=0 */
### UPDATE db01.t1
### WHERE
### @= /* INT meta=0 nullable=0 is_null=0 */
### @='tom' /* STRING(60) meta=65084 nullable=0 is_null=0 */
### @= /* ENUM(1 byte) meta=63233 nullable=0 is_null=0 */
### @='shanghai' /* VARSTRING(90) meta=90 nullable=0 is_null=0 */
### SET
### @= /* INT meta=0 nullable=0 is_null=0 */
### @='tom' /* STRING(60) meta=65084 nullable=0 is_null=0 */
### @= /* ENUM(1 byte) meta=63233 nullable=0 is_null=0 */
### @='zhuhai' /* VARSTRING(90) meta=90 nullable=0 is_null=0 */
### UPDATE db01.t1
### WHERE
### @= /* INT meta=0 nullable=0 is_null=0 */
### @='liany' /* STRING(60) meta=65084 nullable=0 is_null=0 */
### @= /* ENUM(1 byte) meta=63233 nullable=0 is_null=0 */
### @='beijing' /* VARSTRING(90) meta=90 nullable=0 is_null=0 */
### SET
### @= /* INT meta=0 nullable=0 is_null=0 */
### @='liany' /* STRING(60) meta=65084 nullable=0 is_null=0 */
### @= /* ENUM(1 byte) meta=63233 nullable=0 is_null=0 */
### @='zhuhai' /* VARSTRING(90) meta=90 nullable=0 is_null=0 */

可以看见里面记录了每一行的变化,这也是binglog格式要一定是row才行的原因。其中@1,@2,@3,@4,分别对应表中id,name,sex,address字段。相信大家看到这里有点明白了吧,对,没错,你猜到了,我们将相关记录转换为sql语句,重新导入数据库。

6.处理分析处理的二进制日志

[root@localhost mysql]# mysqlbinlog --no-defaults -v -v --base64-output=DECODE-ROWS mysql-bin. | sed -n '/# at 1679/,/COMMIT/p' > t1.txt
[root@localhost mysql]# cat t1.txt
# at
# :: server id end_log_pos Table_map: `db01`.`t1` mapped to number
# :: server id end_log_pos Update_rows: table id flags: STMT_END_F
### UPDATE db01.t1
### WHERE
### @= /* INT meta=0 nullable=0 is_null=0 */
### @='daiiy' /* STRING(60) meta=65084 nullable=0 is_null=0 */
### @= /* ENUM(1 byte) meta=63233 nullable=0 is_null=0 */
### @='guangzhou' /* VARSTRING(90) meta=90 nullable=0 is_null=0 */
### SET
### @= /* INT meta=0 nullable=0 is_null=0 */
### @='daiiy' /* STRING(60) meta=65084 nullable=0 is_null=0 */
### @= /* ENUM(1 byte) meta=63233 nullable=0 is_null=0 */
### @='zhuhai' /* VARSTRING(90) meta=90 nullable=0 is_null=0 */
### UPDATE db01.t1
### WHERE
### @= /* INT meta=0 nullable=0 is_null=0 */
### @='tom' /* STRING(60) meta=65084 nullable=0 is_null=0 */
### @= /* ENUM(1 byte) meta=63233 nullable=0 is_null=0 */
### @='shanghai' /* VARSTRING(90) meta=90 nullable=0 is_null=0 */
### SET
### @= /* INT meta=0 nullable=0 is_null=0 */
### @='tom' /* STRING(60) meta=65084 nullable=0 is_null=0 */
### @= /* ENUM(1 byte) meta=63233 nullable=0 is_null=0 */
### @='zhuhai' /* VARSTRING(90) meta=90 nullable=0 is_null=0 */
### UPDATE db01.t1
### WHERE
### @= /* INT meta=0 nullable=0 is_null=0 */
### @='liany' /* STRING(60) meta=65084 nullable=0 is_null=0 */
### @= /* ENUM(1 byte) meta=63233 nullable=0 is_null=0 */
### @='beijing' /* VARSTRING(90) meta=90 nullable=0 is_null=0 */
### SET
### @= /* INT meta=0 nullable=0 is_null=0 */
### @='liany' /* STRING(60) meta=65084 nullable=0 is_null=0 */
### @= /* ENUM(1 byte) meta=63233 nullable=0 is_null=0 */
### @='zhuhai' /* VARSTRING(90) meta=90 nullable=0 is_null=0 */
# at
# :: server id end_log_pos Xid =
COMMIT/*!*/;
[root@localhost mysql]#

这里sed有点复杂,需要童鞋们好好自己研究研究,这里我就不多说了。

[root@localhost mysql]# sed '/WHERE/{:a;N;/SET/!ba;s/\([^\n]*\)\n\(.*\)\n\(.*\)/\3\n\2\n\1/}' t1.txt | sed -r '/WHERE/{:a;N;/@4/!ba;s/###   @2.*//g}' | sed 's/### //g;s/\/\*.*/,/g' | sed '/WHERE/{:a;N;/@1/!ba;s/,/;/g};s/#.*//g;s/COMMIT,//g' | sed '/^$/d' > recover.sql 
[root@localhost mysql]# cat recover.sql
UPDATE db01.t1
SET
@= ,
@='daiiy' ,
@= ,
@='guangzhou' ,
WHERE
@= ;
UPDATE db01.t1
SET
@= ,
@='tom' ,
@= ,
@='shanghai' ,
WHERE
@= ;
UPDATE db01.t1
SET
@= ,
@='liany' ,
@= ,
@='beijing' ,
WHERE
@= ;
[root@localhost mysql]#

将文件中的@1,@2,@3,@4替换为t1表中id,name,sex,address字段,并删除最后字段的","号

[root@localhost mysql]# sed -i 's/@1/id/g;s/@2/name/g;s/@3/sex/g;s/@4/address/g' recover.sql
[root@localhost mysql]# sed -i -r 's/(address=.*),/\1/g' recover.sql
[root@localhost mysql]# cat recover.sql
UPDATE db01.t1
SET
id= ,
name='daiiy' ,
sex= ,
address='guangzhou'
WHERE
id= ;
UPDATE db01.t1
SET
id= ,
name='tom' ,
sex= ,
address='shanghai'
WHERE
id= ;
UPDATE db01.t1
SET
id= ,
name='liany' ,
sex= ,
address='beijing'
WHERE
id= ;
[root@localhost mysql]#

7.到这里日志就处理好了,现在导入即可(导入数据后,解锁表);

mysql> source recover.sql;
Query OK, 1 row affected (0.12 sec)
Rows matched: 1 Changed: 1 Warnings: 0 Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0 Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0 mysql> select * from t1;
+----+-------+-----+-----------+
| id | name | sex | address |
+----+-------+-----+-----------+
| 1 | daiiy | m | guangzhou |
| 2 | tom | f | shanghai |
| 3 | liany | m | beijing |
| 4 | lilu | m | zhuhai |
+----+-------+-----+-----------+
4 rows in set (0.00 sec) mysql>

可以看见数据已经完全恢复,这种方法的优点是快速,方便。

二. delete 忘加where条件误删除恢复(binglog格式必须是ROW)
其实这和update忘加条件差不多,不过这处理更简单,这里就用上面那张表做测试吧
1.模拟误删除数据
mysql> select * from t1;
+----+-------+-----+-----------+
| id | name | sex | address |
+----+-------+-----+-----------+
| 1 | daiiy | m | guangzhou |
| 2 | tom | f | shanghai |
| 3 | liany | m | beijing |
| 4 | lilu | m | zhuhai |
+----+-------+-----+-----------+
4 rows in set (0.00 sec) mysql> delete from t1;
Query OK, 4 rows affected (0.03 sec) mysql> select * from t1;
Empty set (0.00 sec) mysql>

2.在binglog中去查找相关记录

[root@localhost mysql]# mysqlbinlog --no-defaults --base64-output=decode-rows -v -v mysql-bin. | sed -n '/### DELETE FROM db01.t1/,/COMMIT/p' > delete.txt
[root@localhost mysql]# cat delete.txt
### DELETE FROM db01.t1
### WHERE
### @= /* INT meta=0 nullable=0 is_null=0 */
### @='daiiy' /* STRING(60) meta=65084 nullable=0 is_null=0 */
### @= /* ENUM(1 byte) meta=63233 nullable=0 is_null=0 */
### @='guangzhou' /* VARSTRING(90) meta=90 nullable=0 is_null=0 */
### DELETE FROM db01.t1
### WHERE
### @= /* INT meta=0 nullable=0 is_null=0 */
### @='tom' /* STRING(60) meta=65084 nullable=0 is_null=0 */
### @= /* ENUM(1 byte) meta=63233 nullable=0 is_null=0 */
### @='shanghai' /* VARSTRING(90) meta=90 nullable=0 is_null=0 */
### DELETE FROM db01.t1
### WHERE
### @= /* INT meta=0 nullable=0 is_null=0 */
### @='liany' /* STRING(60) meta=65084 nullable=0 is_null=0 */
### @= /* ENUM(1 byte) meta=63233 nullable=0 is_null=0 */
### @='beijing' /* VARSTRING(90) meta=90 nullable=0 is_null=0 */
### DELETE FROM db01.t1
### WHERE
### @= /* INT meta=0 nullable=0 is_null=0 */
### @='lilu' /* STRING(60) meta=65084 nullable=0 is_null=0 */
### @= /* ENUM(1 byte) meta=63233 nullable=0 is_null=0 */
### @='zhuhai' /* VARSTRING(90) meta=90 nullable=0 is_null=0 */
# at
# :: server id end_log_pos Xid =
COMMIT/*!*/;
[root@localhost mysql]#

3.将记录转换为SQL语句

[root@localhost mysql]# cat delete.txt | sed -n '/###/p' | sed 's/### //g;s/\/\*.*/,/g;s/DELETE FROM/INSERT INTO/g;s/WHERE/SELECT/g;' | sed -r 's/(@4.*),/\1;/g' | sed 's/@[1-9]=//g' > t1.sql
[root@localhost mysql]# cat t1.sql
INSERT INTO db01.t1
SELECT
,
'daiiy' ,
,
'guangzhou' ;
INSERT INTO db01.t1
SELECT
,
'tom' ,
,
'shanghai' ;
INSERT INTO db01.t1
SELECT
,
'liany' ,
,
'beijing' ;
INSERT INTO db01.t1
SELECT
,
'lilu' ,
,
'zhuhai' ;
[root@localhost mysql]#

4.导入数据,验证数据完整性

mysql> source t1.sql;
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0 Query OK, 1 row affected (0.02 sec)
Records: 1 Duplicates: 0 Warnings: 0 Query OK, 1 row affected (0.02 sec)
Records: 1 Duplicates: 0 Warnings: 0 Query OK, 1 row affected (0.01 sec)
Records: 1 Duplicates: 0 Warnings: 0 mysql> select * from t1;
ERROR 1046 (3D000): No database selected
mysql> select * from db01.t1;
+----+-------+-----+-----------+
| id | name | sex | address |
+----+-------+-----+-----------+
| 1 | daiiy | m | guangzhou |
| 2 | tom | f | shanghai |
| 3 | liany | m | beijing |
| 4 | lilu | m | zhuhai |
+----+-------+-----+-----------+
4 rows in set (0.00 sec) mysql>

到这里数据就完整回来了。将binglog格式设置为row有利有弊,好处是记录了每一行的实际变化,在主从复制时也不容易出问题。但是由于记录每行的变化,会占用大量磁盘,主从复制时带宽占用会有所消耗。到底是使用row还是mixed,需要在实际工作中自己去衡量,但从整体上来说,binglog的格式设置为row,都是不二的选择。

总结:

所以在数据库操作的过程中我们需要格外小心,当然开发那边我们需要做好权限的控制,不过有一个参数可以解决我们的问题,让我们不用担心类似的问题发生:

在[mysql]段落开启这个参数:

safe-updates

这样当我们在做DML操作时忘记加where条件时,mysqld服务器是不会执行操作的:

mysql> select *  from t1;
+----+------------------+
| id | name |
+----+------------------+
| 1 | yayun |
| 2 | atlas |
| 3 | mysql |
| 6 | good yayun heheh |
+----+------------------+
4 rows in set (0.00 sec) mysql> delete from t1;
ERROR 1175 (HY000): You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column
mysql>

MySQL 误操作后数据恢复(update,delete忘加where条件)的更多相关文章

  1. MySQL 误删数据、误更新数据(update,delete忘加where条件)

    MySQL 误操作后数据恢复(update,delete忘加where条件) 关键词:mysql误删数据,mysql误更新数据 转自:https://www.cnblogs.com/gomysql/p ...

  2. MySQL 误操作后数据恢复(update,delete忘加where条件)【转】

    在数据库日常维护中,开发人员是最让人头痛的,很多时候都会由于SQL语句 写的有问题导致服务器出问题,导致资源耗尽.最危险的操作就是在做DML操作的时候忘加where条件,导致全表更新,这是作为运维或者 ...

  3. MySQL误操作后如何快速恢复数据

    基本上每个跟数据库打交道的程序员(当然也可能是你同事)都会碰一个问题,MySQL误操作后如何快速回滚?比如,delete一张表,忘加限制条件,整张表没了.假如这还是线上环境核心业务数据,那这事就闹大了 ...

  4. MySQL 误操作后如何快速恢复数据~!~!~

    基本上每个跟数据库打交道的程序员(当然也可能是你同事)都会碰一个问题,MySQL误操作后如何快速回滚?比如,delete一张表,忘加限制条件,整张表没了.假如这还是线上环境核心业务数据,那这事就闹大了 ...

  5. MySQL误操作后如何快速回滚(转)

    本文转自http://www.cnblogs.com/dfcao/p/6147970.html#undefined 感谢作者 基本上每个跟数据库打交道的程序员(当然也可能是你同事)都会碰一个问题,My ...

  6. MySQL误操作后如何快速恢复数据?

    摘要: 利用binlog闪回误操作数据. 基本上每个跟数据库打交道的程序员(当然也可能是你同事)都会碰一个问题,MySQL误操作后如何快速回滚?比如,delete一张表,忘加限制条件,整张表没了.假如 ...

  7. mysql误操作后通过binlog恢复,同时解决tmp目录满的问题

    注意: 本文的恢复,并不是基于恢复某个时间点的全量备份后的增量恢复,而是指在现有数据库基础上基于binlog的恢复.适用于较小的数据误操作. 提取日志文件为sql语句: /usr-ext/local/ ...

  8. MySQL中truncate误操作后的数据恢复案例

    MySQL中truncate误操作后的数据恢复案例 这篇文章主要介绍了MySQL中truncate误操作后的数据恢复案例,主要是要从日志中定位到truncate操作的地方然后备份之前丢失的数据,需要的 ...

  9. MySQL误操作删除后,怎么恢复数据?

    MySQL误操作删除后,怎么恢复数据?登陆查数据库mysql> select * from abc.stad;+----+-----------+| id | name |+----+----- ...

随机推荐

  1. Python 获得最近一个月的每天的日期

    直接上代码: #coding:utf-8 # from common.contest import * import datetime import time begin_date = (dateti ...

  2. 你见过的最全面的python重点

    首先和大家说个对不起,由于总结了太多的东西,所以篇幅有点长,这也是我"缝缝补补"总结了好久的东西,对于Nginx的东西我没总结在这里,大家可以Python聚焦看,点击直达专栏哦. ...

  3. Python中的三元运算符

    Python中的三元运算符 对于如下需求: if var1>1 : goal = "执行表达式1" else: goal = "执行表达式2" 1.在其他 ...

  4. 【gulp】gulp + browsersync 构建前端项目自动化工作流

    什么是 gulp? gulp.js 是一个自动化构建工具,开发者可以使用它在项目开发过程中自动执行常见任务.gulp.js 是基于 node.js 构建的,利用 node.js 流的威力,你可以快速构 ...

  5. 解决java.lang.annotation.AnnotationFormatError: Invalid default: public abstract java.lang.Class org.robolectric.annotation.Config.application()

    Deleting the .gradle folder worked for me too. Odd. Guessing some high level caching going on somewh ...

  6. GeForce GTX 1080 ti安装记录

    安装GeForce GTX 1080ti 安装GeForce GTX 1080ti,8+8pin需要全接,接4pin就开机显示器上会提示电源线没接完,将显示器线接在显卡上. 设置Win 10 pro ...

  7. “Java是编译执行的语言”这句话对吗?

    现在让你谈谈对Java平台的理解,你是否会感觉内容过于庞大?这个问题是比较宽泛的,Java发展到现在已经不仅仅是语言这么简单了,Java平台涉及的,包括但不仅限于下面提到的这些内容: Java语言本身 ...

  8. Oracle_lhr_CentOS 7.3 ECS上搭建RAC 18c+单实例DG+EMCC+DG的FSFO快速故障转移配置

    Oracle_lhr_CentOS 7.3  ECS上搭建RAC 18c+单实例DG+EMCC+DG的FSFO快速故障转移配置 [大型连续免费公开课]Oracle 18c rac+dg+13.3的em ...

  9. PXE:终于成功启动 fedora live 了!

    default menu.c32 timeout 1 label fedora29-live menu label fedora29-live from ftp kernel fedora29live ...

  10. JavaScript:事件

    1. 事件对象|事件冒泡 // 示例代码:[鼠标点击事件]的事件对象 var oBtn=document.getElementById('btn1'); // 按钮DOM oBtn.onclick=f ...