TiDB 利用binlog 恢复-反解析binlog

我们知道TiDB的binlog记录了所有已经执行成功的dml语句,类似mysql binlog row模式

,TiDB官方也提供了reparo可以进行解析binlog,如下所示:

[2024/04/26 20:58:02.136 +08:00] [INFO] [config.go:153] ["Parsed start TSO"] [ts=449217508147200000]
[2024/04/26 20:58:02.136 +08:00] [INFO] [config.go:160] ["Parsed stop TSO"] [ts=449222855884800000]
schema: coupon_trade; table: coupon_trade_record; type: Insert
coupon_id(varchar): 1
merchant_id(varchar): 2
coupon_code(varchar): 4
customer_id(varchar): 4
customer_mobile_no(varchar): 5
trade_channel(varchar): 03
trade_store_id(varchar): 1440040
trade_store_name(varchar): <nil>
bill_type(tinyint): 0
bill_code(varchar): 6
external_order_id(varchar): <nil>
receive_channel_code(varchar): 105
coupon_card_type(tinyint): 0
coupon_type(tinyint): 1
receive_type(smallint): 6
receive_src_code(varchar): <nil>
process_type(tinyint): 2
order_food_type(varchar): 1
order_mode(varchar): 0
is_give_away(tinyint): 0
is_available(tinyint): 1
is_deleted(bigint): 0
updated_date(datetime): 2024-04-21 00:00:00
created_date(datetime): 2024-04-21 00:00:00
updated_user(varchar): default
created_user(varchar): default
version(int): 1
schema: coupon_trade; table: coupon_trade_record; type: Insert
coupon_id(varchar): 2
merchant_id(varchar): <nil>
coupon_code(varchar): 5
customer_id(varchar): 6
customer_mobile_no(varchar): 7
trade_channel(varchar): 03
trade_store_id(varchar): 1420452
trade_store_name(varchar): <nil>
bill_type(tinyint): 0
bill_code(varchar): 8
external_order_id(varchar): <nil>
receive_channel_code(varchar): 03
coupon_card_type(tinyint): 0
coupon_type(tinyint): 0
receive_type(smallint): 4
receive_src_code(varchar): <nil>
process_type(tinyint): 2
order_food_type(varchar): 1
order_mode(varchar): 0
is_give_away(tinyint): 0
is_available(tinyint): 1
is_deleted(bigint): 0
updated_date(datetime): 2024-04-21 00:00:00
created_date(datetime): 2024-04-21 00:00:00
updated_user(varchar): default
created_user(varchar): default
version(int): 1

另外reparo 支持print 和mysql 两种模式,print只做解析打印到标准输出,不执行 SQL,mysql:是直接再下游数据库执行SQL

但是我们有的时候需要进去反解析,比如我们误删除了一些数据,我们要把误删除的数据解析成INSERT语句,这怎么办呢,TIDB目前不提供这种反解析工具,于是自己写了一个工具进行解析,代码如下:

package mainimport ("bufio""encoding/json""flag""fmt""log""os""regexp""strings""time"
)var (binlogFile stringschema     stringtable      stringsqlType    stringwhereSql   stringlogPath    stringfiledRe    = regexp.MustCompile(`\(.*?\)+:.*`)logOutFile *log.Logger
)type filed struct {Name  string `json:"name"`Value string `json:"value"`
}
type Parser struct {lines []filed
}//	type lists struct {
//		filed []filed
//	}
func compressStr(str string) string {str = strings.ReplaceAll(str, " ", "")r := strings.NewReplacer("\r", "", "\n", "")str = r.Replace(str)//匹配一个或多个空白符的正则表达式reg := regexp.MustCompile("\\s+")return reg.ReplaceAllString(str, "")
}func main() {flag.StringVar(&binlogFile, "binlogFile", "", "binlog日志文件路径")flag.StringVar(&schema, "schema", "", "要解析的数据库名称")flag.StringVar(&table, "table", "", "要解析的表名称")flag.StringVar(&sqlType, "sqlType", "", "要解析的dml类型")flag.StringVar(&logPath, "logPath", "", "输出文件路径名称")flag.Parse()if binlogFile == "" {log.Println("请输入binlog日志文件路径...")return}if schema == "" {log.Println("请输入要解析的数据库名称...")return}if table == "" {log.Println("请输入要解析的表名称...")return}if sqlType == "" {log.Println("请输入要解析的dml类型...")return}if logPath == "" {log.Println("请输入输出日志文件路径...")return}outSlowLogFile(logPath)file, err := os.Open(binlogFile)if err != nil {log.Println("读取文件失败...")return}schemaRe := fmt.Sprintf("schema:%v;table:%v;type:%v", schema, table, sqlType)//schemaRe2 := fmt.Sprintf("schema:%v;table:%v;type:%v", schema, table, "Insert")defer file.Close()scanner := bufio.NewScanner(file)scanner.Buffer(make([]byte, 1024*1024), 1024*1024*10)inHeader := falsevar f filedvar array []stringvar time2, _ = time.Parse("2006-01-02 15:04:05", "2024-04-01 00:00:00")fields := ""for scanner.Scan() {line := scanner.Text()if compressStr(line) == compressStr(schemaRe) {if fields != "" {fields = fmt.Sprintf("{%v}", fields)array = append(array, fields)}fields = ""inHeader = truecontinue} else if strings.Contains(line, "sync binlog success") || strings.Contains(line, "read file end") {inHeader = falsecontinue} else {if inHeader {if filedRe.MatchString(line) {f.Name = strings.Split(line, "(")[0]tmpValue := strings.Split(line, ": ")if len(tmpValue) == 1 {inHeader = falsecontinue} else {f.Value = strings.Split(line, ": ")[1]}if fields == "" {fields = fmt.Sprintf("\"%s\":\"%s\" ", f.Name, f.Value)} else {fields += fmt.Sprintf(",\"%s\":\"%s\"", f.Name, f.Value)}} else {inHeader = falsecontinue}}}}if fields != "" {fields = fmt.Sprintf("{%v}", fields)array = append(array, fields)}if err := scanner.Err(); err != nil {log.Println(err)}for i := 0; i < len(array); i++ {//decoder := json.NewDecoder(strings.NewReader(array[i]))////for key, value := range JsonToMap(array[i]) {//	fmt.Printf("键:%v,值:%d\n", key, value)//}m := make(map[string]string)err = json.Unmarshal([]byte(array[i]), &m)if err != nil {fmt.Printf("Unmarshal with error: %+v", err)}keys := ""values := ""isExec := false//newKeys := make([]string, 0, len(m))//for k := range m {//	newKeys = append(newKeys, k)//}对切片进行排序//sort.Strings(newKeys)for key, value := range m {if keys == "" {keys = fmt.Sprintf("%v", key)if value == "<nil>" {values = fmt.Sprintf("%v", "NULL")} else {values = fmt.Sprintf("'%v'", value)}} else {keys += fmt.Sprintf(",%v", key)if value == "<nil>" {values += fmt.Sprintf(",%v", "NULL")} else {values += fmt.Sprintf(",'%v'", value)}}if key == "updated_date" {isExec = trueupdateTime, _ := time.Parse("2006-01-02 15:04:05", value)if updateTime.After(time2) {isExec = true} else {isExec = false}}}if isExec {inSql := fmt.Sprintf("insert into coupon_trade_record(%v) values(%v);", keys, values)logOutFile.Println(inSql)}}
}func outSlowLogFile(outFile string) {outFilePath, err := os.OpenFile(outFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0766)if err != nil {log.Println(fmt.Sprintf("创建输出文件失败:%s", err))return}logOutFile = log.New(outFilePath, "", log.Lmsgprefix)
}

通过代码可以将DELETE sql直接转成insert 语句:

 至此完成将数据重新插入到业务库里面,即可完成恢复

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/5037.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[C++][数据结构]二叉搜索树:介绍和实现

二叉搜索树 概念 二叉搜索树又称二叉排序树&#xff0c;它是一棵空树&#xff0c;或者是具有以下性质的二叉树: 若它的左子树不为空&#xff0c;则左子树上所有节点的值都小于根节点的值若它的右子树不为空&#xff0c;则右子树上所有节点的值都大于根节点的值它的左右子树也…

IoT Scenario: Smart Retail System-Multiple Sources and Multiple Terminals

物联网/大数据可视化领域发文可以联系&#xff1a;nascimsina.com IoT Scenario: Smart Retail System Overview The use of IoT in the retail industry enhances customer experiences, optimizes inventory management, and provides valuable insights into consumer beh…

【架构】后端项目如何分层及分层领域模型简化

文章目录 一. 如何分层1. 阿里规范2. 具体案例分析 二. 分层领域模型的转换1. 阿里规范2. 模型种类简化分析 三. 小结 本文描述后端项目中如何进行分层&#xff0c;以及分层领域模型简化 一. 如何分层 1. 阿里规范 阿里的编码规范中约束分层逻辑如下: 开放接口层&#xff1a…

Java全栈开发前端+后端(全栈工程师进阶之路)-环境搭建

在课程开始前我们要配置好我们的开发环境&#xff0c;这里我的电脑太乱了&#xff0c;我使用vm虚拟机进行搭建开发环境&#xff0c;如果有需要环境的或者安装包&#xff0c;可以私信我。 那我们开始 首先我们安装数据库 这里我们使用小皮面板 小皮面板(phpstudy) - 让天下没…

【计算机毕业设计】基于SSM++jsp的社区管理与服务系统【源码+lw+部署文档+讲解】

目录 摘 要 Abstract 第一章 绪论 第二章 系统关键技术 第三章 系统分析 3.1.1技术可行性 3.1.2经济可行性 3.1.3运行可行性 3.1.4法律可行性 3.4.1注册流程 3.4.2登录流程 3.4.3活动报名流程 第四章 系统设计 4.3.1登录模块顺序图 4.3.2添加信息模块顺序图 4.4.1 数据库E-…

【Node.js工程师养成计划】之express框架

一、Express 官网&#xff1a;http://www.expressjs.com.cn express 是一个基于内置核心 http 模块的&#xff0c;一个第三方的包&#xff0c;专注于 web 服务器的构建。 Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用&…

使用LocalGPT+cpolar打造可远程访问的本地私有类chatgpt服务

文章目录 前言环境准备1. localGPT部署2. 启动和使用3. 安装cpolar 内网穿透4. 创建公网地址5. 公网地址访问6. 固定公网地址 前言 本文主要介绍如何本地部署LocalGPT并实现远程访问&#xff0c;由于localGPT只能通过本地局域网IP地址端口号的形式访问&#xff0c;实现远程访问…

iOS 实现视图遮罩效果

有时候&#xff0c;我们会遇到这种需求&#xff0c;只讲视图的某个部分展示出来 这时候&#xff0c;我们可以通过设置该视图layer.mask layerb来实现&#xff0c;需要注意的是&#xff0c;这里的layerb必须要设置backgroundColor&#xff0c;渐变layer有colors,否则达不到效果…

Java学习3:程序流程控制

Java程序流程控制 1.执行顺序 顺序结构分支顺序 if,switch 循环结构 for ,while ,do-while 2.if分支 三种形式 if(条件表达式){} else if(){} else{}3.switch分支 string week "周一"; switch(week){case "周一":stem.out.println("周一&qu…

UG NX二次开发(C++)-获取模型中所有的拉伸(Extrude)特征

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 1、前言2、在UG 12中创建几个拉伸特征3、UFun中获取对象类型4、通过NXOpen过渡5.测试结果1、前言 在采用UG NX二次开发时,有时需要在模型中获取特定的对象,比如拉伸特征、关联特征等等。但是通过…

vue2 实现echarts图表进入可视区域后再加载动画,以及 使用了resize之后,动画失效问题解决

Intersection Observer API 是一个现代的浏览器 API&#xff0c;用于监测一个或多个目标元素与其祖先元素或视窗&#xff08;viewport&#xff09;之间的交叉状态&#xff08;intersection&#xff09;的变化。它可以有效地监听元素是否进入或离开可视区域&#xff0c;从而实现…

【氮化镓】AlGaN/GaN HEMTs沟道温度测量

文章是关于AlGaN/GaN HEMTs&#xff08;高电子迁移率晶体管&#xff09;在不同基底&#xff08;如蓝宝石和硅&#xff09;上生长时&#xff0c;通过直流&#xff08;DC&#xff09;特性方法确定沟道温度的研究。文章由J. Kuzmk, P. Javorka, A. Alam, M. Marso, M. Heuken, 和 …

UWB人员实时定位系统,Spring boot +Vue框架开发的UWB源码

UWB定位技术最核心的优势就是定位精度&#xff0c;可达厘米级&#xff0c;是其它定位技术的成百上千倍&#xff0c;在此精度下&#xff0c;可以满足绝大多数行业精细化管理的需求。 有了精准的位置数据&#xff0c;就可以把人员和物资的轨迹进行数据化还原&#xff0c;通过人工…

爬虫自动调用shell通过脚本运行scrapy爬虫(crawler API)

一、爬虫时如何同时调用shell 1)终端cd项目>>scrapy crawl example 2)打开example.py import scrapy from scrapy.shell import inspect_response#引入shellclass ExampleSpider(scrapy.Spider):name "example"allowed_domains ["example.com"]…

从0开始学习制作一个微信小程序 学习部分(2)json文件的说明与app.json文件的操作

系列文章目录 本文是小程序制作系列的学习篇的第二篇 学习篇第一篇我们讲了编译器下载&#xff0c;项目、环境建立、文件说明与简单操作&#xff1a;第一篇链接 本篇将继续讲解一些基础的编码&#xff0c;分析json文件的作用,着重讲解app.json里可以对小程序进行的切换页面&am…

Blender常见操作

1.局部视图&#xff1a;Local View&#xff0c;也可称作Solo模式&#xff0c;按快捷键 “/”进入&#xff0c;在按退出&#xff0c;只显示选中的物体&#xff08;可多选&#xff09;&#xff0c;方便编辑 2.物体合并&#xff1a;Ctrl J 其中&#xff0c;当选中多个物体时&am…

二、VLAN原理和配置

vlan不是协议&#xff0c;是一个技术&#xff0c;虚拟局域网技术&#xff0c;基于802.1q协议。 vlan&#xff08;虚拟局域网&#xff09;&#xff0c;将一个物理的局域网在逻辑上划分成多个广播域的技术。 目录 1.冲突域和广播域 概念 范围 2.以太网帧格式 3.以太网帧封装…

字节5面挂,恶心到了。。。

字节五面 今天脉脉看到一篇帖子&#xff1a; 楼主是 tx 的前员工&#xff0c;在字节五面&#xff08;加轮&#xff09;被挂后&#xff0c;认定&#xff08;或许私下做了一些调查&#xff09;是字节 HR 向 tx 背调&#xff0c;然后被前同事捏造虚假信息&#xff0c;导致的面试失…

分类规则挖掘(一)

目录 一、分类问题概述&#xff08;一&#xff09;分类规则挖掘&#xff08;二&#xff09;分类规则评估&#xff08;三&#xff09;分类规则应用 二、k-最近邻分类法 一、分类问题概述 动物分类&#xff1a;设有动物学家陪小朋友林中散步&#xff0c;若有动物突然从小朋友身边…

【Linux】yum、vim

&#x1f308;个人主页&#xff1a;秦jh__https://blog.csdn.net/qinjh_?spm1010.2135.3001.5343&#x1f525; 系列专栏&#xff1a;https://blog.csdn.net/qinjh_/category_12625432.html 目录 Linux 软件包管理器 yum 什么是软件包 查看软件包 如何安装软件 如何卸载软…