使用 mysql binlog 监听数据库变化,优化缓存操作

  最近在项目中使用到缓存,感觉优雅的使用缓存工具比较麻烦,业界主流的缓存使用方法有先删除缓存在更新数据库,或者先更新数据库在删除缓存,只不过在业务代码中对数据操作不止一处,若想实现缓存数据的清除,必须在多处地方调用清除缓存的地方,麻烦不说,要是忘记某处数据更新未同步更新缓存数据,还会造成数据不一致的问题。

  前几天在研究数据库主从同步的时候,发现 mysql 的 binlog 对数据库的数据更新会做日志记录,那么只要监听数据库的更新,是不是代表着可以在一处统一维护数据和缓存的一致性。

  下面就简单介绍下 mysql 的 binlog

什么是 Binlog

Binlog 是 MySQL Server 维护的一种二进制日志,主要是用来记录对 MySQL 数据更新或潜在发生更新的 SQL 语句,并以"事务"的形式保存在磁盘中(文件)

主要用途

  • 1. 复制:MySQL 的 Master-Slave 协议,让 Slave 可以通过监听 Binlog 实现数据复制,达到数据一致的目的

  • 2. 数据恢复:通过 mysqlbinlog 工具恢复数据

  • 3. 增量备份

支持的格式

  • ROW

仅保存记录被修改细节,不记录 SQL 语句上下文相关信息:能非常清晰的记录下每行数据的修改细节,不需要记录上下文相关信息,因此不会发生某些特定情况下的 procedure、function、及 trigger 的调用触发无法被正确复制的问题,任何情况都可以被复制,且能加快从库重放日志的效率,保证从库数据的一致性。

  • STATEMENT

每一条会修改数据的 SQL 都会记录在 Binlog 中:只需要记录执行语句的细节和上下文环境,避免了记录每一行的变化,在一些修改记录较多的情况下相比 ROW 类型能大大减少 Binlog 日志量,节约IO,提高性能;还可以用于实时的还原;同时主从版本可以不一样,从服务器版本可以比主服务器版本高。

  • MIXED

以上两种类型的混合使用。经过前面的对比,可以发现 ROW 类型和 STATEMENT 类型各有优势,如能根据 SQL 语句取舍可能会有更好地性能和效果;MIXED 便是以上两种类型的结合。

Binlog 的相关命令

相关变量

-- Binlog 开关变量
mysql> show variables like 'log_bin';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_bin       | ON    |
+---------------+-------+
1 row in set (0.30 sec)

-- Binlog 日志的格式
mysql> show variables like 'binlog_format';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | ROW   |
+---------------+-------+
1 row in set (0.00 sec)

常用操作命令

SQL语句语句含义
show master logs;查看所有 Binlog 的日志列表
show master status;查看最后一个 Binlog 日志的编号名称,及最后一个事件结束的位置(pos)
flush logs;刷新 Binlog,此刻开始产生一个新编号的 Binlog 日志文件
reset master;清空所有的 Binlog 日志
show binlog events;查看第一个 Binlog 日志
show binlog events in ‘binlog.000030’;查看指定的 Binlog 日志
show binlog events in ‘binlog.000030’ from 931;从指定的位置开始,查看指定的 Binlog 日志
show binlog events in ‘binlog.000030’ from 931 limit 2;从指定的位置开始,查看指定的 Binlog 日志,限制查询的条数
show binlog events in ‘binlog.000030’ from 931 limit 1, 2;从指定的位置开始,带有偏移,查看指定的 Binlog 日志,限制查询的条数

Binlog 的 Event 类型

MySQL Binlog Event 类型有很多种(MySQL 官方定义了 36 种),例如:XID、TABLE_MAP、QUERY 等等。但是,我们需要了解的(也是最常用的)类型不是很多。

Event Type事件重要程度
QUERY_EVENT与数据无关的操作, begin、drop table、truncate table 等了解即可
XID_EVENT标记事务提交了解即可
TABLE_MAP_EVENT记录下一个操作所对应的表信息,存储了数据库名和表名非常重要
WRITE_ROWS_EVENT插入数据,即 insert 操作非常重要
UPDATE_ROWS_EVENT更新数据,即 update 操作非常重要
DELETE_ROWS_EVENT删除数据,即 delete 操作非常重要

最好的方式是在数据库中建一张测试表,然后对表进行增删改操作,再去查看下 Binlog 里面都有些什么,如下图所示
Binlog 示例

说明

对于 MySQL Binlog,我们可以不用过分追究 Binlog 里面到底包含了些什么,对于应用的话,我们最重要要搞清楚 Binlog 的 Event:每个 Event 包含 header 和 data 两个部分;header 提供了 Event 的创建时间,哪个服务器等信息,data 部分提供的是针对该 Event 的具体信息,如具体数据的修改。我们对 Binlog 的解析,即为对 Event 的解析。

  • Binlog 的 EventType (需要注意,不同版本的 MySQL,EventType 可能会不同)

  • Binlog 中并不会打印数据表的列名

既然了解了 binlog 那么就说说 binlog在项目中如何使用:

mysql-binlog-connector-java (监听解析 Binlog 的开源工具)

地址说明

  • github 地址

mysql-binlog-connector-java

  • Maven 仓库地址
<dependency>
	<groupId>com.github.shyiko</groupId>
	<artifactId>mysql-binlog-connector-java</artifactId>
	<version>0.21.0</version>
</dependency>

使用说明

首先,我们需要清楚,我们的目的是实现对 MySQL 数据表状态的变更有所感知,也就是能够实现对 Binlog 的监听、并解析成我们 ”想要的格式(Java 对象)”。

  • 简单使用
import com.github.shyiko.mysql.binlog.BinaryLogClient;
import com.github.shyiko.mysql.binlog.event.*;
import com.github.shyiko.mysql.binlog.event.deserialization.EventDeserializer;

import java.io.IOException;

/**
 * @author gaobo
 * @version 1.0.0
 * @ClassName Test
 * @Description TODO
 * @Date 2020/8/2 22:14
 */
public class Test {

    public static void main(String[] args) {
        BinaryLogClient client = new BinaryLogClient("127.0.0.1", 3306, "root", "123456");
        EventDeserializer eventDeserializer = new EventDeserializer();
        eventDeserializer.setCompatibilityMode(
                EventDeserializer.CompatibilityMode.DATE_AND_TIME_AS_LONG,
                EventDeserializer.CompatibilityMode.CHAR_AND_BINARY_AS_BYTE_ARRAY
        );

        new Thread(() -> {
            client.registerEventListener(event -> {
                final EventData data = event.getData();
                if (data instanceof WriteRowsEventData) {
                    WriteRowsEventData writeRowsEventData = (WriteRowsEventData) data;
                    System.out.println(writeRowsEventData);
                } else if (data instanceof UpdateRowsEventData) {
                    UpdateRowsEventData updateRowsEventData = (UpdateRowsEventData) data;
                    System.out.println(updateRowsEventData);
                } else if (data instanceof DeleteRowsEventData) {
                    DeleteRowsEventData deleteRowsEventData = (DeleteRowsEventData) data;
                    System.out.println(deleteRowsEventData);
                }
            });
            try {
                client.connect();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
    }
}
  • 看一看 client 监听到的 event 里面都有啥
public class Event implements Serializable {

    // Binlog 中的两个最核心的元素:header 和 data
    private EventHeader header;
    // 这里需要注意,EventData 是个接口,不同的 EventType 对应到不同的实现,例如:WriteRowsEventData、UpdateRowsEventData 等等
    private EventData data;

    // data.toString() 打印的格式在这里定义的
    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder();
        sb.append("Event");
        sb.append("{header=").append(header);
        sb.append(", data=").append(data);
        sb.append('}');
        return sb.toString();
    }
}
  • 自定义 Binlog 的监听器

目前已经知道了 BinaryLogClient 的基础用法,要实现监听解析 Binlog,我们只需要定义监听器即可,这就要去实现 BinaryLogClient.EventListener 接口

public interface EventListener {

    // 只需要实现 onEvent 方法,然后自定义解析 Event 的方法即可
    // 核心思想是两个步骤:
    //    1. 当前的 Event 是否需要处理 -- 对 header 部分的处理
    //    2. 怎么去处理  -- 对 data 部分的处理
    void onEvent(Event event);
}

看起来是不是很简单了,那么下一篇 我们就进入实战代码中,使用binlog优雅的实现数据库监听

 

注:下文中的 *** 代表文件名中的组件名称。 # 包含: 中文-英文对照文档:【***-javadoc-API文档-中文(简体)-英语-对照版.zip】 jar包下载地址:【***.jar下载地址(官方地址+国内镜像地址).txt】 Maven依赖:【***.jar Maven依赖信息(可用于项目pom.xml).txt】 Gradle依赖:【***.jar Gradle依赖信息(可用于项目build.gradle).txt】 源代码下载地址:【***-sources.jar下载地址(官方地址+国内镜像地址).txt】 # 本文件关键字: 中文-英文对照文档,中英对照文档,java,jar包,Maven,第三方jar包,组件,开源组件,第三方组件,Gradle,中文API文档,手册,开发手册,使用手册,参考手册 # 使用方法: 解压 【***.jar中文文档.zip】,再解压其中的 【***-javadoc-API文档-中文(简体)版.zip】,双击 【index.html】 文件,即可用浏览器打开、进行查看。 # 特殊说明: ·本文档为人性化翻译,精心制作,请放心使用。 ·本文档为双语同时展示,一行原文、一行译文,可逐行对照,避免了原文/译文来回切换的麻烦; ·有原文可参照,不再担心翻译偏差误导; ·边学技术、边学英语。 ·只翻译了该翻译的内容,如:注释、说明、描述、用法讲解 等; ·不该翻译的内容保持原样,如:类名、方法名、包名、类型、关键字、代码 等。 # 温馨提示: (1)为了防止解压后路径太长导致浏览器无法打开,推荐在解压时选择“解压到当前文件夹”(放心,自带文件夹,文件不会散落一地); (2)有时,一套Java组件会有多个jar,所以在下载前,请仔细阅读本篇描述,以确保这就是你需要的文件;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值