如何实现实时监听mysql数据库变化

Debezium官方地址:

https://docs.redhat.com/zh-cn/documentation/red_hat_build_of_debezium/2.3.7/html/debezium_user_guide/index

Debezium 是一组分布式服务,用于捕获数据库中的行级更改,以便您的应用程序能够查看和响应这些更改。Debezium 记录提交到每个数据库表的所有行级更改。每个应用程序都会读取感兴趣的事务日志,以按照发生的顺序查看所有操作。

Debezium就是监听数据库的binlog(前提是mysql要打开binlog,其他数据库也一样),从而实现感知数据变化,然后通知我们的应用。

Debezium可以监听常见的数据库,有Mysql、Postgres、Mongodb、Oracle、SqlServer、Db2等,debezium其中对应的概念叫连接器,不同的数据库用不同的连接器实现监听。

架构描述

如图中所示,部署了 MySQL 和 PostgresSQL 的 Debezium 连接器,以捕获对这两种类型的数据库的更改。每个 Debezium 连接器建立与源数据库的连接:

  • MySQL 连接器使用客户端库来访问 binlog。

  • PostgreSQL 连接器从逻辑复制流读取。

  • Kafka Connect 作为 Kafka 代理之外的独立服务运行。

默认情况下,从一个数据库表的更改写入 Kafka 主题,其名称对应于表名称。如果需要,可以通过配置 Debezium 的主题 路由转换来调整目标主题名称。例如,:

将记录路由到名称与表名称不同的主题

流将多个表的事件记录改为单个主题

在 Apache Kafka 中更改了事件记录后,Kafka Connect eco-system 的不同连接器可以将记录流传输到其他系统和数据库,如 Elasticsearch、数据仓库和分析系统或缓存,如 Infinispan。根据所选的 sink 连接器,您可能需要配置 Debezium 的新记录状态提取转换。此 Kafka Connect SMT 将 after 结构从 Debezium 的更改事件传播到接收器连接器。

mysql连接器应用

以SpringBoot项目为例,整合debezium实现数据库的监听,然后再处理下游的数据动作。

一、导入maven依赖:

<debezium.version>1.5.2.Final</debezium.version>
<dependency>      <groupId>io.debezium</groupId>      <artifactId>debezium-api</artifactId>      <version>${debezium.version}</version></dependency>
<dependency>      <groupId>io.debezium</groupId>      <artifactId>debezium-embedded</artifactId>      <version>${debezium.version}</version></dependency>
<dependency>      <groupId>io.debezium</groupId>      <artifactId>debezium-connector-mysql</artifactId>      <version>${debezium.version}</version></dependency>

二、将数据库的信息配置到yml配置文件中

debezium:  mysql:      ## name可以自己起一个  name: mysql-connector      ## 连接器类型  connector-class: io.debezium.connector.mysql.MySqlConnector      database-hostname: 你的数据库地址     database-port: 端口      database-user: 用户  database-password: 密码      database-server-name: customer-mysql-db-server      database-history: io.debezium.relational.history.FileDatabaseHistory      database-history-filename: E:/google download/spring-boot-debezium-master/dbhistory.dat      offset-storage: org.apache.kafka.connect.storage.FileOffsetBackingStore      offset-storage-filename: E:/google download/spring-boot-debezium-master/offsets.dat      offset-flush-interval-ms: 6000     ## 要监听哪些表,可以是多个,database.table的格式   table-include-list: db.sys_user,db.risk      include-schema-changes: false      database-server-id: 1

三,配置类读取配置的信息

@Configuration@ConfigurationProperties(prefix = "debezium.mysql")@Data@Slf4jpublic class MysqlDebeziumConfiguration {    private String name;    private String connectorClass;    private String databaseHostname;    private String databasePort;    private String databaseUser;    private String databasePassword;    private String databaseServerName;    private String databaseHistory;    private String databaseHistoryFilename;    private String offsetStorage;    private String offsetStorageFilename;    private int offsetFlushIntervalMs;    private String tableIncludeList;    private String includeSchemaChanges;    private String databaseServerId;    public Properties buidProperties() {          Properties pros = new Properties();          pros.put("name", getName());          pros.put("connector.class", getConnectorClass());          pros.put("database.hostname", getDatabaseHostname());          pros.put("database.history", getDatabaseHistory());          pros.put("database.port", getDatabasePort());          pros.put("database.user", getDatabaseUser());          pros.put("database.password", getDatabasePassword());//          pros.put("database.server.id", getDatabaseServerId());          pros.put("database.history.file.filename", getDatabaseHistoryFilename());          pros.put("database.server.name", getDatabaseServerName());          pros.put("offset.storage", getOffsetStorage());          pros.put("offset.storage.file.filename", getOffsetStorageFilename());          pros.put("offset.flush.interval.ms", getOffsetFlushIntervalMs());        // mysql中是[database.tableName],多个表以,分割          pros.put("table.include.list", getTableIncludeList());          pros.put("include.schema.changes", getIncludeSchemaChanges());          return pros;    }    
private static void checkFileExists(String absolutePath) {          String dir = absolutePath.substring(0, absolutePath.lastIndexOf("/"));          String fileName = absolutePath.substring(absolutePath.lastIndexOf("/") + 1);          System.out.println("file name" + fileName);          System.out.println("dir " + dir);          File dirFile = new File(dir);          if (!dirFile.exists()) {                dirFile.mkdirs();          }          File file = new File(absolutePath);          if (!file.exists()) {                try {                      boolean res = file.createNewFile();                      log.info("{} 文件不存在,新建文件:{}", absolutePath, res);              } catch (IOException e) {                  log.info("{} 文件新建失败, msg: {}", absolutePath, e.getMessage());                  throw new RuntimeException(e);              }        }    }}

四,编写一个类,用来启动debezium引擎

@Datapublic class DebeziumServerBootstrap implements InitializingBean, SmartLifecycle {    private final Executor executor = Executors.newSingleThreadExecutor();    private DebeziumEngine<?> debeziumEngine;    @Override    public void start() {          executor.execute(debeziumEngine);    }    @SneakyThrows    @Override    public void stop() {          debeziumEngine.close();    }    
@Override    public boolean isRunning() {          return false;    }    
@Override    public void afterPropertiesSet() throws Exception {          Assert.notNull(debeziumEngine, "debeziumEngine must not be null");    }
}

五,将其注册为一个bean,让spring容器维护运行。

@Configurationpublic class DebeziumConfiguration {    @Autowired    private MysqlDebeziumConfiguration debeziumConfiguration;    
@Bean    DebeziumServerBootstrap debeziumServerBootstrap() {          DebeziumServerBootstrap debeziumServerBootstrap = new DebeziumServerBootstrap();          DebeziumEngine<RecordChangeEvent<SourceRecord>> debeziumEngine = DebeziumEngine.create(ChangeEventFormat.of(Connect.class))                    .using(debeziumConfiguration.buidProperties())                    .notifying(DebeziumHandler::notifyEvent)                    .build();        debeziumServerBootstrap.setDebeziumEngine(debeziumEngine);        return debeziumServerBootstrap;    }}

六,最后封装一下发送的动作事件

public class DebeziumHandler {    public static void notifyEvent(List<RecordChangeEvent<SourceRecord>> recordChangeEvents, DebeziumEngine.RecordCommitter<RecordChangeEvent<SourceRecord>> recordCommitter) {          recordChangeEvents.forEach(r -> {                System.out.println("========================change = " + r);                SourceRecord sourceRecord = r.record();                System.out.println("========================sourceRecord = " + sourceRecord);                Struct sourceRecordChangeValue = (Struct) sourceRecord.value();                if (sourceRecordChangeValue != null) {                // 判断操作的类型 过滤掉读 只处理增删改   这个其实可以在配置中设置                      Envelope.Operation operation = Envelope.Operation.forCode((String) sourceRecordChangeValue.get(OPERATION));                      if (operation != Envelope.Operation.READ) {                            String record = operation == Envelope.Operation.DELETE ? BEFORE : AFTER;                    // 获取增删改对应的结构体数据                            Struct struct = (Struct) sourceRecordChangeValue.get(record);                    // 将变更的行封装为Map                            Map<String, Object> payload = struct.schema().fields().stream()                                      .map(Field::name)                                      .filter(fieldName -> struct.get(fieldName) != null)                                      .map(fieldName -> Pair.of(fieldName, struct.get(fieldName)))                                      .collect(toMap(Pair::getKey, Pair::getValue));                    // 这里简单打印一下                              System.out.println(operation+ ":payload = " + payload);                        }                  }          });    }}

最后启动项目运行一下,手动去更改一下数据库中的数据,就可以看到此时已经监听到变化了,就会有相应的日志打印。

如果删除或者更新数据库中的数据,就会通知我们的应用程序,打印相应的日志

总结

Debezium对于监听常见数据库的数据变化是非常适合的,也可以方便的集成进我们自己的项目,帮我们处理了复杂的过程,我们只需要简单的做好下游数据处理即可。

原文:如何实现实时监听mysql数据库变化Mysql数据库实现实时数据监听,实现动态实时数据处理icon-default.png?t=O83Ahttps://mp.weixin.qq.com/s/wPRW4E0e_RpKvVLsTtm6VQ

在Spring Boot中监听MySQL数据库变化通常是通过监听数据库的事务提交或行级别的变更通知。一种常见的实现方式是使用第三方库,例如`mybatis-spring-boot-starter`配合`Liquibase`或者`Flyway`进行版本控制,以及`MyBatis`的监听功能。这里我们以`MyBatis`为例: 1. **添加依赖**: 在`pom.xml`中添加MyBatis、MyBatis-Spring和SQL监听器相关的依赖: ```xml <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> ``` 2. **配置MyBatis**: 在`application.yml` 或 `application.properties` 中配置Druid和MyBatis: ```yaml # Druid配置 druid: ... (其他配置) filters: stat,wall mybatis: type-aliases-package: com.example.yourpackage.entity sqlSessionFactoryBeanName: sqlSessionFactory ``` 3. **启用监听器**: 在`SqlSessionTemplate`中启用监听器,通常在`@Configuration`类中设置: ```java import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.event.SessionEventListener; @Configuration public class MybatisConfig { @Autowired private SqlSessionFactory sqlSessionFactory; @PostConstruct public void init() { ((SqlSession)sqlSessionFactory.openSession()).getConfiguration().addEventListeners(new MybatisEventListener()); } private static class MybatisEventListener implements SessionEventListener { @Override public void sessionCreated(SqlSession sqlSession) { // 注册事件处理器 sqlSession.getConfiguration().addMapper(MyMapper.class); // 替换为你的mapper接口 } @Override public void statementExecuted(SqlSession sqlSession, StatementProxy statement) { // 可能需要在这里获取执行的SQL,检查是否涉及更新操作 } @Override public void statementFinished(SqlSession sqlSession, StatementProxy statement) { // 如果是更新操作,触发更新后的业务逻辑 if (isUpdateStatement(statement)) { updateAfterInsertOrUpdate(sqlSession); } } private boolean isUpdateStatement(StatementProxy statement) { String sql = statement.getActualSql(); return sql.contains("UPDATE"); // 根据实际需求调整条件 } private void updateAfterInsertOrUpdate(SqlSession sqlSession) { // 执行后续更新UI或通知逻辑 User updatedUser = ... // 获取更新后的用户对象 sendWebSocketNotification(updatedUser); } } } ``` 注意这只是一个基本示例,实际应用中可能需要根据具体需求来定制监听逻辑。此外,如果你使用的是JPA,可以考虑使用`@EntityChange`注解或者第三方库如`Auditing`实现类似功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值