{"id":18602821,"url":"https://github.com/obgnail/mysql-flashback","last_synced_at":"2025-05-16T18:12:16.529Z","repository":{"id":112904248,"uuid":"507318765","full_name":"obgnail/mysql-flashback","owner":"obgnail","description":"基于 binlog 的 mysql 闪回工具","archived":false,"fork":false,"pushed_at":"2022-06-28T16:25:58.000Z","size":19,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-02-18T01:41:57.832Z","etag":null,"topics":["binlog","binlog2sql","golang","mysql"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/obgnail.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-06-25T13:30:15.000Z","updated_at":"2023-12-28T14:37:29.000Z","dependencies_parsed_at":null,"dependency_job_id":"861f73c8-737e-465a-8140-20abaadd226c","html_url":"https://github.com/obgnail/mysql-flashback","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obgnail%2Fmysql-flashback","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obgnail%2Fmysql-flashback/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obgnail%2Fmysql-flashback/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obgnail%2Fmysql-flashback/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/obgnail","download_url":"https://codeload.github.com/obgnail/mysql-flashback/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254582909,"owners_count":22095519,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["binlog","binlog2sql","golang","mysql"],"created_at":"2024-11-07T02:12:38.012Z","updated_at":"2025-05-16T18:12:16.511Z","avatar_url":"https://github.com/obgnail.png","language":"Go","readme":"# mysql-flashback\n\n## 简介\n\n基于解析 binlog 的 Mysql 数据库闪回（flashback）工具。实现数据库的快速回滚。\n\n- 标准模式下，通过不同的参数生成某个范围内执行的 CUD 操作 SQL。\n- 回滚模式下，通过不同的参数生成某个范围内执行的 CUD 操作的回滚 SQL。\n\n利用 binlog 闪回，**binlog 格式必须将设置为 row**。\n\n\n\n## 版本\n\nMySQL 5.6+\n\n\n\n## 使用\n\n### 输出标准 SQL\n\n```bash\n./mysql-flashback -h=127.0.0.1 -P=3306 -u=root -p=root -d=es_river -start-file=\"/Users/XXX/volume/mysql/data/mysql-bin.000026\" -start-pos=259 -filter-tx=false -output=\"raw.sql\"\n```\n\n输出：\n\n```mysql\n/* BEGIN -\u003e Transaction BEGIN | binlog: mysql-bin.000026 | pos: (259, 335) | time: 2022-06-26 17:38:18 */\nINSERT INTO `es_river`.`user`(`uuid`, `name`, `name_pinyin`, `email`, `avatar`, `phone`, `password`, `status`, `create_time`, `modify_time`) VALUES ('NsktovQv', '123', '123', 'qwe@qwe.com', 'qwe', '123456789', '', 1, 1656236297995157000, 1656236297995157000); /* ROW -\u003e binlog: mysql-bin.000026 | pos: (259, 511) | time: 2022-06-26 17:38:18 */\n/* COMMIT -\u003e Transaction COMMIT | xid: 131 | binlog: mysql-bin.000026 | pos: (511, 542) | time: 2022-06-26 17:38:18 */\n\n/* BEGIN -\u003e Transaction BEGIN | binlog: mysql-bin.000026 | pos: (607, 683) | time: 2022-06-26 17:40:07 */\nINSERT INTO `es_river`.`user`(`uuid`, `name`, `name_pinyin`, `email`, `avatar`, `phone`, `password`, `status`, `create_time`, `modify_time`) VALUES ('GRXVSPx5', 'es_river', '123', 'qwe@qwe.com', 'qwe', '123456789', '', 1, 1656236407174364000, 1656236407174364000); /* ROW -\u003e binlog: mysql-bin.000026 | pos: (607, 864) | time: 2022-06-26 17:40:07 */\nINSERT INTO `es_river`.`user`(`uuid`, `name`, `name_pinyin`, `email`, `avatar`, `phone`, `password`, `status`, `create_time`, `modify_time`) VALUES ('HBfZ7bFD', 'es_river', '123', 'qwe@qwe.com', 'qwe', '123456789', '', 1, 1656236407174364000, 1656236407174364000); /* ROW -\u003e binlog: mysql-bin.000026 | pos: (607, 1045) | time: 2022-06-26 17:40:07 */\nINSERT INTO `es_river`.`user`(`uuid`, `name`, `name_pinyin`, `email`, `avatar`, `phone`, `password`, `status`, `create_time`, `modify_time`) VALUES ('RKQ7xete', 'es_river', '123', 'qwe@qwe.com', 'qwe', '123456789', '', 1, 1656236407174364000, 1656236407174364000); /* ROW -\u003e binlog: mysql-bin.000026 | pos: (607, 1226) | time: 2022-06-26 17:40:07 */\n/* COMMIT -\u003e Transaction COMMIT | xid: 148 | binlog: mysql-bin.000026 | pos: (1226, 1257) | time: 2022-06-26 17:40:07 */\n\n/* BEGIN -\u003e Transaction BEGIN | binlog: mysql-bin.000026 | pos: (1322, 1398) | time: 2022-06-26 17:44:42 */\nINSERT INTO `es_river`.`user`(`uuid`, `name`, `name_pinyin`, `email`, `avatar`, `phone`, `password`, `status`, `create_time`, `modify_time`) VALUES ('D5t6Ekij', 'es_river2', '123', 'qwe@qwe.com', 'qwe', '123456789', '', 1, 1656236682906976000, 1656236682906976000); /* ROW -\u003e binlog: mysql-bin.000026 | pos: (1322, 1580) | time: 2022-06-26 17:44:42 */\nINSERT INTO `es_river`.`user`(`uuid`, `name`, `name_pinyin`, `email`, `avatar`, `phone`, `password`, `status`, `create_time`, `modify_time`) VALUES ('YRNWxCYS', 'es_river2', '123', 'qwe@qwe.com', 'qwe', '123456789', '', 1, 1656236682906976000, 1656236682906976000); /* ROW -\u003e binlog: mysql-bin.000026 | pos: (1322, 1762) | time: 2022-06-26 17:44:42 */\nINSERT INTO `es_river`.`user`(`uuid`, `name`, `name_pinyin`, `email`, `avatar`, `phone`, `password`, `status`, `create_time`, `modify_time`) VALUES ('T3mrAzoi', 'es_river2', '123', 'qwe@qwe.com', 'qwe', '123456789', '', 1, 1656236682906976000, 1656236682906976000); /* ROW -\u003e binlog: mysql-bin.000026 | pos: (1322, 1944) | time: 2022-06-26 17:44:42 */\nUPDATE `es_river`.`user` SET `uuid`='NsktovQv', `name`='123', `name_pinyin`='123', `email`='qwe@qwe.com', `avatar`='qwe', `phone`='123456789', `password`='', `status`=0, `create_time`=1656236297995157000, `modify_time`=1656236297995157000 WHERE `uuid`='NsktovQv' AND `name`='123' AND `name_pinyin`='123' AND `email`='qwe@qwe.com' AND `avatar`='qwe' AND `phone`='123456789' AND `password`='' AND `status`=1 AND `create_time`=1656236297995157000 AND `modify_time`=1656236297995157000 LIMIT 1; /* ROW -\u003e binlog: mysql-bin.000026 | pos: (1322, 2187) | time: 2022-06-26 17:44:42 */\nUPDATE `es_river`.`user` SET `uuid`='GRXVSPx5', `name`='es_riverXXXXXXXX', `name_pinyin`='123', `email`='qwe@qwe.com', `avatar`='qwe', `phone`='123456789', `password`='', `status`=1, `create_time`=1656236407174364000, `modify_time`=1656236682906976000 WHERE `uuid`='GRXVSPx5' AND `name`='es_river' AND `name_pinyin`='123' AND `email`='qwe@qwe.com' AND `avatar`='qwe' AND `phone`='123456789' AND `password`='' AND `status`=1 AND `create_time`=1656236407174364000 AND `modify_time`=1656236407174364000 LIMIT 1; /* ROW -\u003e binlog: mysql-bin.000026 | pos: (1322, 2448) | time: 2022-06-26 17:44:42 */\n/* COMMIT -\u003e Transaction COMMIT | xid: 196 | binlog: mysql-bin.000026 | pos: (2448, 2479) | time: 2022-06-26 17:44:42 */\n\n/* BEGIN -\u003e Transaction BEGIN | binlog: mysql-bin.000026 | pos: (2544, 2620) | time: 2022-06-26 19:46:35 */\nDELETE FROM `es_river`.`user` WHERE `uuid`='YRNWxCYS' AND `name`='es_river2' AND `name_pinyin`='123' AND `email`='qwe@qwe.com' AND `avatar`='qwe' AND `phone`='123456789' AND `password`='' AND `status`=1 AND `create_time`=1656236682906976000 AND `modify_time`=1656236682906976000 LIMIT 1; /* ROW -\u003e binlog: mysql-bin.000026 | pos: (2544, 2802) | time: 2022-06-26 19:46:35 */\n/* COMMIT -\u003e Transaction COMMIT | xid: 574 | binlog: mysql-bin.000026 | pos: (2802, 2833) | time: 2022-06-26 19:46:35 */\n```\n\n\n\n### 输出回滚 SQL\n\n```bash\n./mysql-flashback -h=127.0.0.1 -P=3306 -u=root -p=root -d=es_river -start-file=\"/Users/XXX/volume/mysql/data/mysql-bin.000026\" -start-pos=259 -rollback -output=\"rollback.sql\"\n```\n\n```mysql\nINSERT INTO `es_river`.`user`(`uuid`, `name`, `name_pinyin`, `email`, `avatar`, `phone`, `password`, `status`, `create_time`, `modify_time`) VALUES ('YRNWxCYS', 'es_river2', '123', 'qwe@qwe.com', 'qwe', '123456789', '', 1, 1656236682906976000, 1656236682906976000); /* ROW -\u003e binlog: mysql-bin.000026 | pos: (2544, 2802) | time: 2022-06-26 19:46:35 */\nUPDATE `es_river`.`user` SET `uuid`='GRXVSPx5', `name`='es_river', `name_pinyin`='123', `email`='qwe@qwe.com', `avatar`='qwe', `phone`='123456789', `password`='', `status`=1, `create_time`=1656236407174364000, `modify_time`=1656236407174364000 WHERE `uuid`='GRXVSPx5' AND `name`='es_riverXXXXXXXX' AND `name_pinyin`='123' AND `email`='qwe@qwe.com' AND `avatar`='qwe' AND `phone`='123456789' AND `password`='' AND `status`=1 AND `create_time`=1656236407174364000 AND `modify_time`=1656236682906976000 LIMIT 1; /* ROW -\u003e binlog: mysql-bin.000026 | pos: (1322, 2448) | time: 2022-06-26 17:44:42 */\nUPDATE `es_river`.`user` SET `uuid`='NsktovQv', `name`='123', `name_pinyin`='123', `email`='qwe@qwe.com', `avatar`='qwe', `phone`='123456789', `password`='', `status`=1, `create_time`=1656236297995157000, `modify_time`=1656236297995157000 WHERE `uuid`='NsktovQv' AND `name`='123' AND `name_pinyin`='123' AND `email`='qwe@qwe.com' AND `avatar`='qwe' AND `phone`='123456789' AND `password`='' AND `status`=0 AND `create_time`=1656236297995157000 AND `modify_time`=1656236297995157000 LIMIT 1; /* ROW -\u003e binlog: mysql-bin.000026 | pos: (1322, 2187) | time: 2022-06-26 17:44:42 */\nDELETE FROM `es_river`.`user` WHERE `uuid`='T3mrAzoi' AND `name`='es_river2' AND `name_pinyin`='123' AND `email`='qwe@qwe.com' AND `avatar`='qwe' AND `phone`='123456789' AND `password`='' AND `status`=1 AND `create_time`=1656236682906976000 AND `modify_time`=1656236682906976000 LIMIT 1; /* ROW -\u003e binlog: mysql-bin.000026 | pos: (1322, 1944) | time: 2022-06-26 17:44:42 */\nDELETE FROM `es_river`.`user` WHERE `uuid`='YRNWxCYS' AND `name`='es_river2' AND `name_pinyin`='123' AND `email`='qwe@qwe.com' AND `avatar`='qwe' AND `phone`='123456789' AND `password`='' AND `status`=1 AND `create_time`=1656236682906976000 AND `modify_time`=1656236682906976000 LIMIT 1; /* ROW -\u003e binlog: mysql-bin.000026 | pos: (1322, 1762) | time: 2022-06-26 17:44:42 */\nDELETE FROM `es_river`.`user` WHERE `uuid`='D5t6Ekij' AND `name`='es_river2' AND `name_pinyin`='123' AND `email`='qwe@qwe.com' AND `avatar`='qwe' AND `phone`='123456789' AND `password`='' AND `status`=1 AND `create_time`=1656236682906976000 AND `modify_time`=1656236682906976000 LIMIT 1; /* ROW -\u003e binlog: mysql-bin.000026 | pos: (1322, 1580) | time: 2022-06-26 17:44:42 */\nDELETE FROM `es_river`.`user` WHERE `uuid`='RKQ7xete' AND `name`='es_river' AND `name_pinyin`='123' AND `email`='qwe@qwe.com' AND `avatar`='qwe' AND `phone`='123456789' AND `password`='' AND `status`=1 AND `create_time`=1656236407174364000 AND `modify_time`=1656236407174364000 LIMIT 1; /* ROW -\u003e binlog: mysql-bin.000026 | pos: (607, 1226) | time: 2022-06-26 17:40:07 */\nDELETE FROM `es_river`.`user` WHERE `uuid`='HBfZ7bFD' AND `name`='es_river' AND `name_pinyin`='123' AND `email`='qwe@qwe.com' AND `avatar`='qwe' AND `phone`='123456789' AND `password`='' AND `status`=1 AND `create_time`=1656236407174364000 AND `modify_time`=1656236407174364000 LIMIT 1; /* ROW -\u003e binlog: mysql-bin.000026 | pos: (607, 1045) | time: 2022-06-26 17:40:07 */\nDELETE FROM `es_river`.`user` WHERE `uuid`='GRXVSPx5' AND `name`='es_river' AND `name_pinyin`='123' AND `email`='qwe@qwe.com' AND `avatar`='qwe' AND `phone`='123456789' AND `password`='' AND `status`=1 AND `create_time`=1656236407174364000 AND `modify_time`=1656236407174364000 LIMIT 1; /* ROW -\u003e binlog: mysql-bin.000026 | pos: (607, 864) | time: 2022-06-26 17:40:07 */\n```\n\n\n\n## 参数\n\n### Mysql 连接参数\n\n- `h`：mysql host\n- `P`：mysql port\n- `u`：mysql user\n- `p`：mysql password\n\n### binlog 筛选参数\n\n- `d`：只解析目标 db 的 sql，必填。\n- `t`：只解析目标 table 的 sql，使用英文逗号隔开。为空则解析全部 table。\n- `start-file`：起始解析文件。必填。\n- `start-pos`：起始解析位置。为空则从 0 开始。\n- `start-time`：起始解析时间，格式'%Y-%m-%d %H:%M:%S'。为空则不过滤。\n- `stop-file`：终止解析文件。为空则解析到最新数据。\n- `stop-pos`：终止解析时间，格式'%Y-%m-%d %H:%M:%S'。为空则不过滤。\n- `stop-time`：中止始解析时间，格式'%Y-%m-%d %H:%M:%S'。为空则不过滤\n\n### event 筛选参数\n\n- `gtid-regexp`：若启用 GTID MODE，可用正则过滤，为空则不过滤。\n- `only-sql-type`：解析指定类型，支持 INSERT, UPDATE, DELETE。使用英文逗号隔开。为空则不过滤。\n- `only-DML`：只解析 dml，忽略 ddl。在 rollback 参数启用时，自动关闭。\n- `filter-tx`：生成的标准 SQL 说明其所在的事务。在 rollback 参数启用时，自动关闭。默认为 true。\n\n### 解析模式参数\n\n- `rollback`：为 false 则输出标准 SQL，为 true 则生成 flashback 文件。默认为 false。\n\n### 其他参数\n\n- `output`：输出文件。默认为 stdout，即标准输出流。\n\n\n\n## 回滚原理\n\n首先解析 binlog，得到所有的 CUD event。根据这些 event 组装成标准 SQL。接着根据标准 SQL 生成回滚 SQL。\n\n对于 delete 操作，生成的回滚语句是 insert。\n\n```mysql\n# raw\nDELETE FROM `es_river`.`user` WHERE `uuid`='YRNWxCYS' AND `name`='es_river2' AND `name_pinyin`='123' AND `email`='qwe@qwe.com' AND `avatar`='qwe' AND `phone`='123456789' AND `password`='' AND `status`=1 AND `create_time`=1656236682906976000 AND `modify_time`=1656236682906976000 LIMIT 1;\n\n# rollback\nINSERT INTO `es_river`.`user`(`uuid`, `name`, `name_pinyin`, `email`, `avatar`, `phone`, `password`, `status`, `create_time`, `modify_time`) VALUES ('YRNWxCYS', 'es_river2', '123', 'qwe@qwe.com', 'qwe', '123456789', '', 1, 1656236682906976000, 1656236682906976000);\n```\n\n对于 insert 操作，生成的回滚语句是 delete。\n\n```mysql\n# raw\nINSERT INTO `es_river`.`user`(`uuid`, `name`, `name_pinyin`, `email`, `avatar`, `phone`, `password`, `status`, `create_time`, `modify_time`) VALUES ('NsktovQv', '123', '123', 'qwe@qwe.com', 'qwe', '123456789', '', 1, 1656236297995157000, 1656236297995157000);\n\n# rollback\nDELETE FROM `es_river`.`user` WHERE `uuid`='GRXVSPx5' AND `name`='es_river' AND `name_pinyin`='123' AND `email`='qwe@qwe.com' AND `avatar`='qwe' AND `phone`='123456789' AND `password`='' AND `status`=1 AND `create_time`=1656236407174364000 AND `modify_time`=1656236407174364000 LIMIT 1;\n```\n\n对于 update 操作，生成的回滚语句交换 set 和 where 的值。\n\n```mysql\n# raw\nUPDATE `es_river`. `user` SET `uuid`='GRXVSPx5', `name`='es_riverXXXXXXXX', `name_pinyin`='123', `email`='qwe@qwe.com', `avatar`='qwe', `phone`='123456789', `password`='', `status`=1, `create_time`=1656236407174364000, `modify_time`=1656236682906976000 WHERE `uuid`='GRXVSPx5' AND `name`='es_river' AND `name_pinyin`='123' AND `email`='qwe@qwe.com' AND `avatar`='qwe' AND `phone`='123456789' AND `password`='' AND `status`=1 AND `create_time`=1656236407174364000 AND `modify_time`=1656236407174364000 LIMIT 1;\n\n# rollback\nUPDATE `es_river`.`user` SET `uuid`='GRXVSPx5', `name`='es_river', `name_pinyin`='123', `email`='qwe@qwe.com', `avatar`='qwe', `phone`='123456789', `password`='', `status`=1, `create_time`=1656236407174364000, `modify_time`=1656236407174364000 WHERE `uuid`='GRXVSPx5' AND `name`='es_riverXXXXXXXX' AND `name_pinyin`='123' AND `email`='qwe@qwe.com' AND `avatar`='qwe' AND `phone`='123456789' AND `password`='' AND `status`=1 AND `create_time`=1656236407174364000 AND `modify_time`=1656236682906976000 LIMIT 1;\n```\n\n最后，只需要将所有的 SQL 语句，倒序输出即可。\n\n\n\n## 其他\n\n- 此工具基于 binlog，而 TABLE_MAP_EVENT 是没有存储 Table Field Name 的，且无法得知该 db 下的所有 Table。因此必须去数据库查。这就是需要连接数据库的原因。\n- binlog 对于 ddl 的记录并不完全。对于 drop table，create index 之类的语句在 binlog 找不到完整的数据。如果你不小心删库了，那还是赶紧跑路吧。\n- 因为生成的回滚 SQL 是根据标准 SQL 处理后的倒序输出，所以如果 output 参数使用 stdout，一样会生成一个中间文件，其格式为 fmt.Sprintf(\"rollback_%d.sql\", time.Now().Unix())。\n- 若执行 rollback SQL，一样也会生成 binlog event，所以理论上你可以使用 flashback 去 flashback 自己 :)\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fobgnail%2Fmysql-flashback","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fobgnail%2Fmysql-flashback","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fobgnail%2Fmysql-flashback/lists"}