• 31 数据误删的恢复


    31 数据误删的恢复

    1 使用delete语句误删数据行

    2 使用drop table或者truncate table语句误删除数据表

    3 使用drop database语句误删数据库

    4 使用rm命令误删整个mysql实例

    误删行

    如果使用delete语句误删了数据行,可以使用flushback工具通过闪回把数据恢复出来

    Flushback恢复数据的原理,是修改binlog的内容,拿回原库重放,使用flushback工具恢复的前提是,需要确保binlog_format=rowbinlog_row_image=FULL.

    具体恢复数据时,对单个事务做如下处理:

    1 对于insert语句,对于的binlog event类型是Write_rows event,把它改成Delete_row event即可

    2 同理,对于delete,也是将Delete_rows event改为Write_rows event;

    而如果是Update_rows的话,binlog里面记录了数据行修改前和修改后的值,对调这行的位置即可

    如果误操作不是一个,而是多个,比如下面3个事务

    (A) delete ...

    (B) insert ...

    (C)update ...

    现在要把数据库恢复这3个事务操作之前的状态,用flashback工具解析binlog后,写回主库的命令是:

    (reverse C)update ...

    (reverse B)delete ...

    (reverse A)insert ...

    也就是说,如果误删除数据涉及到了多个事务的话,需要将事务的顺序调过来再执行

    需要说明的是,不建议这些操作直接在主库上进行回放

    恢复数据比较安全的做法是,恢复出一个备份,或者找一个从库作为临时库,在这个临时库上执行这些操作,然后再将确认过的临时库的数据,恢复回主库。

    防止在数据误删的情况下,业务逻辑又继续更改其他的数据,所以这时候单恢复这几行数据,而又未经确认的话,可能会出现对数据的二次破坏。

    防止数据误删的事前预防,有两个建议

    1 sql_safe_updates参数设置为on,这样一来,如果忘记在delete或者update的语句写where条件,或者where条件里面没有包含索引字段的话,这条语句的执行就会报错。

    2 代码上线前,必须经过sql审计。

    如果确定这个删除操作没问题的话,可以在delete语句中加上where条件,比如where id>0;

    但是delete全表很慢的,而且需要写回滚日志、写redo、写binlog,所以从性能角度考虑,应该优先考虑使用truncate或者drop table命令。

    使用delete命令删除的数据,可以用flashback来恢复,而使用truncate/drop tabledrop database命令删除的数据,就没有办法使用flashback来恢复。因为设置了binlog_format=row,执行这3个命令时,binlog里面只有一个truncate/drop语句,这些信息是恢复不出数据的。

    误删库/

    这种情况下,要想恢复数据,就需要使用全量备份,加增量日志的方式,这个方案要求线上有定期的全量备份,并且实时备份binlog

    在这2个条件下,假如有人中午12点误删了一个库,恢复数据的流程如下:

    1 取最近一次全备份,假设这个库一天一个备份,上次备份是当天0

    2 用备份恢复出一个临时库 

    3 从日志备份里面,取出凌晨0点之后的日志

    4 把这些日志,除了误删除数据的语句外,全部应用到临时库 

    --说明

    1 为了加速数据恢复,如果这个临时库上有多个数据库,可以在使用mysqlbinlog命令时加一个--database参数,用来指定误删表所在的库,避免了在恢复数据时还要应用其他库的日志情况。

    2 在应用日志的时候,需要跳过12点误操作的那个语句的binlog

    --如果实例没有使用gtid,只能在应用到包含12点的binlog文件的时候,先用--stop-position参数执行到误操作之前的日志,然后在用--start-position从无操作之后的日志进行恢复

    --如果使用使用gtid模式,就方便多了,假设误操作命令的gtidgtid1,那么只需要执行set gtid_next =gtid1;begin;commit;先把这个gtid加到临时实例的gtid结合,之后按顺序执行binlog的时候,就会自动跳过错误的语句。

    不过,即使这样,使用mysqlbinlog方法恢复数据还是不够快,主要原因:

    --1 如果是误删表,最好就是只恢复出这张表,也就是只重放这张表的操作,但是mysqlbinlog工具并不能指定解析一个表的日志。

    --2 mysqlbinlog解析出日志应用,应用日志的过程就只能是单线程

    一种加速的方法是,在用备份恢复出临时实例后,将这个临时实例设置成线上线上备库的从库,这样

    start slave之前,先通过执行:

    --1 change replication filter replicate_do_table = (table_name),命令,就可以让临时库只同步误操作的表。

    --2 这样做也可以用到并行复制,来加速整个数据恢复的过程。

    假设,我们发现当前临时实例需要的binlogmaster.000005开始,但是在备库执行show binlogs显示的最小的binlog文件是master.000007,意味着少了两个binlog文件,这时,就需要去binlog备份系统中找到这两个文件。

    把之前删掉的binlog放回备库的操作:

    --1 从备份系统下载master.000005master.000006这两个文件,放到备库的日志目录下

    --2 打开日志目录文件master.index,在文件开头加入两行,内容分表是”./master.000005”和”./master.000006”

    --3 重启备库,目的是要让备库重新识别这2个日志文件

    --4 现在这个备库已经有临时库需要的所有binlog,建立主备关系,就可以正常同步。

    上面两种方式恢复,主要的思路是:通过备份,加上应用binlog的方式。

    但是,一个系统不可能备份无限的日志,还需要根据成本和磁盘空间资源,设定一个日志的保留天数,或者告知需要保存某个实例恢复到半个月内的任意时间,就表示备份系统保留的日志时间就至少是半个月。

    延迟复制备库

    一般的主备复制结构存在的问题是,如果主库上有个表被删除了,这个命令会很快发给从库,进而导致所有从库的数据表也一起被删除掉,延迟复制的备库是一种特殊的备库,通过change master to master_delay=n 命令,可以指定这个备库持续保持跟主库的有n秒的延时。

    如果把n设置为3600,就代表如果主库上有数据被误删了,并且在1个小时发现了这个误命令,在延迟备库上,执行stop slave,然后跳过误操作的命令,就可以恢复数据。

    预防误删库/表的方法

    建议

    --1 账号分离,避免写错命令

    ---我们只给业务开发同学DML权限,而不给truncate/drop权限,而如果业务开发人员有DDL需求的话,也可以通过开发管理系统得到支持。

    ---即使dba成员,日常也都有规定只使用只读账号,必要的时候才使用有更新权限的账号

    --2制定操作规范,避免写错要删除的表名

    ---在删除数据表之前,必须先对表做改名操作,然后,观察一段时间,确保对业务无影响以后再删除这张表。

    ---改表名的时候,要求给表名加固定的后缀,比如to_be_deleted,然后删除表的动作必须通过管理系统执行,并且管理系统删除表的时候,而能删除固定后缀的表。

    rm 删除数据

    对于一个有高可用机制的mysql集群来说,只要不是恶意把整个集群删除,而是删除其中某一个节点的数据的话,ha系统就会开始工作,选出一个新的主库,从而保证整个集群的正常工作。

    现在不止是DBA有自动化系统,SA(系统管理员)也有自动管理系统,建议就是尽量把备份跨机房,或者跨城市。

    小结

    强调的是,预防远比处理的意义来得更大

    另外,在mysql的集群方案中,会时不时得用到备份来恢复实例,因此定期检查备份的有效性也很有必要,如果是业务开发,可以用show grants命令查看账户的权限,如果权限过大,可以建议分配权限低一些的账号,也可以评估业务的重要性,和dba商量备份的周期,是否有必要创建延迟复制的备库等等。

    数据和服务的可靠性不止是运维团队的工作,最终是各个环节一起保障的结果。

    关于空表的间隙的定义

    一个空表就只有一个间隙,在空表上执行

    begin;
    select * from t where id>1 for update;

    这个查询语句加锁的范围是next-key lock(-无穷,supermum]

    CREATE TABLE `t31` (
      `id` int(11) NOT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    (system@127.0.0.1:3306) [test]> SET SESSION tx_isolation='REPEATABLE-READ';

    SESSION A

    SESSION B

    begin;

    select * from t31 where id>1 for update;

    insert into t31 values(2);(blocked)

    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

    --show engine innodb stautsG;

  • 相关阅读:
    autorelease的对象何时被释放
    如何处理webView跳转
    根据Url 获取图片尺寸 iOS
    iOS开发证书"此证书的签发者无效"解决方法
    IOS, xib和storyboard的混用
    友盟社交分享中的那些坑
    iOS手势(滑动)返回的实现(自定义返回按钮)
    关于tableview下拉刷新崩溃的问题
    dispatch_async 和dispatch_sync
    ios调用系统界面显示英文
  • 原文地址:https://www.cnblogs.com/yhq1314/p/10774433.html
Copyright © 2020-2023  润新知