前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >深入go-mysql源码(1)--Slave的注册和流开启

深入go-mysql源码(1)--Slave的注册和流开启

原创
作者头像
有财君
修改2024-03-29 07:41:21
2070
修改2024-03-29 07:41:21
举报
文章被收录于专栏:我的编码学习笔记

由于工作的关系,我需要大量的使用go-mysql的代码中关于复制的一部分。

作为一个曾经的DBA,也刚好对这部分内容感兴趣,所以就深入的研究了一下这部分的代码,现在将学习的笔记整理在此。

如果可能的话,顺手就连alibaba canal一起学了。

1. Binlog各类报文

首先考虑这样一个问题:我们要如何得到MySQL的Binlog,像Slave一样?

回答这个问题之前,先回忆一下如何将一个slave加入到集群里:

  • 启动一个MySQL实例,执行change master语句,指定master的基本信息和binlog的基本信息;
  • 执行start slave命令,开始复制。

这些操作其实就是一系列的网络请求,只需要了解其背后的报文格式,就可以实现。

先说说change master做了什么:发送了一个COM_REGISTER_SLAVE报文给master机器,这个报文的编码是0x15,也就是10进制的21。

这样就完成了注册操作,但是此时还不能开始dump binlog。接下来会给master发送一个COM_BINLOG_DUMP报文,这个报文的编码是0x12,也就是10进制的18。

大部分情况下,我们只需要关注这两种报文就可以启动一个进程来源源不断的获取binlog了。

1.1 COM_REGISTER_SLAVE

先来看看一个典型的change master语句:

代码语言:sql
复制
change master to master_host='localhost', MASTER_PORT=3306, master_user='repl', master_password='repl', master_log_file='mysql-bin.000001', master_log_pos=0;

也就是说,在发送COM_REGISTER_SLAVE报文的时候,还需要携带一些关键信息上去。

看一下这个报文的结构。

字节数

说明

1

0x15

4

serverId

1

hostname长度

n

hostname

1

user name长度

n

user name

1

password长度

n

password

2

port

4

replication rank,忽略

4

master id,给0

但是,这里需要注意,go-mysql里这个报文不是从位置0开始的,而是从4开始:

代码语言:go
复制
pos := 4

data[pos] = COM_REGISTER_SLAVE
pos++

对于这一点,我是比较疑惑的,后来翻看了一下alibaba canal的相同代码,没有发现前4位有什么特殊的地方:

不过后来我发现MariaDB里有这样一个文档:

https://mariadb.com/kb/en/com_register_slave/

也就是说,前4位可能是一个类似于魔法数一样的东西。

这个问题留待以后再去探求。

1.2 COM_BINLOG_DUMP

完成注册以后,就可以开始DUMP binlog数据了。

这个报文有官方文档可以看,还是比较友好的:

这个报文基本都是定长数据,只有filename这一个变长数据。

但是,go-mysql里的cmd也是从位置4开始写的。还是从MariaDB处得到答案:

可见,前面4位确实是一类类似魔数的东西。

这个答案,在调试代码的还是发现了,其实这4位就是预留了Header的位置。

那么这个Header有什么呢?还是在alibaba的canal里找到了答案:

也就是前三位保存body的长度(小端序),第4位保存一个序列号,这个序列号在拼body的数据时候就已经置为0了,在alibaba canal里也是一样的处理方式:

1.3 COM_BINLOG_DUMP_GTID

这个报文和之前的COM_BINLOG_DUMP有类似之处,都是启动binlog的dump操作,不同的是,这个报文是专门为了gtid而设计的。

比如这个命令:

代码语言:sql
复制
change master to master_host='192.110.103.41',master_port=3106,master_auto_position=1; 

结构说明如下:

字节数

说明

1

0xef

2

0x04

4

server-id

4

binlog filename len,可以写成0,后面的file name就直接忽略掉,跳过不传即可

n

file name

8

Pos,可以写死成4

4

gtid data len

n

gtid data

  • go-mysql和alibaba canal对file name的处理是不同的,alibaba直接忽略了filename,并给file name length设置成了0。
  • go-mysql和alibaba canal对pos的处理也不同,alibaba给了4。

实际上,从节约的角度来看问题,alibaba的处理更为优秀,在autoposition的设置下,fileName和Pos是无关紧要的东西。

2. Syncer的启动

从之前的描述中,我们知道了启动复制是一件不太神秘的事情。下面来分析一下Syncer的启动过程,了解一下go-mysql是如何读取binlog事件的。

这个代码的位置在replication/binlogsyncer.go中,所有的逻辑都会落在这段代码里:

代码语言:go
复制
// StartSync starts syncing from the `pos` position.
func (b *BinlogSyncer) StartSync(pos Position) (*BinlogStreamer, error) {
	b.cfg.Logger.Infof("begin to sync binlog from position %s", pos)

	b.m.Lock()
	defer b.m.Unlock()

	if b.running {
		return nil, errors.Trace(errSyncRunning)
	}

	if err := b.prepareSyncPos(pos); err != nil {
		return nil, errors.Trace(err)
	}

	return b.startDumpStream(), nil
}

这个过程有几个关键点需要注意的:

  • prepareSyncPos:发送Dump报文给master,具体的发送内容参考1.2的描述
  • startDumpStream:这个函数会新建一个stream(主体是一个channel),并启动一个Goroutine去不断地拉取binlog event写入stream中。

所以,启动syncer的过程实际还是不复杂的,可以写这么一段测试代码:

代码语言:go
复制
func TestCanal(t *testing.T) {
	cfg := replication.BinlogSyncerConfig{
		ServerID: 100,
		Flavor:   "mysql",
		Host:     "127.0.0.1",
		Port:     3306,
		User:     "root",
		Password: "a123456!",
	}
	syncer := replication.NewBinlogSyncer(cfg)
	pos := mysql.Position{
		Name: "binlog.000042",
	}
	streamer, _ := syncer.StartSync(pos)

	for {
		ev, _ := streamer.GetEvent(context.Background())
		// Dump event
		ev.Dump(os.Stdout)
	}
}

可以很清楚的看到,启动Syncer就是打开了一个流,去不断地获取event。

3. MySQL的各类常用Event

各类事件都会被记录在event的Header中,占用一个字节。比如4代表了ROTATE_EVENT。

3.1 ROTATE_EVENT

这是能解析到的第一个事件,但是有意思的是,如果用show binlog events命令查看,却看不到这个事件:

如果单步调试一下,注意这个LogPos,很明显是从0开始的,这也就解释了为啥我们看到的事件都是从位置4开始的,这里的LogPos实际就是我们在命令里看到的End_log_pos的值(只是这个事件不显示罢了):

在实际操作的过程中,我们也几乎不会去关注这个事件,所以仅做简单的介绍就可以了。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. Binlog各类报文
    • 1.1 COM_REGISTER_SLAVE
      • 1.2 COM_BINLOG_DUMP
        • 1.3 COM_BINLOG_DUMP_GTID
        • 2. Syncer的启动
        • 3. MySQL的各类常用Event
          • 3.1 ROTATE_EVENT
          相关产品与服务
          云数据库 MySQL
          腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档