PowerData北鱼 PowerData

目前MySQL的同步工具有很多,Maxwell也是其中比较好的一个选择,本文就同步工具的优略,以及Maxwell的使用做一个较为详细的说明。

1.Maxawell 简介

Maxwell是一个能实时读取MySQL的binlog日志,并生成 JSON 格式的消息,发送给 Kafka,Kinesis、RabbitMQ、Redis或其他平台的应用程序。常见应用场景有ETL、维护缓存、为搜索引擎构建索引等。

官网(http://maxwells-daemon.io)、GitHub(https://github.com/zendesk/ma...

Maxwell的主要特色:

  • 支持以 select * FROM table 的方式全量同步数据;
  • 支持断点续传;
  • 可以根据database、table、column等级别的数据进行分区,用于解决数据倾斜问题;
  • 轻量级应用;

1.1常见 MySQL 同步工具对比

Maxwell相比于Canal、Flink CDC 更加轻量级,不依赖其他组件。Canal需要自己编写客户端来消费解析到的数据,而Maxwell直接输出JSON格式的数据,不需要再用客户端单独解析。同时Maxwell支持全量数据同步,虽然高可用没有直接支持,但是可以断点续传。

1.2 Maxwell原理解析

Maxwell的工作原理非常简单:

  1. 伪装成 MySQL 的一个 slave,然后接收binlog
  2. 对binlog进行解析,封装成Maxwell的JSON数据格式
  3. 记录读取到的binlog位移信息,保存到MySQL中,用于断点续传
  4. 将封装的JSON数据,发送至Kafka、RabbitMQ等下游

而Maxwell的全量同步则是通过select * FROM table 的方式,分页查出全量数据,然后发送到下游。全量和增量可以通过配置同步、或异步进行。

2.快速开始

2.1. 下载Maxwell

下载Maxwell: https://github.com/zendesk/ma...

解压缩:

tar zxvf  maxwell-1.39.2.tar.gz cd maxwell-1.39.2

也可以用docker

docker pull zendesk/maxwell

2.2. 配置MySQL

MySQL开启binlog

# /etc/my.cnf [mysqld] binlog_format=row server_id=1  log-bin=master

创建Maxwell用户,并赋予 maxwell 库的一些权限

mysql> CREATE USER 'maxwell'@'%' IDENTIFIED BY 'XXXXXX'; mysql> CREATE USER 'maxwell'@'localhost' IDENTIFIED BY 'XXXXXX'; mysql> GRANT ALL ON maxwell.* TO 'maxwell'@'%'; mysql> GRANT ALL ON maxwell.* TO 'maxwell'@'localhost'; mysql> GRANT SELECT, REPLICATION CLIENT, REPLICATION SLAVE ON *.* TO 'maxwell'@'%'; mysql> GRANT SELECT, REPLICATION CLIENT, REPLICATION SLAVE ON *.* TO 'maxwell'@'localhost';

创建一个测试表

CREATE TABLE `maxwell_test` (  `id` bigint(11) NOT NULL AUTO_INCREMENT,  `test_key` int(11) DEFAULT NULL,  `test_value` varchar(255) DEFAULT NULL,  `test_time` datetime NULL DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2.3. 运行Maxwell

可以用命令行输出的形式进行测试:

bin/maxwell --user='maxwell' --password='XXXXXX' --host='127.0.0.1' --producer=stdout

注意:如果是1.30及以上版本需要JDK11, 否则会报错

这里我们通过修改执行脚本,指定JDK11路径即可,也可以安装多个版本jdk采用alternatives命令来动态切换,不过还是建议升级到JDK11来解决。

修改运行脚本

vim bin/maxwell

输出到Kafka:

bin/maxwell --user='maxwell' --password='XXXXXX' --host='127.0.0.1' \ --producer=kafka --kafka.bootstrap.servers=localhost:9092 --kafka_topic=maxwell

使用过滤器,只显示测试表的数据:

bin/maxwell --user='maxwell' --password='XXXXXX' --host='127.0.0.1' --producer=stdout --filter='exclude: *.*, include: db_test.maxwell_test'

执行测试SQL

insert into maxwell_test values(1,22,"hello","2022-01-01 12:00:00"); update maxwell_test set test_value='hello22' where id=1; delete from maxwell_test where id=1;

2.4 结果格式说明

执行测试SQL后可以在控制台看到输出

新增

insert into maxwell_test values(1,22,"hello","2022-01-01 12:00:00");

{     "database":"db_test",     "table":"maxwell_test",     "type":"insert",     "ts":1667897460,     "xid":1410361478,     "commit":true,     "data":{         "id":1,         "test_key":22,         "test_value":"hello",         "test_time":"2022-01-01 12:00:00"     } }

  • type: 大多数是 insert/update/delete, 如果采用全量同步,会看到 bootstrap-insert 类型;
  • ts: 秒级别的时间戳
  • xid:MySQL 事务id
  • commit: 一个xid,只有一个事务,可以根据这个字段重组事务
  • data: 新数据

更新update maxwell_test set test_value='hello22' where id=1;

{     "database":"db_test",     "table":"maxwell_test",     "type":"update",     "ts":1667897460,     "xid":1410361479,     "commit":true,     "data":{         "id":1,         "test_key":22,         "test_value":"hello22",         "test_time":"2022-01-01 12:00:00"     },     "old":{         "test_value":"hello"     } }

和新增相比,增加了一个old字段,代表旧值

删除delete from maxwell_test where id=1;

{     "database":"db_test",     "table":"maxwell_test",     "type":"delete",     "ts":1667897460,     "xid":1410361480,     "commit":true,     "data":{         "id":1,         "test_key":22,         "test_value":"hello22",         "test_time":"2022-01-01 12:00:00"     } }

删除的数据依旧会在data中显示一遍。

3.进阶使用

3.1 基本配置

选项

参数值

描述

默认值

config

String

配置文件 config.properties 的路径

$PWD/config.properties

log\_level

debug|info|warn|error

日志等级

info

daemon

运行Maxwell作为守护进程

env\_config\_prefix

STRING

匹配该前缀的环境变量将被视为配置值

Maxwell的配置可以通过命令命令行、配置文件、环境变量来指定,配置的优先级为:

命令行 > 环境变量 > 配置文件 > 默认值

一般通过配置文件进行配置即可,通过 --config指定,或者在当前工作目录中命名为config.properties

mkdir config vim dev.properties

粘贴下面的配置

# mysql user=maxwell password=123456 host=127.0.0.1 jdbc_options=serverTimezone=Asia/Shanghai filter=exclude: *.*, include: db_test.maxwell_test  --producer=stdout

重新运行maxwell:

bin/maxwell --config config/dev.properties

3.2 MySQL配置

Maxwell中的角色被划分为:host、replication\_host、schema\_host三种。一般情况下,这三个角色都在一个主机上,也可以分开指定。

  • host: Maxwell维护信息的主机,存储了捕获到的schema、binlog偏移位置、全量同步数据等信息,一共有六张表:
  • bootstrap: 全量数据同步记录,如果不想用客户端操作全量同步,也可以通过SQL,向这个表里插入记录。
  • columns: 所有的字段信息
  • databases: 所有的数据库信息
  • tables:所有的表信息
  • schemas: 所有的binlog文件信息
  • positions:读取到的binlog的位移信息,用于断点续传
  • heartbeats:Maxwell的心跳信息
  • replication\_host: 需要采集binlog信息的主机,将host和replication\_host分开,可以避免maxwell的数据写入生产数据库。
  • shcema\_host: 捕获schema的主机,一般用不到,不用单独配置。

常用的MySQL配置项如下:

选项

参数值

描述

默认值

host

STRING

mysql 地址

localhost

user

STRING

mysql username

password

STRING

mysql password

(no password)

port

INT

mysql port

3306

jdbc\_options

STRING

mysql jdbc 连接选项

schema\_database

STRING

Maxwell用于维护的schema和position将使用的数据库

maxwell

client\_id

STRING

用于标识Maxwell实例的唯一字符串

maxwell

replica\_server\_id

LONG

用于标识Maxwell实例的唯一数字

6379

gtid\_mode

BOOLEAN

是否开启 GTID 复制

false

replication\_host

STRING

复制的服务器

schema-store host

replication\_password

STRING

password on replication server

(none)

replication\_port

INT

port on replication server

3306

replication\_user

STRING

user on replication server

replication\_jdbc\_options

STRING

mysql jdbc connection options for replication server

[DEFAULT\_JDBC\_OPTS]

SSL\_OPTION: [ DISABLED | PREFERRED | REQUIRED | VERIFY\_CA | VERIFY\_IDENTITY ]

3.3 生产者配置

大数据领域常用的生产者就是Kafka, Maxwelll还支持Rabbitmq、Redis等,可以参考官方文档,有两点需要注意:

  • Topic: Maxwell默认向 Kafka 写入的 topic 是 maxwell,可以通过--kafka_topic来指定,当然也可以动态配置,例如namespace_%{database}_%{table}, 这个topic将会根据发送的数据动态改变。
  • Partition:  Maxwell默认分区是根据数据库进行hash,同一个库的数据会被放到同一个 partition, 这可能会导致数据倾斜问题,如果要解决可以通过配置producer_partition_by, 指定 database、table、primary\_key 来解决。

生产者的通用配置:

选项

参数值

描述

默认值

producer

[PRODUCER\_TYPE]

生产者类型

stdout

custom\_producer.factory

CLASS\_NAME

自定义消费者的工厂类

producer\_ack\_timeout

异步消费认为消息丢失的超时时间(毫秒ms)

producer\_partition\_by

[PARTITION\_BY]

输入到kafka/kinesis的分区函数

database

producer\_partition\_columns

STRING

若按列分区,以逗号分隔的列名称

PRODUCER\_TYPE: [ stdout | file | kafka | kinesis | pubsub | sqs | rabbitmq | redis ]

PARTITION\_BY\_FALLBACK: [ database | table | primary\_key | transaction\_id ]

Kafka配置:

option

argument

description

default

kafka.bootstrap.servers

STRING

kafka 集群列表, 格式 HOST:PORT[,HOST:PORT]

kafka\_topic

STRING

要写入的kafka topic

maxwell

kafka\_version

[KAFKA\_VERSION]

指定maxwell的 kafka 生产者客户端版本,不可在config.properties中配置

0.11.0.1

KAFKA\_VERSION: [ 0.8.2.2 | 0.9.0.1 | 0.10.0.1 | 0.10.2.1 | 0.11.0.1 ]

3.4 过滤器配置

Maxwell 可以配置只输出指定表的binlog, 通过--filter 命令,也可以对列进行过滤,一些官网的例子:

案例1:

--filter = 'exclude: foodb.*, include: db_test.maxwell_test, include: db_test./table_\d+/'

这个案例会让 Maxwell 采集排除db_test的所有变更,除了 maxwell_test表 和符合正则表达式 /table_\d+/ 的所有表。

案例2:

--filter = 'exclude: *.*, include: db1.*'

这个案例会让 Maxwell 采集排除所有数据库的采集,除了db1库的所有表。

案例3:

--filter = 'exclude: db.tbl.col = reject'

这会排除对 db.tb1 表中,col 列数据的采集。

3.5 数据初始化

MySQL的binlog是会被清除的,如果需要历史数据,在没有binlog,且同步不能影响业务的情况下,我们怎么才能同步整张表的数据呢?

可以通过 maxwell-bootstrap 命令开启数据初始化,原理就是通过 select * from table 查询把结果输出到流中。

具体参数为:

选项

描述

--log\_level LOG\_LEVEL

日志等级 (DEBUG, INFO, WARN or ERROR)

--user USER

mysql username

--password PASSWORD

mysql password

--host HOST

mysql host

--port PORT

mysql port

--database DATABASE

mysql 中需要初始化的数据库

--table TABLE

mysql 中需要初始化的表

--where WHERE\_CLAUSE

where语句,可以筛选需要输出的数据

--client\_id CLIENT\_ID

需要执行的Maxwell实例的client\_id

--comment COMMENT

初始化的描述

3.5.1 案例

  1. 可以直接对整个表初始化

bin/maxwell-bootstrap --user root --password 123456 --host 127.0.0.1 --client_id maxwell --database db_test --table maxwell_test

  1. 通过where来筛选数据

bin/maxwell-bootstrap --user root --password 123456 --host 127.0.0.1 --client_id maxwell --database db_test --table maxwell_test --where "my_date >= '2022-01-01 00:00:00'"

也可以通过SQL执行:

在生产环境,偶尔会遇到命令行初始化没有响应的现象,这种一般在数据量比较大的情况下容易出现。还有的同学可能有第三方系统需要和Maxwell初始进行对接,那解决方法就是操作 Maxwell 数据存储库的 bootstrap 表, 这里需要指定一下 client_id

mysql> insert into maxwell.bootstrap (database_name, table_name, client_id) values ('db_test', 'maxwell_test', 'maxwell_client_id');

如果需要定时启动Maxwell的数据初始化,可以配置 started_at 列。

mysql> insert into maxwell.bootstrap (database_name, table_name, client_id, started_at) values ('db_test', 'maxwell_test', 'maxwell_client_id', '2022-01-01 12:30:00');

3.5.2 异步与非异步

通过 bootstrapper 指定是否异步,--bootstrapper=sync 时,在处理全量数据同步时,会阻塞正常的binlog解析; --bootstrapper=async 时,不会阻塞

3.5.3 初始化数据格式

  • bootstrap  以type = "bootstrap-start" 事件开始, 以type = "bootstrap-complete" 事件结束,这两个事件的data字段为空,只是个标志事件。
  • 在bootstrap执行过程中,每行数据的事件为type = "bootstrap-insert"
  • 同时会穿插着其他标准事件,type = "insert", type = "update", type = "delete"

案例执行如下SQL:

mysql> create table db_test.maxwell_test(content varchar(255)); mysql> insert into db_test.maxwell_test (content) values ("hello"), ("test"); mysql> insert into maxwell.bootstrap (database_name, table_name) values ("db_test", "maxwell_test");

输出结果:

{"database":"db_test","table":"maxwell_test","type":"insert","ts":1450557598,"xid":13,"data":{"content":"hello"}} {"database":"db_test","table":"maxwell_test","type":"insert","ts":1450557598,"xid":13,"data":{"content":"test"}} {"database":"db_test","table":"maxwell_test","type":"bootstrap-start","ts":1450557744,"data":{}} {"database":"db_test","table":"maxwell_test","type":"bootstrap-insert","ts":1450557744,"data":{"content":"hello"}} {"database":"db_test","table":"maxwell_test","type":"bootstrap-insert","ts":1450557744,"data":{"content":"test"}} {"database":"db_test","table":"maxwell_test","type":"bootstrap-complete","ts":1450557744,"data":{}}

3.5.4 失败处理

如果在运行bootstrap的过程中maxwell崩溃或重启,那么bootstrap会重新开始,无论之前的进度是多少。也可以通过修改数据库中 bootstrapis_complete 字段为1,表示完成。

3.6 高可用

从1.29.1版本开始,maxwell包含了高可用模块,但是还在alpha版本,选举算法采用RAFT,所以最少需要3个节点。

创建raft.xml,并写入配置(官方文档写的复制,但是发行版里并没有示例文件)

mkdir raft.xml <?xml version='1.0' encoding='utf-8'?> <config xmlns="urn:org:jgroups"         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="urn:org:jgroups http://www.jgroups.org/schema/jgroups.xsd">     <UDP mcast_addr="228.8.8.8" mcast_port="${jgroups.udp.mcast_port:45588}"/>     <PING />     <MERGE3 />     <FD_SOCK/>     <FD_ALL/>     <VERIFY_SUSPECT timeout="1500"/>     <pbcast.NAKACK2 xmit_interval="500"/>     <UNICAST3 xmit_interval="500"/>     <pbcast.STABLE desired_avg_gossip="50000" max_bytes="4M"/>     <raft.NO_DUPES/>     <pbcast.GMS print_local_addr="true" join_timeout="2000"/>     <UFC max_credits="2M" min_threshold="0.4"/>     <MFC max_credits="2M" min_threshold="0.4"/>     <FRAG2 frag_size="60K"/>     <raft.ELECTION election_min_interval="500" election_max_interval="1000" heartbeat_interval="250"/>     <raft.RAFT members="A,B,C" raft_id="${raft_id:undefined}"/>     <raft.REDIRECT/> </config>

然后开启每个高可用节点

 bin/maxwell --ha --raft_member_id=A --config config/dev.properties --log_level=DEBUG  bin/maxwell --ha --raft_member_id=B --config config/dev.properties --log_level=DEBUG  bin/maxwell --ha --raft_member_id=C --config config/dev.properties --log_level=DEBUG

可以看到输出:

当关闭leader后,会重新进行选举保证高可用。

需要注意的是,每个节点需要保证 --replica_server_id--client_id 一致。

4.常见问题及注意事项

4.1 binlog丢失导致的异常

Maxwell 在 maxwell库中维护了binlog的位移等信息,如果maxwell库中binlog的信息和实际的无法匹配,则会导致maxwell异常,这时候需要手动修改binlog位移或者重建maxwell库。

如果你采集阿里云、腾讯云等云数据库上的MySQL需要注意,服务方通常会定时清理binlog, 这时候可能会导致Maxwell异常。如: https://github.com/zendesk/ma...

解决方案就是:

  1. 修改清理方案策略,保留最近XXX小时的数据。
  2. 出现异常后清空maxwell库,并重启Maxwell, 会自动重建数据库。

4.2 多个Maxwell

如果需要采集多个数据库、表,或者发送到多个Topic, 可以在同一个节点上,部署多个Maxwell。但是每个Maxwell实例都必须配置一个唯一的client\_id,以便对应不用的binlog。

参考文档

Maxwell官方文档

MySQL Binlog 解析工具 Maxwell 详解


我们是由一群数据从业人员,因为热爱凝聚在一起,以开源精神为基础,组成的PowerData数据之力社区。

可关注下方二维码点击“加入我们”,与PowerData一起成长


PowerData
1 声望2 粉丝

PowerData社区官方思否账号