es 2.x后官方废弃jdbc river插件,因其轮询查表延迟高、锁表风险大且无法捕获delete和ddl;实时同步必须基于binlog解析,推荐canal(轻量、中文友好)或debezium(kafka生态、事务支持严谨)。

为什么不能直接用ES的JDBC River插件
ES 2.x 之后官方就废弃了 jdbc-river,现在连社区维护版都极难适配新版 ES(比如 8.x)和高版本 MySQL(如 8.0+ 的默认认证插件 caching_sha2_password)。它本质是轮询查表,延迟高、锁表风险大、无法捕获 DELETE 和 DDL。真正实时同步必须走 Binlog 解析路线。
选 Canal 还是 Debezium?关键看你的技术栈
两者都基于 MySQL Binlog 的增量订阅,但定位不同:
-
Canal是阿里开源的纯 Java 实现,轻量、文档中文友好,适合 Spring Boot + MySQL + ES 简单链路;它模拟从库协议,要求 MySQL 开启binlog_format=ROW和binlog_row_image=FULL -
Debezium是 Kafka 生态一员,必须依赖Kafka或Kafka Connect,适合已有流平台的场景;它对 DDL、多表关联、事务边界支持更严谨,但部署复杂度翻倍 - 如果只是单业务库单向同步,
Canal+canal-server+ 自研 client(或用canal-adapter)更快落地;若已用 Kafka,优先选Debezium避免重复造轮子
Canal 同步到 ES 时字段类型不匹配怎么办
MySQL 的 DATETIME、TINYINT(1)、JSON 类型在 ES 中没有直接对应,硬映射会报 mapper_parsing_exception。必须在写入前做转换:
-
DATETIME→ ES 的date类型:client 端用LocalDateTime.parse()转成 ISO 格式字符串(如"2024-05-20T14:23:11"),ES mapping 中显式声明"format": "strict_date_optional_time" -
TINYINT(1)(常作布尔)→ ES 的boolean:不要传1/0,client 中转成true/false再写入 -
JSON字段 → ES 的object或keyword:若内容固定结构,mapping 定义为object;若只用于检索(如 tags 数组),建议预处理成keyword数组,避免 dynamic mapping 带来类型冲突
漏掉这些转换,ES 会在第一次写入后固化错误类型,后续同名字段写入直接失败,必须删 index 重建 mapping。
DELETE 操作同步失败的常见原因
Canal 默认能捕获 DELETE 事件,但 client 端常忽略 RowData.getBeforeColumnsList() —— 这才是 DELETE 的主键值来源。错误写法是只读 afterColumns,结果删不了 ES 文档。
正确逻辑是:
- INSERT/UPDATE:用
afterColumns构建 ES document,_id取主键列值 - DELETE:用
beforeColumns提取主键,调用 ES 的delete by id接口(如RestHighLevelClient.delete()) - 务必校验
beforeColumns是否为空——某些 Binlog 配置下(如binlog_row_image=MINIMAL),DELETE 可能只含主键,其他字段为 null
没处理好这个分支,ES 里就会残留大量“幽灵数据”,且无日志报错,只能靠定期比对 count 差异才发现。
Binlog 解析不是黑盒,每个 event 的字段完整性、MySQL 版本对 GTID 的影响、网络断连后的位点恢复逻辑,都得在 client 层有明确兜底。别指望中间件自动搞定一致性。











