Skip to content

Commit f4630a6

Browse files
committed
修复指定开始时间从binlog最开始解析bug
1 parent 517cbf7 commit f4630a6

File tree

3 files changed

+65
-4
lines changed

3 files changed

+65
-4
lines changed

README.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,21 @@
22

33
史上最变态的MySQL DML 闪回工具
44

5+
* [MySQL Flashback](#mysql-flashback)
6+
* [原理](#%E5%8E%9F%E7%90%86)
7+
* [再次捣腾这功能原因](#%E5%86%8D%E6%AC%A1%E6%8D%A3%E8%85%BE%E8%BF%99%E5%8A%9F%E8%83%BD%E5%8E%9F%E5%9B%A0)
8+
* [生成二进制](#%E7%94%9F%E6%88%90%E4%BA%8C%E8%BF%9B%E5%88%B6)
9+
* [支持的功能](#%E6%94%AF%E6%8C%81%E7%9A%84%E5%8A%9F%E8%83%BD)
10+
* [大家有我也有](#%E5%A4%A7%E5%AE%B6%E6%9C%89%E6%88%91%E4%B9%9F%E6%9C%89)
11+
* [我的亮点](#%E6%88%91%E7%9A%84%E4%BA%AE%E7%82%B9)
12+
* [生成回滚语句](#%E7%94%9F%E6%88%90%E5%9B%9E%E6%BB%9A%E8%AF%AD%E5%8F%A5)
13+
* [溜溜的玩法](#%E6%BA%9C%E6%BA%9C%E7%9A%84%E7%8E%A9%E6%B3%95)
14+
* [你可以指定多个 SQL](#%E4%BD%A0%E5%8F%AF%E4%BB%A5%E6%8C%87%E5%AE%9A%E5%A4%9A%E4%B8%AA-sql)
15+
* [老旧玩法](#%E8%80%81%E6%97%A7%E7%8E%A9%E6%B3%95)
16+
* [执行回滚SQL](#%E6%89%A7%E8%A1%8C%E5%9B%9E%E6%BB%9Asql)
17+
* [回滚原理和注意事项](#%E5%9B%9E%E6%BB%9A%E5%8E%9F%E7%90%86%E5%92%8C%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9)
18+
* [执行回滚就简单了](#%E6%89%A7%E8%A1%8C%E5%9B%9E%E6%BB%9A%E5%B0%B1%E7%AE%80%E5%8D%95%E4%BA%86)
19+
520
## 原理
621

722
`MySQL Binlog` 已经被玩烂掉的时代, 我想大家也都知道其中的原理. 无非就是解析 `Binlog` 反向生成需要回滚的 `SQL` 语句.
@@ -212,7 +227,7 @@ Flags:
212227

213228
1. 由于生成回滚语句是顺序记入到文件中去的, 所以我们回滚的时候需要倒序读取文件 `SQL` 进行回滚.
214229

215-
2. 在看回滚语句的时候你会发现, 每个`SQL`前面都有注释`/* crc32:xxx */`, 这里面的值`xxx`记录的是没条数据主键的一个`crc32`值. 主要是为了并发而记录的.
230+
2. 在看回滚语句的时候你会发现, 每个`SQL`前面都有注释`/* crc32:xxx */`, 这里面的值`xxx`记录的是每条数据主键的一个`crc32`值. 主要是为了并发而记录的.
216231

217232
```
218233
/* crc32:2313941001 */ INSERT INTO `employees`.`emp1`(`emp_no`, `birth_date`, `first_name`, `last_name`, `gender`, `hire_date`) VALUES(10008, "1958-02-19", "Saniya", "Kalloufi", 1, "1994-09-15");
@@ -252,4 +267,4 @@ Flags:
252267
--filepath string 指定执行的文件
253268
-h, --help help for execute
254269
--paraller int 回滚并发数 (default 1)
255-
```
270+
```

services/create/creator.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ import (
1818
"time"
1919
)
2020

21+
const (
22+
START_POS_BY_NONE uint8 = iota
23+
START_POS_BY_POS
24+
START_POS_BY_TIME
25+
)
26+
2127
type Creator struct {
2228
CC *config.CreateConfig
2329
DBC *config.DBConfig
@@ -26,8 +32,11 @@ type Creator struct {
2632
StartPosition *models.Position
2733
EndPosition *models.Position
2834
CurrentPosition *models.Position
35+
GetStartPosType uint8
2936
CurrentTimestamp uint32
3037
CurrentThreadID uint32
38+
StartTime time.Time
39+
StartTimestamp uint32
3140
HaveEndPosition bool
3241
EndTime time.Time
3342
HaveEndTime bool
@@ -62,6 +71,23 @@ func NewFlashback(sc *config.CreateConfig, dbc *config.DBConfig, mTables []*visi
6271
if err != nil {
6372
return nil, err
6473
}
74+
75+
// 获取获取位点的类型, 是通过 位点获取, 还是通过时间
76+
if ct.CC.HaveStartPosInfo() { // 通过指定位点获取开始位点
77+
ct.GetStartPosType = START_POS_BY_POS
78+
seelog.Infof("解析binglog开始位点通过指定 开始binlog 位点获取. 开始位点: %s", ct.StartPosition.String())
79+
} else if ct.CC.HaveStartTime() { // 通过时间获取开始位点
80+
ct.GetStartPosType = START_POS_BY_TIME
81+
seelog.Infof("解析binglog开始位点通过指定 开始时间 获取. 开始位点: %s", ct.StartPosition.String())
82+
ct.StartTime, err = utils.NewTime(ct.CC.StartTime)
83+
if err != nil {
84+
return nil, fmt.Errorf("输入的开始时间有问题. %v", err)
85+
}
86+
ct.StartTimestamp = uint32(ct.StartTime.Unix())
87+
} else {
88+
return nil, fmt.Errorf("无法获取")
89+
}
90+
6591
// 原sql文件
6692
fileName := ct.getSqlFileName("origin_sql")
6793
ct.OriSQLFile = fmt.Sprintf("%s/%s", ct.CC.GetSaveDir(), fileName)
@@ -202,6 +228,13 @@ func (this *Creator) runProduceEvent(wg *sync.WaitGroup) {
202228
defer wg.Done()
203229
defer this.Syncer.Close()
204230

231+
// 判断是否需要跳过, 位点
232+
var isSkip bool
233+
if this.GetStartPosType == START_POS_BY_TIME { // 如果开始位点是通过时间获取的就需要执行跳过
234+
isSkip = true
235+
}
236+
seelog.Debugf("是否是需要跳过事件: %v", isSkip)
237+
205238
pos := mysql.Position{this.StartPosition.File, this.StartPosition.Position}
206239
streamer, err := this.Syncer.StartSync(pos)
207240
if err != nil {
@@ -222,6 +255,19 @@ produceLoop:
222255
seelog.Error(err.Error())
223256
this.quit()
224257
}
258+
259+
// 过去掉还没到开始时间的事件
260+
if isSkip {
261+
// 判断是否到了开始时间
262+
if ev.Header.Timestamp < this.StartTimestamp {
263+
continue
264+
} else {
265+
isSkip = false
266+
seelog.Infof("停止跳过, 开始生成回滚sql. 时间戳: %d, 时间: %s, 位点: %s:%d", ev.Header.Timestamp,
267+
utils.TS2String(int64(ev.Header.Timestamp), utils.TIME_FORMAT), this.StartPosition.File, ev.Header.LogPos)
268+
}
269+
}
270+
225271
if err = this.handleEvent(ev); err != nil {
226272
seelog.Error(err.Error())
227273
this.quit()

services/create/helper.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func GetStartPosition(cc *config.CreateConfig, dbc *config.DBConfig) (*models.Po
3131
if err != nil {
3232
return nil, err
3333
}
34-
return getPositionByTime(uint32(ts), dbc)
34+
return getStartFileByTime(uint32(ts), dbc)
3535
}
3636

3737
return nil, nil
@@ -67,7 +67,7 @@ func checkStartPosInRange(startPos *models.Position) error {
6767
}
6868

6969
// 通过开始时间获取位点信息
70-
func getPositionByTime(ts uint32, dbc *config.DBConfig) (*models.Position, error) {
70+
func getStartFileByTime(ts uint32, dbc *config.DBConfig) (*models.Position, error) {
7171
// 如果指定时间大于但前 返回错误.
7272
nts := utils.NowTimestamp() // 但前时间戳
7373
if int64(ts) > nts {

0 commit comments

Comments
 (0)