文章目录
1、背景
最近在开发的过程中遇到这么一个问题,当产生某种类型的工单后,需要实时通知到另外的系统,由另外的系统进行数据的研判操作。
由于某种原因, 像向消息队列中推送工单消息、或直接调用另外系统的接口、或者部署Cannal
等都不可行,因此此处使用 mysql-binlog-connector-java
这个库来完成数据库binlog
的监听,从而通知到另外的系统。
2、mysql-binlog-connector-java简介
mysql-binlog-connector-java
是一个Java库,通过它可以实现mysql binlog
日志的监听和解析操作。它提供了一系列可靠的方法,使开发者通过监听数据库的binlog日志,来实时
获取数据库的变更信息,比如:数据的插入
、更新
、删除
等操作。
github地址 https://github.com/osheroff/mysql-binlog-connector-java
3、准备工作
1、验证数据库是否开启binlog
mysql> show variables like '%log_bin%';
+---------------------------------+------------------------------------+
| Variable_name | Value |
+---------------------------------+------------------------------------+
| log_bin | ON |
| log_bin_basename | /usr/local/mysql/data/binlog |
| log_bin_index | /usr/local/mysql/data/binlog.index |
| log_bin_trust_function_creators | OFF |
| log_bin_use_v1_row_events | OFF |
| sql_log_bin | ON |
+---------------------------------+------------------------------------+
log_bin
的值为 ON
时,表示开启了binlog
2、开启数据库的binlog
# 修改 my.cnf 配置文件
[mysqld]
#binlog日志的基本文件名,需要注意的是启动mysql的用户需要对这个目录(/usr/local/var/mysql/binlog)有写入的权限
log_bin=/usr/local/var/mysql/binlog/mysql-bin
# 配置binlog日志的格式
binlog_format = ROW
# 配置 MySQL replaction 需要定义,不能和已有的slaveId 重复
server-id=1
3、创建具有REPLICATION SLAVE权限的用户
CREATE USER binlog_user IDENTIFIED BY 'binlog#Replication2024!';
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'binlog_user'@'%';
FLUSH PRIVILEGES;
4、事件类型 eventType 解释
注意:不同的mysql版本事件类型可能不同,我们本地是mysql8
TABLE_MAP: 在表的 insert、update、delete 前的事件,用于记录操作的数据库名和表名。
EXT_WRITE_ROWS: 插入数据事件类型,即 insert 类型
EXT_UPDATE_ROWS: 插入数据事件类型,即 update 类型
EXT_DELETE_ROWS: 插入数据事件类型,即 delete 类型
ROTATE: 当mysqld切换到新的二进制日志文件时写入。当发出一个FLUSH LOGS 语句。或者当前二进制日志文件超过max_binlog_size。
1、TABLE_MAP 的注意事项
一般情况下,当我们向数据库中执行insert
、update
或delete
事件时,一般会先有一个TABLE_MAP
事件发出,通过这个事件,我们就知道当前操作的是那个数据库和表。 但是
如果我们操作的表上存在触发器时,那么可能顺序就会错乱,导致我们获取到错误的数据库名和表名。
2、获取操作的列名
此处以 EXT_UPDATE_ROWS
事件为列,当我们往数据库中update
一条记录时,触发此事件,事件内容为:
Event{
header=EventHeaderV4{
timestamp=1727498351000, eventType=EXT_UPDATE_ROWS, serverId=1, headerLength=19, dataLength=201, nextPosition=785678, flags=0}, data=UpdateRowsEventData{
tableId=264, includedColumnsBeforeUpdate={
0, 1, 2, 3, 4, 5, 6, 7}, includedColumns={
0, 1, 2, 3, 4, 5, 6, 7}, rows=[
{
before=[1, zhangsan, 张三-update, 0, [B@7b720427, [B@238552f, 1727524798000, 1727495998000], after=[1, zhangsan, 张三-update, 0, [B@21dae489, [B@2c0fff72, 1727527151000, 1727498351000]}
]}}
从上面的语句中可以看到includedColumnsBeforeUpdate
和includedColumns
这2个字段表示更新前的列名和更新后的列名,但是这个时候展示的数字,那么如果展示具体的列名呢?
可以通过information_schema.COLUMNS
获取。
5、监听binlog的position
1、从最新的binlog位置开始监听
默认情况下,就是从最新的binlog位置开始监听。
BinaryLogClient client = new BinaryLogClient(hostname, port, username, password);
2、从指定的位置开始监听
BinaryLogClient client = new BinaryLogClient(hostname, port, username, password);
// binlog的文件名
client.setBinlogFilename("");
// binlog的具体位置
client.setBinlogPosition(11);
3、断点续传
这个指的是,当我们的 mysql-binlog-connector-java
程序宕机后,如果数据发生了binlog的变更,我们应该从程序上次宕机的位置的position进行监听,而不是程序重启后从最新的binlog position位置开始监听。默认情况下mysql-binlog-connector-java
程序没有为我们实现,需要我们自己去实现。大概的实现思路为:
- 监听
ROTATE
事件,可以获取到最新的binlog文件名和位置。 - 记录每个事件的position的位置。
6、创建表和准备测试数据
CREATE TABLE `binlog_demo`
(
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键',
`user_name` varchar(64) DEFAULT NULL COMMENT '用户名',
`nick_name` varchar(64) DEFAULT NULL COMMENT '昵称',
`sex` tinyint DEFAULT NULL COMMENT '性别 0-女 1-男 2-未知',
`address` text COMMENT '地址',
`ext_info` json DEFAULT NULL COMMENT '扩展信息',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` timestamp NULL DEFAULT NULL COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uidx_username` (`user_name`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='测试binlog'
-- 0、删除数据
truncate table binlog_demo;
-- 1、添加数据
insert into binlog_demo(user_name, nick_name, sex, address, ext_info, create_time, update_time)
values ('zhangsan', '张三', 1, '地址', '[
"aaa",
"bbb"
]',