翼度科技»论坛 编程开发 mysql 查看内容

工具分享丨数据闪回工具MyFlash

4

主题

4

帖子

12

积分

新手上路

Rank: 1

积分
12
工具分享丨数据闪回工具MyFlash

在当今数字化的时代,数据已经成为了企业和个人最宝贵的资产之一。数据不仅仅是简单的信息集合,它更是决策的依据、业务的支撑以及创新的源泉。
数据丢失是一种极其危险且令人头疼的情况。想象一下,企业因系统故障、人为误操作或遭受恶意攻击而丢失了关键的业务数据,这可能导致业务中断、客户流失,甚至面临法律风险和声誉损害。
在这样的背景下,有效的数据管理和保护工具就显得尤为重要。在之前的社区文章中,有介绍过一款闪回工具binlog2sql但今天介绍的闪回工具相对已有的回滚工具,其增加了更多的过滤选项,性能优于 binlog2sql、mysqlbinlog
除使用工具还可以手动恢复数据,详见推文:
MyFlash 工具介绍

MyFlash 是由美团点评公司技术工程部开发维护的一个回滚DML操作的工具。该工具通过解析 v4 版本的 binlog,完成回滚操作。相对已有的回滚工具,其增加了更多的过滤选项,让回滚更加容易。

  • 可以针对实例、数据库、表及指定的DML语句进行回滚
  • 如果binlog日志保留,可以闪回到任意时间
该工具开源地址为:https://github.com/Meituan-Dianping/MyFlash
限制


  • binlog格式必须为row,且binlog_row_image=full
  • 仅支持 MySQL5.6 与 MySQL5.7
  • 只能回滚DML(增、删、改)
介绍说只支持 5.6 与 5.7 版本,但在 GreatSQL 8.0.32-26 版本测试中也可用
安装方法

拉取工具仓库,并编译安装
  1. $ git clone https://github.com/Meituan-Dianping/MyFlash.git
复制代码
动态编译链接
  1. $ gcc -w  `pkg-config --cflags --libs glib-2.0` source/binlogParseGlib.c  -o binary/flashback
复制代码
若不想每次去重新编译,可以选择使用静态链接,但是该方法需要知道glib库的版本和位置,因此对于每台机器略有不同,请谨慎使用
静态编译链接
  1. $ gcc -w -g `pkg-config --cflags  glib-2.0` source/binlogParseGlib.c   -o binary/flashback /usr/lib64/libglib-2.0.a -lrt
复制代码
编译完成后,在binary目录下有可执行文件flashback
参数介绍
  1. $ ./binary/flashback -help
  2. Usage:
  3.   flashback [OPTION?]
  4. Help Options:
  5.   -h, --help                  Show help options
  6. Application Options:
  7.   --databaseNames             databaseName to apply. if multiple, seperate by comma(,)
  8.   --tableNames                tableName to apply. if multiple, seperate by comma(,)
  9.   --tableNames-file           tableName to apply. if multiple, seperate by comma(,)
  10.   --start-position            start position
  11.   --stop-position             stop position
  12.   --start-datetime            start time (format %Y-%m-%d %H:%M:%S)
  13.   --stop-datetime             stop time (format %Y-%m-%d %H:%M:%S)
  14.   --sqlTypes                  sql type to filter . support INSERT, UPDATE ,DELETE. if multiple, seperate by comma(,)
  15.   --maxSplitSize              max file size after split, the uint is M
  16.   --binlogFileNames           binlog files to process. if multiple, seperate by comma(,)  
  17.   --outBinlogFileNameBase     output binlog file name base
  18.   --logLevel                  log level, available option is debug,warning,error
  19.   --include-gtids             gtids to process. if multiple, seperate by comma(,)
  20.   --include-gtids-file        gtids to process. if multiple, seperate by comma(,)
  21.   --exclude-gtids             gtids to skip. if multiple, seperate by comma(,)
  22.   --exclude-gtids-file        gtids to skip. if multiple, seperate by comma(,)
复制代码

  • databaseNames:指定需要回滚的数据库名。多个数据库可以用“,”隔开。如果不指定该参数,相当于指定了所有数据库。
  • tableNames:指定需要回滚的表名。多个表可以用“,”隔开。如果不指定该参数,相当于指定了所有表。
  • start-position:指定回滚开始的位置。如不指定,从文件的开始处回滚。请指定正确的有效的位置,否则无法回滚。
  • stop-position:指定回滚结束的位置。如不指定,回滚到文件结尾。请指定正确的有效的位置,否则无法回滚。
  • start-datetime:指定回滚的开始时间。注意格式必须是 %Y-%m-%d %H:%M:%S。如不指定,则不限定时间。
  • stop-datetime:指定回滚的结束时间。注意格式必须是 %Y-%m-%d %H:%M:%S。如不指定,则不限定时间。
  • sqlTypes:指定需回滚的SQL类型。目前支持的过滤类型是INSERT, UPDATE ,DELETE。多个类型可以用“,”隔开。
  • maxSplitSize:一旦指定该参数,对文件进行固定尺寸的分割(单位为M),过滤条件有效,但不进行回滚操作。该参数主要用来将大的binlog文件切割,防止单次应用的binlog尺寸过大,对线上造成压力。
  • binlogFileNames:指定需要回滚的binlog文件,目前只支持单个文件,后续会增加多个文件支持。经测试已支持多个binlog文件。
  • outBinlogFileNameBase:指定输出的binlog文件前缀,如不指定,则默认为binlog_output_base.flashback。
  • logLevel:仅供开发者使用,默认级别为error级别。在生产环境中不要修改这个级别,否则输出过多。
  • include-gtids:指定需要回滚的gtid,支持gtid的单个和范围两种形式。
  • exclude-gtids:指定不需要回滚的gtid,用法同include-gtids。
最佳实践

此次测试环境情况如下:

  • 数据库:GreatSQL 8.0.32-26
  • 操作系统:Linux debian 6.1.0-22-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.94-1 (2024-06-21) x86_64 GNU/Linux
创建一个test库和students表,并插入 5 条数据
  1. greatsql> CREATE TABLE students (
  2.     id INT AUTO_INCREMENT PRIMARY KEY,
  3.     first_name VARCHAR(50),
  4.     last_name VARCHAR(50),
  5.     age INT,
  6.     grade VARCHAR(10),
  7.     city VARCHAR(50)
  8. );
  9. greatsql> INSERT INTO students (first_name, last_name, age, grade, city) VALUES
  10. ('Alice', 'Smith', 18, 'Grade 12', 'New York'),
  11. ('Bob', 'Johnson', 17, 'Grade 11', 'Los Angeles'),
  12. ('Charlie', 'Williams', 16, 'Grade 10', 'Chicago'),
  13. ('David', 'Brown', 15, 'Grade 9', 'Houston'),
  14. ('Eve', 'Davis', 17, 'Grade 11', 'Miami');
复制代码
模拟误操作将 age 列全部修改为0
  1. greatsql> UPDATE students SET age = 0;
  2. greatsql> SELECT * FROM students;
  3. +----+------------+-----------+------+----------+-------------+
  4. | id | first_name | last_name | age  | grade    | city        |
  5. +----+------------+-----------+------+----------+-------------+
  6. |  1 | Alice      | Smith     |    0 | Grade 12 | New York    |
  7. |  2 | Bob        | Johnson   |    0 | Grade 11 | Los Angeles |
  8. |  3 | Charlie    | Williams  |    0 | Grade 10 | Chicago     |
  9. |  4 | David      | Brown     |    0 | Grade 9  | Houston     |
  10. |  5 | Eve        | Davis     |    0 | Grade 11 | Miami       |
  11. +----+------------+-----------+------+----------+-------------+
  12. 5 rows in set (0.00 sec)
复制代码
记录误操作时间,并使用FLUSH LOGS换一个 Binary Log 记录,查看 Binary Log 文件名
  1. greatsql> SELECT current_timestamp();
  2. +---------------------+
  3. | current_timestamp() |
  4. +---------------------+
  5. | 2024-08-08 14:41:05 |
  6. +---------------------+
  7. greatsql> FLUSH LOGS;
  8. Query OK, 0 rows affected (0.06 sec)
  9. greatsql> SHOW BINARY LOGS;
  10. +---------------+-----------+-----------+
  11. | Log_name      | File_size | Encrypted |
  12. +---------------+-----------+-----------+
  13. | binlog.000006 |      2146 | No        |
  14. | binlog.000007 |       197 | No        |
  15. +---------------+-----------+-----------+
复制代码
要用闪回需要注意闪回的时间点,例如这次的需求是,闪回到 INSERT INTO 5条语句后
  1. $ mysqlbinlog --no-defaults -v binlog.000006
  2. # 发生误操作的时间`14:33:56`及开始位置`1359`和结束位置`2102`
复制代码
MyFlash 解析闪回文件
  1. $ ./flashback --databaseNames=test --tableNames=students --start-position='1359' --stop-position='2102' --binlogFileNames=/data/greatsql/binlog.000006 --outBinlogFileNameBase=students
复制代码
如果确定误操作类型也可以加上--sqlTypes=UPDATE/INSERT/DELETE(多类型用','隔开)
生成文件 students.flashback 并查看
  1. $ mysqlbinlog --no-defaults --base64-output=decode-rows -v students.flashback
  2. # 可以看到文件中将 UPDATE 语句进行回滚
  3. # ... 节选 ...
  4. ### UPDATE `test`.`students`
  5. ### WHERE
  6. ###   @1=1
  7. ###   @2='Alice'
  8. ###   @3='Smith'
  9. ###   @4=0
  10. ###   @5='Grade 12'
  11. ###   @6='New York'
  12. ### SET
  13. ###   @1=1
  14. ###   @2='Alice'
  15. ###   @3='Smith'
  16. ###   @4=18
  17. ###   @5='Grade 12'
  18. ###   @6='New York'
复制代码
执行闪回恢复
  1. $ mysqlbinlog --no-defaults --skip-gtids students.flashback |mysql -uroot -p
复制代码
再次查看数据,已经恢复
  1. greatsql> SELECT * FROM students;
  2. +----+------------+-----------+------+----------+-------------+
  3. | id | first_name | last_name | age  | grade    | city        |
  4. +----+------------+-----------+------+----------+-------------+
  5. |  1 | Alice      | Smith     |   18 | Grade 12 | New York    |
  6. |  2 | Bob        | Johnson   |   17 | Grade 11 | Los Angeles |
  7. |  3 | Charlie    | Williams  |   16 | Grade 10 | Chicago     |
  8. |  4 | David      | Brown     |   15 | Grade 9  | Houston     |
  9. |  5 | Eve        | Davis     |   17 | Grade 11 | Miami       |
  10. +----+------------+-----------+------+----------+-------------+
  11. 5 rows in set (0.00 sec)
复制代码
回滚该文件中指定语句
回滚该文件中所有 INSTER 语句,UPDATE/DELETE语句也同理
  1. $ ./flashback  --sqlTypes='INSERT' --binlogFileNames=binlog.000006
  2. mysqlbinlog students.flashback | mysql -h<host> -u<user> -p
复制代码
回滚大文件
该工具中有maxSplitSize参数,可用于切割大文件
  1. # 回滚
  2. $ ./flashback --binlogFileNames=binlog.000006
  3. # 切割大文件
  4. $ ./flashback --maxSplitSize=1 --binlogFileNames=students.flashback
  5. # 应用
  6. $ mysqlbinlog students.flashback.000001 | mysql -h<host> -u<user> -p
  7. ...
  8. $ mysqlbinlog students.flashback.<N> | mysql -h<host> -u<user> -p
复制代码
性能测试

和社区上次推荐的 binlog2sql 测试下性能,看哪个恢复速度快
创建 orders 表
  1. CREATE TABLE `orders` (
  2.   `order_id` int NOT NULL AUTO_INCREMENT,
  3.   `customer_id` int NOT NULL,
  4.   `product_id` int NOT NULL,
  5.   `order_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  6.   `order_status` char(10) NOT NULL DEFAULT 'pending',
  7.   `quantity` int NOT NULL,
  8.   `order_amount` decimal(10,2) NOT NULL,
  9.   `shipping_address` varchar(255) NOT NULL,
  10.   `billing_address` varchar(255) NOT NULL,
  11.   `order_notes` varchar(255) DEFAULT NULL,
  12.   PRIMARY KEY (`order_id`),
  13.   KEY `idx_customer_id` (`customer_id`),
  14.   KEY `idx_product_id` (`product_id`),
  15.   KEY `idx_order_date` (`order_date`),
  16.   KEY `idx_order_status` (`order_status`)
  17. ) ENGINE=InnoDB AUTO_INCREMENT=100001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
复制代码
创建 Shell 脚本,并插入 110504 条数据
  1. #!/bin/bash
  2. # 插入的订单数量
  3. num_orders=110504  # 你可以根据需要更改插入的数量
  4. # 循环插入数据
  5. for ((i=1; i<=$num_orders; i++))
  6. do
  7.     customer_id=$((RANDOM % 1000 + 1))        # 随机生成1到1000的顾客ID
  8.     product_id=$((RANDOM % 100 + 1))          # 随机生成1到100的产品ID
  9.     order_date=$(date +"%Y-%m-%d %H:%M:%S")    # 当前时间作为订单日期
  10.     order_status="pending"                    # 默认订单状态为pending
  11.     quantity=$((RANDOM % 5 + 1))              # 随机生成1到5的数量
  12.     order_amount=$(echo "scale=2; $((RANDOM % 500 + 50)) + $((RANDOM % 99)) / 100.0" | bc)  # 随机生成50到550之间的订单金额,保留两位小数
  13.     shipping_address="Address $i, City $((RANDOM % 10 + 1)), Country"  # 构造随机的配送地址
  14.     billing_address="Billing Address $i, City $((RANDOM % 10 + 1)), Country"  # 构造随机的账单地址
  15.     order_notes="Order notes for order $i"    # 每个订单有不同的订单备注
  16.     # 构造SQL插入语句
  17.     insert_query="INSERT INTO orders (customer_id, product_id, order_date, order_status, quantity, order_amount, shipping_address, billing_address, order_notes) VALUES ($customer_id, $product_id, '$order_date', '$order_status', $quantity, $order_amount, '$shipping_address', '$billing_address', '$order_notes');"
  18.     # 执行插入语句
  19.     mysql -uroot -p test -e "$insert_query"
  20. done
  21. echo "$num_orders 条订单数据插入完成。"
复制代码
MyFlash 测试

模拟误操作删除数据
  1. # 执行脚本
  2. $ bash insert_students.sh
  3. greatsql> SELECT COUNT(*) FROM orders;  
  4. +----------+
  5. | count(*) |
  6. +----------+
  7. |   110504 |
  8. +----------+
  9. 1 row in set (0.01 sec)
复制代码
查看 Binary Log 日志,找到误操作位置
  1. # 误操作删除数据
  2. greatsql> DELETE FROM orders WHERE 1=1;
  3. Query OK, 110504 rows affected (1.59 sec)
  4. # 记录误操作时间
  5. greatsql> SELECT current_timestamp();
  6. +---------------------+
  7. | current_timestamp() |
  8. +---------------------+
  9. | 2024-08-08 17:51:10 |
  10. +---------------------+
  11. 1 row in set (0.00 sec)
  12. # 启用新 binlog 日志
  13. greatsql> FLUSH LOGS;
  14. Query OK, 0 rows affected (0.06 sec)
  15. # 查看旧 binlog 日志
  16. greatsql> SHOW BINARY LOGS;
  17. +---------------+-----------+-----------+
  18. | Log_name      | File_size | Encrypted |
  19. +---------------+-----------+-----------+
  20. | binlog.000008 | 101319050 | No        |
  21. | binlog.000009 |       197 | No        |
  22. +---------------+-----------+-----------+
  23. 2 rows in set (0.01 sec)
复制代码
使用 MyFlash 生成恢复的 Binary Log
  1. $ mysqlbinlog --no-defaults -v binlog.000008
  2. # 开始位置 86092119 时间 17:50:52  结束 101319006
复制代码
查看生成的文件,并使用 mysqlbinlog 回滚恢复
  1. $ time ./flashback --databaseNames=test --tableNames=orders --start-position='86092119' --stop-position='101319006' --binlogFileNames=/data/greatsql/binlog.000008 --outBinlogFileNameBase=orders
  2. real    0m0.138s
  3. user    0m0.029s
  4. sys     0m0.065s
复制代码
查看数据恢复情况
  1. $ ls -l
  2. -rwxr-xr-x 1 root root   957744 Aug  5 11:26 flashback
  3. -rw-r--r-- 1 root root 15430854 Aug  9 10:52 orders.flashback
  4. 闪回恢复
  5. $ mysqlbinlog --no-defaults --skip-gtids orders.flashback |mysql -uroot -p
  6. real    0m9.414s
  7. user    0m0.433s
  8. sys     0m0.082s
复制代码
用切割的方式导入
  1. greatsql> SELECT COUNT(*) FROM orders;  
  2. +----------+
  3. | count(*) |
  4. +----------+
  5. |   110504 |
  6. +----------+
  7. 1 row in set (0.01 sec)
复制代码
切割后的文件名无法修改,只能叫 binlog_output_base
如果切割的文件多了,需要写个小脚本导入这些切割后的文件
  1. # 每个文件设定大小为 1M (可按需求自定义)
  2. $ ./flashback --maxSplitSize=1 --binlogFileNames=orders.flashback
  3. # 此时就会多出很多 binlog_output_base 文件
  4. -rw-r--r-- 1 root root 1.1M Aug  9 13:55 binlog_output_base.000001
  5. ... 省略 ...
  6. -rw-r--r-- 1 root root 640K Aug  9 13:55 binlog_output_base.000015
复制代码
运行 batch_rollback.sh导入
  1. $ vim batch_rollback.sh
  2. # 循环导入每个文件
  3. for i in $(seq -w 1 15); do
  4.     FILENAME="binlog_output_base.0000$i"
  5.     mysqlbinlog --no-defaults --skip-gtids $FILENAME | mysql -uroot -p
  6. done
复制代码
根据 MyFlash 官方的测试结果和 mysqlbinlog、binlog2sql 两款工具对比,恢复100万条数据

  • MyFlash 耗时 2.774s
  • mysqlbinlog 耗时 6.217s
  • binlog2sql 耗时 581.319s
总结


  • MyFlash 使用较方便,回滚耗时短,大数据量恢复可以使用该工具
  • binlog2sql 需要 Python 环境,可能会造成字符问题,MyFlash 比较稳定

Enjoy GreatSQL
来源:https://www.cnblogs.com/greatsql/p/18501877
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具