HoRain云--详解canal同步MySQL增量数据到ES

  

🎬 HoRain云小助手个人主页

 🔥 个人专栏: 《Linux 系列教程》《c语言教程

⛺️生活的理想,就是为了理想的生活!


⛳️ 推荐

前些天发现了一个超棒的服务器购买网站,性价比超高,大内存超划算!忍不住分享一下给大家。点击跳转到网站。

专栏介绍

专栏名称

专栏介绍

《C语言》

本专栏主要撰写C干货内容和编程技巧,让大家从底层了解C,把更多的知识由抽象到简单通俗易懂。

《网络协议》

本专栏主要是注重从底层来给大家一步步剖析网络协议的奥秘,一起解密网络协议在运行中协议的基本运行机制!

《docker容器精解篇》

全面深入解析 docker 容器,从基础到进阶,涵盖原理、操作、实践案例,助您精通 docker。

《linux系列》

本专栏主要撰写Linux干货内容,从基础到进阶,知识由抽象到简单通俗易懂,帮你从新手小白到扫地僧。

《python 系列》

本专栏着重撰写Python相关的干货内容与编程技巧,助力大家从底层去认识Python,将更多复杂的知识由抽象转化为简单易懂的内容。

《试题库》

本专栏主要是发布一些考试和练习题库(涵盖软考、HCIE、HRCE、CCNA等)

img

以下是使用 Canal 同步 MySQL 增量数据到 Elasticsearch(ES)的详细流程及注意事项:


1. Canal 工作原理

Canal 通过模拟 MySQL Slave 协议,从 MySQL Master 读取 binlog 日志并解析,将增量数据(INSERT/UPDATE/DELETE)转换为结构化事件,最终将数据同步到下游存储(如 ES)。


2. 前置条件

2.1 MySQL 配置
  • 开启 binlog:确保 MySQL 的 binlog 已开启且为 ROW 模式
    # my.cnf
    [mysqld]
    log-bin=mysql-bin
    binlog-format=ROW
    server-id=1
    
  • 创建 Canal 账户
    CREATE USER 'canal'@'%' IDENTIFIED BY 'canal';
    GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';
    FLUSH PRIVILEGES;
    
2.2 安装 Canal
  • 下载 Canal Deployer 包
  • 解压并修改配置:
    # conf/canal.properties
    canal.serverMode = tcp  # 使用 TCP 模式
    canal.port = 11111      # Canal 服务端口
    
    # conf/example/instance.properties
    canal.instance.mysql.slaveId=1234       # 唯一 Slave ID
    canal.instance.master.address=127.0.0.1:3306
    canal.instance.dbUsername=canal
    canal.instance.dbPassword=canal
    canal.instance.filter.regex=.*\\..*     # 监控所有库表
    

3. 同步到 Elasticsearch

3.1 方案选择
  • 自定义开发:编写 Canal 客户端解析数据并写入 ES。
  • Canal Adapter:使用官方工具 Canal Adapter 直接配置 ES 映射。

3.2 方案一:自定义客户端开发
步骤 1:引入依赖
<!-- Canal 客户端 -->
<dependency>
    <groupId>com.alibaba.otter</groupId>
    <artifactId>canal.client</artifactId>
    <version>1.1.6</version>
</dependency>
<!-- ES Java Client -->
<dependency>
    <groupId>co.elastic.clients</groupId>
    <artifactId>elasticsearch-java</artifactId>
    <version>8.12.0</version>
</dependency>
步骤 2:Canal 客户端代码
public class CanalToESClient {
    public static void main(String[] args) {
        // 1. 连接 Canal
        CanalConnector connector = CanalConnectors.newSingleConnector(
            new InetSocketAddress("127.0.0.1", 11111), "example", "", "");
        connector.connect();
        connector.subscribe(".*\\..*"); // 订阅所有表

        // 2. 监听事件
        while (true) {
            Message message = connector.getWithoutAck(100); // 批量获取
            List<CanalEntry.Entry> entries = message.getEntries();
            if (entries.isEmpty()) {
                Thread.sleep(1000);
                continue;
            }

            for (CanalEntry.Entry entry : entries) {
                if (entry.getEntryType() == CanalEntry.EntryType.ROWDATA) {
                    CanalEntry.RowChange rowChange = CanalEntry.RowChange.parseFrom(entry.getStoreValue());
                    for (CanalEntry.RowData rowData : rowChange.getRowDatasList()) {
                        // 3. 解析事件类型(INSERT/UPDATE/DELETE)
                        CanalEntry.EventType eventType = rowChange.getEventType();
                        String tableName = entry.getHeader().getTableName();
                        List<CanalEntry.Column> columns = eventType == CanalEntry.EventType.DELETE ? 
                            rowData.getBeforeColumnsList() : rowData.getAfterColumnsList();

                        // 4. 构建 ES 文档
                        Map<String, Object> doc = new HashMap<>();
                        for (CanalEntry.Column column : columns) {
                            doc.put(column.getName(), column.getValue());
                        }

                        // 5. 写入 ES
                        IndexRequest request = new IndexRequest("your_index")
                            .id(doc.get("id").toString())
                            .source(doc);
                        // 使用 ElasticsearchClient 发送请求
                        elasticsearchClient.index(request);
                    }
                }
            }
            connector.ack(message.getId()); // 确认消费
        }
    }
}
步骤 3:处理数据映射
  • 字段类型转换:如 MySQL 的 DATETIME 转为 ES 的 date 类型。
  • 关联数据合并:如多表关联写入同一 ES 文档。

3.3 方案二:使用 Canal Adapter
步骤 1:配置 Adapter
# conf/es7/mytest_user.yml
dataSourceKey: defaultDS        # 数据源(对应 application.yml)
destination: example            # Canal 实例名
groupId:                        # 消费组(空表示单机)
esMapping:
  _index: "user_index"          # ES 索引名
  _id: "id"                     # 文档 ID(对应 MySQL 字段)
  sql: "SELECT id, name, age FROM user"
  commitBatch: 3000             # 批量提交大小
步骤 2:启动 Adapter
bin/startup.sh

4. 注意事项

  1. 幂等性:ES 写入需保证幂等(如使用 _id 避免重复插入)。
  2. 性能优化
    • 批量写入 ES(bulk API)。
    • 调整 Canal 客户端的 batchSize 和 ES 的刷新间隔。
  3. 数据一致性
    • 监控 Canal 位点(canal.meta)确保断点续传。
    • 使用事务或消息队列(如 Kafka)解耦 Canal 与 ES。
  4. DDL 同步:Canal 默认不处理 DDL,需手动更新 ES 索引映射。

5. 常见问题

Q1:Canal 无法连接 MySQL
  • 检查 MySQL 用户权限(需 REPLICATION SLAVE, REPLICATION CLIENT)。
  • 确认 binlog 已开启且为 ROW 模式。
Q2:ES 写入延迟高
  • 增加 Canal 客户端的批量处理大小(batchSize)。
  • 优化 ES 索引配置(如分片数、副本数)。
Q3:数据字段类型不匹配
  • 在 ES 中预定义索引映射(mappings),确保与 MySQL 字段类型兼容。

6. 扩展工具

  • Debezium:另一款流行的 CDC 工具,支持多数据库同步到 ES。
  • Logstash JDBC Input:通过定时轮询同步增量数据(非实时)。

❤️❤️❤️本人水平有限,如有纰漏,欢迎各位大佬评论批评指正!😄😄😄

💘💘💘如果觉得这篇文对你有帮助的话,也请给个点赞、收藏下吧,非常感谢!👍 👍 👍

🔥🔥🔥Stay Hungry Stay Foolish 道阻且长,行则将至,让我们一起加油吧!🌙🌙🌙

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值