日常工作中,总会有因手抖、写错条件、写错表名、错连生产库造成的误删库表和数据的事情发生,那么,如果连数据都恢复不了,还要什么 DBA。

相关文章

MySQL备份策略:https://segmentfault.com/a/1190000019955399
MySQL数据恢复:https://segmentfault.com/a/1190000020116271

1.前言

数据恢复的前提的做好备份,且开启 binlog, 格式为 row。如果没有备份文件,那么删掉库表后就真的删掉了,lsof 中还有记录的话,有可能恢复一部分文件,但若刚好数据库没有打开这个表文件,那就只能跑路了。

如果没有开启 binlog,那么恢复数据后,从备份时间点开始的数据都没得了。如果 binlog 格式不为 row,那么在误操作数据后就没有办法做闪回操作,只能老老实实地走备份恢复流程。

2.直接恢复

直接恢复是使用备份文件做全量恢复,这是最常见的场景

2.1.mysqldump备份全量恢复

使用 mysqldump 文件恢复数据非常简单,直接解压了执行

gzip -d backup.sql.gz | mysql -u<user> -h<host> -P<port> -p

2.2.xtrabackup备份全量恢复

恢复过程

# 步骤一:解压(如果没有压缩可以忽略这一步)
innobackupex --decompress <备份文件所在目录># 步骤二:应用日志
innobackupex --apply-log <备份文件所在目录> # 步骤三:复制备份文件到数据目录
innobackupex --datadir=<MySQL数据目录> --copy-back <备份文件所在目录>

2.3.基于时间点恢复

基于时间点的恢复依赖的是binlog日志,需要从 binlog 中找过从备份点到恢复点的所有日志,然后应用,我们测试一下

新建测试表

chengqm-3306>>show create table mytest.mytest G;
*************************** 1. row ***************************Table: mytest
Create Table: CREATE TABLE `mytest` (`id` int(11) NOT NULL AUTO_INCREMENT,`ctime` datetime DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

每秒插入一条数据

[mysql@mysql-test ~]$ while true; do mysql -S /tmp/mysql.sock -e
'insert into mytest.mytest(ctime)values(now())';date;sleep 1;done

备份

[mysql@mysql-test ~]$ mysqldump --opt --single-transaction --master-data=2 --default-character-set=utf8 -S /tmp/mysql.sock -A > backup.sql

找出备份时的日志位置

[mysql@mysql-test ~]$ head -n 25 backup.sql | grep 'CHANGE MASTER TO MASTER_LOG_FILE'
-- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000032', MASTER_LOG_POS=39654;

假设要恢复到 2019-08-09 11:01:54 这个时间点,我们从 binlog 中查找从 39654 到 019-08-09 11:01:54 的日志

[mysql@mysql-test ~]$ mysqlbinlog --start-position=39654 --stop-datetime='2019-08-09 11:01:54' /data/mysql_log/mysql_test/mysql-bin.000032 > backup_inc.sql
[mysql@mysql-test-83 ~]$ tail -n 20 backup_inc.sql
......
### INSERT INTO `mytest`.`mytest`
### SET
###   @1=161 /* INT meta=0 nullable=0 is_null=0 */
###   @2='2019-08-09 11:01:53' /* DATETIME(0) meta=0 nullable=1 is_null=0 */
......

当前数据条数

-- 2019-08-09 11:01:54之前的数据条数
chengqm-3306>>select count(*) from mytest.mytest where ctime < '2019-08-09 11:01:54';
+----------+
| count(*) |
+----------+
|      161 |
+----------+
1 row in set (0.00 sec)-- 所有数据条数
chengqm-3306>>select count(*) from mytest.mytest;
+----------+
| count(*) |
+----------+
|      180 |
+----------+
1 row in set (0.00 sec)

然后执行恢复

# 全量恢复
[mysql@mysql-test ~]$ mysql -S /tmp/mysql.sock < backup.sql # 应用增量日志
[mysql@mysql-test ~]$ mysql -S /tmp/mysql.sock < backup_inc.sql

检查数据

chengqm-3306>>select count(*) from mytest.mytest;
+----------+
| count(*) |
+----------+
|      161 |
+----------+
1 row in set (0.00 sec)chengqm-3306>>select * from mytest.mytest order by id desc limit 5;
+-----+---------------------+
| id  | ctime               |
+-----+---------------------+
| 161 | 2019-08-09 11:01:53 |
| 160 | 2019-08-09 11:01:52 |
| 159 | 2019-08-09 11:01:51 |
| 158 | 2019-08-09 11:01:50 |
| 157 | 2019-08-09 11:01:49 |
+-----+---------------------+
5 rows in set (0.00 sec)

已经恢复到 2019-08-09 11:01:54 这个时间点

3.恢复一个表

3.1.从mysqldump备份恢复一个表

假设要恢复的表是 mytest.mytest

# 提取某个库的所有数据
sed -n '/^-- Current Database: `mytest`/,/^-- Current Database:/p' backup.sql > backup_mytest.sql# 从库备份文件中提取建表语句
sed -e'/./{H;$!d;}' -e 'x;/CREATE TABLE `mytest`/!d;q' backup_mytest.sql > mytest_table_create.sql# 从库备份文件中提取插入数据语句
grep -i 'INSERT INTO `mytest`' backup_mytest.sql > mytest_table_insert.sql# 恢复表结构到 mytest 库
mysql -u<user> -p mytest < mytest_table_create.sql# 恢复表数据到 mytest.mytest 表
mysql -u<user> -p mytest <  mytest_table_insert.sql

3.2.从xtrabackup备份恢复一个表

假设 ./backup_xtra_full 目录为解压后应用过日志的备份文件

3.2.1 MyISAM 表

假设从备份文件中恢复表 mytest.t_myisam,从备份文件中找到 t_myisam.frm t_myisam.MYD t_myisam.MYI 这 3 个文件,复制到对应的数据目录中,并授权。

进入 MySQL,检查表情况

chengqm-3306>>show tables;
+------------------+
| Tables_in_mytest |
+------------------+
| mytest           |
| t_myisam         |
+------------------+
2 rows in set (0.00 sec)chengqm-3306>>check table t_myisam;
+-----------------+-------+----------+----------+
| Table           | Op    | Msg_type | Msg_text |
+-----------------+-------+----------+----------+
| mytest.t_myisam | check | status   | OK       |
+-----------------+-------+----------+----------+
1 row in set (0.00 sec)

3.2.2.Innodb 表

假设从备份文件中恢复表 mytest.t_innodb,恢复前提是设置了 innodb_file_per_table = on

  1. 起一个新实例
  2. 在实例上建一个和原来一模一样的表
  3. 执行 alter table t_innodb discard tablespace;,删除表空间,这个操作会把 t_innodb.ibd 删除
  4. 从备份文件中找到 t_innodb.ibd 这个文件,复制到对应的数据目录,并授权
  5. 执行 alter table t_innodb IMPORT tablespace; 加载表空间
  6. 执行 flush table t_innodb;check table t_innodb; 检查表
  7. 使用 mysqldump 导出数据,然后再导入到要恢复的数据库

注意:

  • 在新实例上恢复再dump出来是为了避免风险,如果是测试,可以直接在原库上操作步骤 2-6
  • 只在 8.0 以前的版本有效

4.跳过误操作SQL

跳过误操作 SQL 一般用于执行了无法闪回的操作比如 drop tabledatabase

4.1.使用备份文件恢复跳过

4.1.1.不开启 GTID

使用备份文件恢复的步骤和基于时间点恢复的操作差不多,区别在于多一个查找 binlog 操作

举个例子,我这里建立了两个表 a 和 b,每分钟插入一条数据,然后做全量备份,再删除表 b,现在要跳过这条 SQL。

删除表 b 后的数据库状态

chgnqm-3306>>show tables;
+------------------+
| Tables_in_mytest |
+------------------+
| a                |
+------------------+
1 row in set (0.00 sec)

1 找出备份时的日志位置

[mysql@mysql-test ~]$ head -n 25 backup.sql | grep 'CHANGE MASTER TO MASTER_LOG_FILE'
-- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000034', MASTER_LOG_POS=38414;

2 找出执行了 drop table 语句的 pos 位置

[mysql@mysql-test mysql_test]$  mysqlbinlog -vv /data/mysql_log/mysql_test/mysql-bin.000034 | grep -i -B 3 'drop table `b`';
# at 120629
#190818 19:48:30 server id 83  end_log_pos 120747 CRC32 0x6dd6ab2a     Query    thread_id=29488    exec_time=0    error_code=0
SET TIMESTAMP=1566128910/*!*/;
DROP TABLE `b` /* generated by server */

从结果中我们可以看到 drop 所在语句的开始位置是 120629,结束位置是 120747

3 从 binglog 中提取跳过这条语句的其他记录

# 第一条的 start-position 为备份文件的 pos 位置,stop-position 为 drop 语句的开始位置
mysqlbinlog -vv --start-position=38414 --stop-position=120629 /data/mysql_log/mysql_test/mysql-bin.000034 > backup_inc_1.sql# 第二条的 start-position 为 drop 语句的结束位置
mysqlbinlog -vv --start-position=120747 /data/mysql_log/mysql_test/mysql-bin.000034 > backup_inc_2.sql

4 恢复备份文件

[mysql@mysql-test ~]$ mysql -S /tmp/mysql.sock < backup.sql

全量恢复后状态

chgnqm-3306>>show tables;
+------------------+
| Tables_in_mytest |
+------------------+
| a                |
| b                |
+------------------+
2 rows in set (0.00 sec)chgnqm-3306>>select count(*) from a;
+----------+
| count(*) |
+----------+
|       71 |
+----------+
1 row in set (0.00 sec)

5 恢复增量数据

[mysql@mysql-test ~]$ mysql -S /tmp/mysql.sock < backup_inc_1.sql
[mysql@mysql-test ~]$ mysql -S /tmp/mysql.sock < backup_inc_2.sql

恢复后状态,可以看到已经跳过了 drop 语句

chgnqm-3306>>show tables;
+------------------+
| Tables_in_mytest |
+------------------+
| a                |
| b                |
+------------------+
2 rows in set (0.00 sec)chgnqm-3306>>select count(*) from a;
+----------+
| count(*) |
+----------+
|      274 |
+----------+
1 row in set (0.00 sec)

4.1.2 开启 GTID

使用 GTID 可以直接跳过错误的 SQL

1.找出备份时的日志位置

2.找出执行了 drop table 语句的 GTID 值

3.导出备份时日志位置到最新的 binglog 日志

4.恢复备份文件

5.跳过这个 GTID

SET SESSION GTID_NEXT='对应的 GTID 值';
BEGIN; COMMIT;
SET SESSION GTID_NEXT = AUTOMATIC;

6.应用步骤 3 得到的增量 binlog 日志

4.2 使用延迟库跳过

4.2.1 不开启 GTID

使用延迟库恢复的关键操作在于 start slave until

我在测试环境搭建了两个 MySQL 节点,节点二延迟600秒,新建 a,b 两个表,每秒插入一条数据模拟业务数据插入。

localhost:3306 -> localhost:3307(delay 600)

当前节点二状态

chengqm-3307>>show slave status G;
...Master_Port: 3306Connect_Retry: 60Master_Log_File: mysql-bin.000039Read_Master_Log_Pos: 15524Relay_Log_File: mysql-relay-bin.000002Relay_Log_Pos: 22845Relay_Master_Log_File: mysql-bin.000038Slave_IO_Running: YesSlave_SQL_Running: Yes
...Seconds_Behind_Master: 600
...

当前节点二表

chengqm-3307>>show tables;
+------------------+
| Tables_in_mytest |
+------------------+
| a                |
| b                |
+------------------+

在节点一删除表 b

chengqm-3306>>drop table b;
Query OK, 0 rows affected (0.00 sec)chengqm-3306>>show tables;
+------------------+
| Tables_in_mytest |
+------------------+
| a                |
+------------------+
1 row in set (0.00 sec)

接下来就是跳过这条 SQL 的操作步骤

1 延迟库停止同步

stop slave;

2 找出执行了 drop table 语句的前一句的 pos 位置

[mysql@mysql-test ~]$ mysqlbinlog -vv /data/mysql_log/mysql_test/mysql-bin.000039 | grep -i -B 10 'drop table `b`';
...
# at 35134
#190819 11:40:25 server id 83  end_log_pos 35199 CRC32 0x02771167     Anonymous_GTID    last_committed=132    sequence_number=133    rbr_only=no
SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
# at 35199
#190819 11:40:25 server id 83  end_log_pos 35317 CRC32 0x50a018aa     Query    thread_id=37155    exec_time=0    error_code=0
use `mytest`/*!*/;
SET TIMESTAMP=1566186025/*!*/;
DROP TABLE `b` /* generated by server */

从结果中我们可以看到 drop 所在语句的前一句开始位置是 35134,所以我们同步到 35134 (这个可别选错了)

3 延迟库同步到要跳过的 SQL 前一条

change master to master_delay=0;
start slave until master_log_file='mysql-bin.000039',master_log_pos=35134;

查看状态看到已经同步到对应节点

chengqm-3307>>show slave status G;
...Master_Port: 3306Connect_Retry: 60Master_Log_File: mysql-bin.000039Read_Master_Log_Pos: 65792
...Slave_IO_Running: YesSlave_SQL_Running: NoExec_Master_Log_Pos: 35134
...Until_Log_File: mysql-bin.000039Until_Log_Pos: 35134

4 跳过一条 SQL 后开始同步

set global sql_slave_skip_counter=1;
start slave;

查看同步状态,删除表 b 的语句已经被跳过

chengqm-3307>>show slave status G;
...Slave_IO_Running: YesSlave_SQL_Running: Yes
...
1 row in set (0.00 sec)chengqm-3307>>show tables;
+------------------+
| Tables_in_mytest |
+------------------+
| a                |
| b                |
+------------------+
2 rows in set (0.00 sec)

4.2.2 开启 GTID

使用 GTID 跳过的步骤会简单很多,只要执行一条和要跳过的 SQL 的 GTID 相同的事务就可以跳过了

1.停止同步

2.找出执行了 drop table 语句的 GTID

3.执行这个 GTID 的事务

SET SESSION GTID_NEXT='对应的 GTID 值';
BEGIN; COMMIT;
SET SESSION GTID_NEXT = AUTOMATIC;

4.继续同步

5.闪回

闪回操作就是反向操作,比如执行了 delete from a where id=1,闪回就会执行对应的插入操作 insert into a (id,…) values(1,…),用于误操作数据,只对 DML 语句有效,且要求 binlog 格式设为 ROW。本章介绍两个比较好用的开源工具

5.1.binlog2sql

binlog2sql 是大众点评开源的一款用于解析 binlog 的工具,可以用于生成闪回语句,项目地址 binlog2sql

5.1.1.安装

wget https://github.com/danfengcao/binlog2sql/archive/master.zip -O binlog2sql.zip
unzip binlog2sql.zip
cd binlog2sql-master/# 安装依赖
pip install -r requirements.txt

5.1.2 生成回滚SQL

python binlog2sql/binlog2sql.py --flashback
-h<host> -P<port> -u<user> -p'<password>' -d<dbname> -t<table_name>
--start-file='<binlog_file>'
--start-datetime='<start_time>'
--stop-datetime='<stop_time>' > ./flashback.sqlpython binlog2sql/binlog2sql.py --flashback
-h<host> -P<port> -u<user> -p'<password>' -d<dbname> -t<table_name>
--start-file='<binlog_file>'
--start-position=<start_pos>
--stop-position=<stop_pos> > ./flashback.sql

5.2 MyFlash

MyFlash 是由美团点评公司技术工程部开发维护的一个回滚 DML 操作的工具,项目链接 MyFlash

限制:

  • binlog格式必须为row,且 binlog_row_image=full
  • 仅支持5.6与5.7
  • 只能回滚DML(增、删、改)

5.2.1 安装

# 依赖(centos)
yum install gcc*  pkg-config glib2 libgnomeui-devel -y# 下载文件
wget https://github.com/Meituan-Dianping/MyFlash/archive/master.zip -O MyFlash.zip
unzip MyFlash.zip
cd MyFlash-master# 编译安装
gcc -w  `pkg-config --cflags --libs glib-2.0` source/binlogParseGlib.c  -o binary/flashback
mv binary /usr/local/MyFlash
ln -s /usr/local/MyFlash/flashback /usr/bin/flashback

5.2.2 使用

生成回滚语句

flashback --databaseNames=<dbname>
--binlogFileNames=<binlog_file>
--start-position=<start_pos>
--stop-position=<stop_pos>

执行后会生成 binlog_output_base.flashback 文件,需要用 mysqlbinlog 解析出来再使用

mysqlbinlog -vv binlog_output_base.flashback | mysql -u<user> -p 


原作者:程淇铭
原文链接:删库不跑路-详解MySQL数据恢复
原出处:segmentfault思否

mysqldump全量恢复_删库不跑路-详解MySQL数据恢复相关推荐

  1. linux mysql恢复数据_删库不跑路详解MySQL数据恢复

    作者:程淇铭 出处:https://segmentfault.com/a/1190000020116271 日常工作中,总会有因手抖.写错条件.写错表名.错连生产库造成的误删库表和数据的事情发生,那么 ...

  2. 删库不跑路-详解MySQL备份策略

    原文链接:https://segmentfault.com/a/1190000019955399 手抖.写错条件.写错表名.错连生产库造成的误删库表和数据总有听说,那么删库之后除了跑路,还能做什么呢, ...

  3. mysqldump全量恢复_【MySQL】全量+增量的备份/恢复

    生产环境中,有时需要做MySQL的备份和恢复工作.因MySQL是在运行过程中的,做全量备份需要时间,全量备份完成后又有数据变动,此时需要增量备份辅助.如果想恢复数据到一个空库(例如数据迁移或者上云等更 ...

  4. editplus设置不生成备份文件_删库不跑路,手把手教你MySQL数据恢复

    你知道的越多,不知道的就越多,业余的像一棵小草! 你来,我们一起精进!你不来,我和你的竞争对手一起精进! 编辑:业余草 来源:segmentfault.com/a/1190000020116271 推 ...

  5. mysql 时间小于_删库不必跑路,自己动手MySQL数据恢复,真香~~

    背景 今天项目上需要对MySQL进行数据修复,通过比较各种方案和工具,准备使用binlog2sql工具进行"数据闪回",具体怎么使用呢,安排. MySQL数据库准备 以恢复某个库的 ...

  6. mysql高级-15-数据库备份与恢复(删库不跑路)

    mysql高级 前言 1.物理备份与逻辑备份 2.mysqldump实现逻辑备份 2.1 备份一个数据库 2.2 备份全部数据库 2.3 备份部分数据库 2.4 备份部分表 2.5 备份单表的部分数据 ...

  7. MySQL从删库到跑路(2):大爷的SQL私房菜

    大爷的SQL私房菜 夜色如墨,月凉如水,一轮皎洁的圆月高高地挂在夜空之上,平日里鼾声如雷的室友今夜也停止了打鼾,如此静谧的夜晚,李有为却辗转难眠. 时间悄然来到凌晨一点半,他已经在窗边站了53分钟23 ...

  8. Linux 下谨慎使用 rm,避免从删库到跑路的悲剧发生

    我们该如何再次避免删库"跑路"等事件的再次发生? 对此,在企业首先做好权限管理以及多重审核机制的同时,CSDN 也曾教诸多程序员们如何在 Linux 下谨慎使用 rm,避免从删库到 ...

  9. 手误【删库】 == 跑路,不存在的 ——删瓦辛格

    手误[删库] ==  跑路,不存在的  --删瓦辛格 前言 今天公司服务器的宝塔打不开,让我去修(ps:宝宝委屈) 打开找一下问题所在 问题: 发现是宝塔官方的cdn好像挂掉了 解决思路: (1)本地 ...

最新文章

  1. JAVA C++ 左花括号{该另起一行写还是写在行尾的思考
  2. SpringMVC+Hibernate+Junit4+json基本框架近乎0配置
  3. python easygui_EasyGUI是python的一个超级简单的GUI工具介绍(一)
  4. 【oracle】复合数据类型
  5. python使用方法-Python的使用方法
  6. Zookeeper集群搭建伪分布式
  7. Alpha 冲刺11——总结
  8. 【Servlet】总结 JSP的四大域对象、Servlet的四个作用域:pageContext、request、session、application
  9. 前端学习(2013)vue之电商管理系统电商系统之监听on-success事件
  10. Linux内核OOM机制的详细分析
  11. 【安卓的一个进程等级】
  12. Linux开发_调试与安全_gdb_peda简介
  13. 斯坦福大学深度学习公开课cs231n学习笔记(3)最优化方法:梯度下降
  14. loadrunner性能测试步骤_性能测试LoadRunner操作流程之一
  15. 富士施乐3065扫描教程_富士施乐打印机3065怎么连接电脑扫描
  16. Windows10邮件添加qq邮箱已过期问题
  17. ios设备的弹窗页面,光标错位,光标乱跳
  18. thinkpad x250装黑苹果教程_ThinkPad E450c 傻瓜式黑苹果一键安装教程
  19. 谈谈8583报文的使用及测试
  20. Xilinx FPGA高速串行收发器简介

热门文章

  1. 今日头条Web HTTP请求的白名单
  2. TCP socket和web socket的区别
  3. 如何给Docker hub用户上传头像
  4. Hyperledger Fabric on SAP Cloud Platform(SAP云平台上的超级账本简介)
  5. 使用JavaScript调用手机平台上的原生API
  6. mysql main函数_关于main()函数的小技巧
  7. 自定义报表 java_报表为什么会没完没了?怎么解决这个问题?
  8. java是解释型_Java 是编译型还是解释型?
  9. win7 asp虚拟服务器,win7怎么利用ASP获取服务器IP地址 win7利用ASP获取服务器IP地址教程...
  10. 小米台灯突然自己亮了_买了台灯,视力反而变差了?