TiDB 利用binlog 恢复-反解析binlog

2024-04-30 07:44
文章标签 tidb 恢复 解析 binlog

本文主要是介绍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 语句:

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

这篇关于TiDB 利用binlog 恢复-反解析binlog的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/948279

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

电脑桌面文件删除了怎么找回来?别急,快速恢复攻略在此

在日常使用电脑的过程中,我们经常会遇到这样的情况:一不小心,桌面上的某个重要文件被删除了。这时,大多数人可能会感到惊慌失措,不知所措。 其实,不必过于担心,因为有很多方法可以帮助我们找回被删除的桌面文件。下面,就让我们一起来了解一下这些恢复桌面文件的方法吧。 一、使用撤销操作 如果我们刚刚删除了桌面上的文件,并且还没有进行其他操作,那么可以尝试使用撤销操作来恢复文件。在键盘上同时按下“C

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

OWASP十大安全漏洞解析

OWASP(开放式Web应用程序安全项目)发布的“十大安全漏洞”列表是Web应用程序安全领域的权威指南,它总结了Web应用程序中最常见、最危险的安全隐患。以下是对OWASP十大安全漏洞的详细解析: 1. 注入漏洞(Injection) 描述:攻击者通过在应用程序的输入数据中插入恶意代码,从而控制应用程序的行为。常见的注入类型包括SQL注入、OS命令注入、LDAP注入等。 影响:可能导致数据泄

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

Spring 源码解读:自定义实现Bean定义的注册与解析

引言 在Spring框架中,Bean的注册与解析是整个依赖注入流程的核心步骤。通过Bean定义,Spring容器知道如何创建、配置和管理每个Bean实例。本篇文章将通过实现一个简化版的Bean定义注册与解析机制,帮助你理解Spring框架背后的设计逻辑。我们还将对比Spring中的BeanDefinition和BeanDefinitionRegistry,以全面掌握Bean注册和解析的核心原理。

CSP 2023 提高级第一轮 CSP-S 2023初试题 完善程序第二题解析 未完

一、题目阅读 (最大值之和)给定整数序列 a0,⋯,an−1,求该序列所有非空连续子序列的最大值之和。上述参数满足 1≤n≤105 和 1≤ai≤108。 一个序列的非空连续子序列可以用两个下标 ll 和 rr(其中0≤l≤r<n0≤l≤r<n)表示,对应的序列为 al,al+1,⋯,ar​。两个非空连续子序列不同,当且仅当下标不同。 例如,当原序列为 [1,2,1,2] 时,要计算子序列 [

多线程解析报表

假如有这样一个需求,当我们需要解析一个Excel里多个sheet的数据时,可以考虑使用多线程,每个线程解析一个sheet里的数据,等到所有的sheet都解析完之后,程序需要提示解析完成。 Way1 join import java.time.LocalTime;public class Main {public static void main(String[] args) thro