TiDB 利用binlog 恢复-反解析binlog

news2024/12/23 1:51:58

我们知道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 main

import (
	"bufio"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"os"
	"regexp"
	"strings"
	"time"
)

var (
	binlogFile string
	schema     string
	table      string
	sqlType    string
	whereSql   string
	logPath    string
	filedRe    = 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 := false
	var f filed
	var array []string
	var 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 = true
			continue
		} else if strings.Contains(line, "sync binlog success") || strings.Contains(line, "read file end") {
			inHeader = false
			continue
		} else {
			if inHeader {
				if filedRe.MatchString(line) {
					f.Name = strings.Split(line, "(")[0]
					tmpValue := strings.Split(line, ": ")
					if len(tmpValue) == 1 {
						inHeader = false
						continue
					} 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 = false
					continue
				}
			}
		}

	}
	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 = true
				updateTime, _ := 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.coloradmin.cn/o/1634551.html

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

相关文章

软考中级之数据库系统工程师笔记总结(五)网络基础

作者&#xff1a;Maynor 博客之星大数据领域Top1,GitHub项目awesome-chatgpt-project作者, 腾讯云TDSQL-C数据库开发者, 全网技术矩阵粉丝7w 公众号&#xff1a;Maynor996随着信息技术的飞速发展&#xff0c;数据库已成为现代企业和组织不可或缺的数据管理工具。对于许多专业人…

pycharm安装pandas包

import pandas时提示未安装pandas&#xff0c;点击下图红框选项&#xff0c;进行pandas安装 pycharm底部会有安装中的提示 pycharm底部提示红框的内容&#xff0c;说明安装成功 这个时候就可以看到import pandas不再报错了

Spring-Mybatis-Xml管理(动态sql语句,sql语句复用)

目录 前置条件 动态SQL语句 动态删除数据 1.集合类型:数组 2.集合类型: List 型 SQL语句重用 说明 &#x1f9e8;前置条件 已经创建了实体类(这边举个例子) 实体类User表 表中的字段名User实体类的属性值id (bigint auto increment) 长整型 自动增长private Long iduser…

消失的数字

一、题目 原题链接. - 力扣&#xff08;LeetCode&#xff09; 二、解题 int missingNumber(int* nums, int numsSize){ //a^0a; //a^a0; //先求出0^1^2......^n的数&#xff0c;再将其与数组每个元素异或即可得到要寻找的数字 int ret0; for(int i0;i<numsSize;i) {ret^i;…

transformer原理-Attention Is All You Need

文章目录 前言动机NLP任务特点循环神经网络循环神经网络流行原因循环神经网络缺点 transformer架构整体架构&#xff08;编码器-解码器&#xff09;自注意力&#xff08;self-attention&#xff09;传统NLP解决方案改进思路 多头注意力&#xff08;Multi-Head Attention&#x…

《Fundamentals of Power Electronics》——反激变换器

反激转换器基于升降压转换器&#xff0c;接下去介绍它的演变过程。下图中的a描述了基本升降压型转换器电路&#xff0c;用一个MOSFET和二极管实现开关。 在图b中&#xff0c;电感绕组由两根导线构成&#xff0c;匝数比为1:1。电感的基础作用未改变&#xff0c;并联绕组可以等效…

在 Windows 系统上安装 TeamViewer 13

在 Windows 系统上安装 TeamViewer 13 References 默认安装到所有用户 同意协议 安装目录 勾选内容 打开文件位置 打开 rClientID.exe Extras -> Options -> Advanced Show advanced options -> Display language 重新启动TeamViewer 语言可修改为中文简体 …

Web 渗透测试神器:HackBar 保姆级教程

一、介绍 HackBar 是一个用于浏览器的扩展插件&#xff0c;主要用于进行网络渗透测试和安全评估。它提供了一系列方便的工具和功能&#xff0c;可以帮助用户执行各种网络攻击和测试&#xff0c;包括 XSS、SQL 注入、CSRF、路径穿越等。以下是 HackBar 插件的一些主要特点和功能…

Transformer - Layer Normalization

Transformer - Layer Normalization flyfish y x − E [ x ] V a r [ x ] ϵ ∗ γ β y \frac{x - \mathrm{E}[x]}{ \sqrt{\mathrm{Var}[x] \epsilon}} * \gamma \beta yVar[x]ϵ ​x−E[x]​∗γβ 论文 Layer Normalization import numpy as np import torch import…

【Jenkins】持续集成与交付 (一):深入理解什么是持续集成?

🟣【Jenkins】持续集成与交付 (一):深入理解什么是持续集成? 1、软件开发生命周期与持续集成2、 持续集成的流程3、持续集成的好处4、Jenkins的应用实践5、结语💖The Begin💖点点关注,收藏不迷路💖 1、软件开发生命周期与持续集成 软件开发生命周期(SDLC)是指软…

Redis缓存介绍以及常见缓存问题:穿透、雪崩和击穿

概念 缓存就是数据交换的缓冲区&#xff08;Cache&#xff09;&#xff0c;是存贮数据的临时地方&#xff0c;一般读写性能较高。 作用&#xff1a; 降低后端负载 提高读写效率&#xff0c;降低相应时间 成本&#xff1a; 数据一致性成本 代码维护成本 运维成本 缓存更…

使用pyqt编写的页面导航框架

使用pyqt编写的页面导航框架 效果 介绍代码 效果 介绍 使用pyqt多种控件编写的导航框架&#xff0c;左边是菜单栏&#xff0c;点击不同的菜单选项可以切换到不同的页面。 代码 import sys from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QP…

微服务之分布式理论zookeeper概述

一、分布式技术相关的理论 CAP理论 CAP定理(CAP theorem)&#xff0c;⼜被称作布鲁尔定理(Eric Brewer)&#xff0c;1998年第⼀次提出. 最初提出是指分布式数据存储不可能同时提供以下三种保证中的两种以上: (1) ⼀致性(Consistency): 每次读取收到的信息都是最新的; (2) …

Leetcode—1329. 将矩阵按对角线排序【中等】(unordered_map、priority_queue)

2024每日刷题&#xff08;121&#xff09; Leetcode—1329. 将矩阵按对角线排序 实现代码 class Solution { public:vector<vector<int>> diagonalSort(vector<vector<int>>& mat) {const int m mat.size();const int n mat[0].size();unorder…

(51单片机)第十三章-STC系列51单片机功能介绍

13.1 单片机空闲与掉电模式的应用 1. 空闲模式 当单片机进入空闲模式时&#xff0c;除CPU处于休眠状态外&#xff0c;其余硬件全部处于活动状态&#xff0c;芯片中程序未涉及的数据存储器和特殊功能寄存器中的数据在空闲模式期间都将保持原值。假若定时器正在运行&#xff0c;…

【tcl脚本实践Demo 1】文本生成、匹配、修改、读写

引言 在芯片设计的流程中,各种EDA工具在设计、综合、布局布线、验证、时序分析等等环节都会产出大量的文件信息。这些信息是海量的,如果单纯靠程序员自己查看信息效率很低并且很容易纰漏。所以脚本语言可以很好的解决这个问题,可以利用脚本语言匹配到敏感的信息,完成对信息…

C++ 如何实现原子性

1.操作系统如何实现原子性 在单处理器,单核,运行多线程的情况下,我们不使用线程同步工具, 我们会出现,线程之间会互相抢夺,临界区的资源,造成数据不符合我们预期的结果, 后面再说解决办法,那么我们怎么帮助实现原子性 1 屏蔽中断,不让线程之间切换,让它完成再切换 2 底层硬…

算法设计与分析 3.2 牛顿法及改进、迭代法、矩阵谱半径、雅可比迭代、高斯迭代

思考题1 改进cosx&#xff1f;优化算法 关键点在于cos计算过于麻烦&#xff0c;而每次都要求sinx的值 故直接简化为cosx的导数 -sinx 即&#xff1a; 原&#xff1a;//double daoshu(double x) { // return 18 * x - cos(x); //} 改&#xff1a;double daoshu(double x) {retu…

基于ssm+vue开放式教学评价管理系统【ppt·代码·文档报告】

项目演示视频 项目名称&#xff1a;开放式教学评价管理系统 系统介绍&#xff1a;本系统是通过java的SSM框架来实现的&#xff0c;前端采用vue框架进行实现 管理员通过登录进入到系统操作界面&#xff0c;结合需求可以对个人信息进行在线修改维护&#xff0c;也可结合需求进行…

深入剖析Redis哨兵模式的原理和应用

【更多精彩内容,欢迎关注小米的微信公众号“软件求生”】 大家好,我是小米!今天我们来聊一聊Redis中一个非常重要的话题——哨兵模式。相信大家在使用Redis时一定遇到过一些分布式系统的问题,而哨兵模式正是解决这些问题的关键之一。让我们一起来深入了解一下哨兵模式的原…