3875 字
约 12 分钟
1
MySQL 数据为什么要同步到 Elasticsearch?全量、增量同步与常见

MySQL 数据为什么要同步到 Elasticsearch?全量、增量同步与常见方案

在实际项目中,我们经常会看到这样的架构:

MySQL → Elasticsearch

甚至:

MySQL → Canal → MQ → Elasticsearch

第一次看到可能会产生几个疑问:

  • 数据已经存在 MySQL 了,为什么还要存一份到 ES?
  • 什么是全量同步?
  • 什么是增量同步?
  • Canal 是干什么的?
  • RabbitMQ / Kafka 又起什么作用?
  • MySQL 和 ES 数据不一致怎么办?

本文用一个电商商品搜索的例子,把这些问题串起来。


一、为什么要把 MySQL 数据同步到 ES?

先记住一句话:

MySQL 负责可靠地存储业务数据,Elasticsearch 负责快速、灵活地搜索数据。

例如 MySQL 中有一张商品表:

CREATE TABLE product (
    id BIGINT PRIMARY KEY,
    name VARCHAR(255),
    description TEXT,
    brand VARCHAR(100),
    price DECIMAL(10, 2),
    stock INT
);

用户需要搜索:

苹果手机

简单情况下,可以直接使用 MySQL:

SELECT *
FROM product
WHERE name LIKE '%苹果手机%';

但是随着业务越来越复杂,可能需要:

关键词搜索
+ 分词
+ 多字段搜索
+ 相关度排序
+ 品牌筛选
+ 价格筛选
+ 搜索高亮
+ 搜索建议

这类搜索场景正是 Elasticsearch 擅长的。

因此系统通常会变成:

                  写数据
                    ↓
                  MySQL
              业务主数据源
                    │
                    │ 同步
                    ▼
             Elasticsearch
                搜索索引
                    ↑
                    │
                 用户搜索

这里要特别注意:

ES 通常不是 MySQL 的替代品。

一般认为:

MySQL = Source of Truth
ES    = 为搜索建立的数据副本

例如:

MySQL

id = 1001
name = iPhone 17 Pro
price = 7999

ES 中可能也保存:

{
  "id": 1001,
  "name": "iPhone 17 Pro",
  "price": 7999
}

用户下单、修改商品等核心业务操作 MySQL。

用户搜索商品时查询 ES。


二、MySQL → ES 的核心问题:数据同步

既然一份数据同时存在:

MySQL
+
Elasticsearch

就必须解决一个问题:

MySQL 数据变化以后,ES 怎么跟着变化?

例如:

UPDATE product
SET price = 6999
WHERE id = 1001;

此时 MySQL:

price = 6999

但是 ES 如果还是:

price = 7999

用户搜索出来的价格就是错误的。

所以需要:

MySQL
   │
   │ 数据同步
   ▼
Elasticsearch

数据同步通常又分为:

全量同步
增量同步

三、什么是全量同步?

全量同步非常好理解:

把 MySQL 中符合条件的所有数据重新同步到 ES。

例如 MySQL 中存在 100 万条商品:

MySQL

商品 1
商品 2
商品 3
...
商品 1000000

全量同步就是:

MySQL
  │
  │ 查询所有商品
  ▼
同步程序
  │
  ▼
ES

最终重新构建完整的商品索引。

简单来说就是:

100 万条数据

全部重新同步一次

全量同步适合什么时候?

最典型的场景是:

1. 第一次初始化 ES

项目之前只有 MySQL,现在第一次引入 Elasticsearch。

ES 是空的:

MySQL:100 万商品
ES:0 条

这时候必须执行一次全量同步。


2. ES 索引需要重建

例如修改了 ES Mapping:

旧索引
 ↓
删除 / 新建索引
 ↓
重新从 MySQL 导入

这时也需要全量同步。


3. 数据严重不一致

如果因为程序 Bug 等原因导致:

MySQL
和
ES

大量数据对不上,可以重新全量构建索引。


四、全量同步为什么不能每次都做?

假设数据库只有:

1000 条

全部同步可能没什么问题。

但如果:

1000 万条商品

今天只有一件商品价格发生变化:

商品 1001

7999 → 6999

为了这一条数据重新同步:

1000 万条

显然非常浪费。

所以实际系统不能只依赖全量同步。

于是就有了:

增量同步


五、什么是增量同步?

增量同步就是:

只同步发生变化的数据。

假设:

MySQL:1000 万商品

今天只有:

商品 1001 修改价格
商品 2002 修改名称
商品 3003 被删除

那么只需要同步:

1001
2002
3003

而不是重新同步 1000 万条。

所以:

全量同步
=
把所有数据重新同步

增量同步
=
只同步新增 / 修改 / 删除的数据

这两个概念一定要区分开。


六、增量同步需要监听哪些操作?

主要就是数据库的:

INSERT
UPDATE
DELETE

例如:

INSERT INTO product ...;

ES:

新增 Document

MySQL:

UPDATE product ...;

ES:

更新 Document

MySQL:

DELETE FROM product WHERE id = 1001;

ES:

删除 Document

因此增量同步的核心问题其实变成了:

怎么知道 MySQL 哪些数据发生了变化?

这就产生了不同的同步方案。


七、方案一:业务代码直接同步 ES

最简单的方法:

业务服务
   │
   ├── 更新 MySQL
   │
   └── 更新 ES

例如:

public void updateProduct(Product product) {

    productMapper.updateById(product);

    elasticsearchRepository.save(product);
}

流程:

修改商品
   ↓
更新 MySQL
   ↓
更新 ES

优点

非常简单。

小项目特别容易实现。

缺点

业务代码和 ES 强耦合。

而且存在一个很麻烦的问题:

MySQL 更新成功
       ↓
ES 更新失败

最终:

MySQL = 新数据
ES    = 旧数据

所以稍微复杂一些的项目通常不会直接这么干。


八、方案二:MySQL + MQ + ES

更常见的一种方式:

业务服务
   ↓
MySQL
   ↓
发送 MQ
   ↓
RabbitMQ / Kafka
   ↓
ES 同步消费者
   ↓
Elasticsearch

例如商品修改完成:

productMapper.updateById(product);

rabbitTemplate.convertAndSend(
    "product.exchange",
    "product.updated",
    product.getId()
);

发送:

{
  "productId": 1001,
  "event": "PRODUCT_UPDATED"
}

消费者收到:

productId = 1001

然后:

查询 MySQL 最新数据
       ↓
更新 ES

结构变成:

Product Service
      ↓
    MySQL
      ↓
 RabbitMQ
      ↓
ES Consumer
      ↓
Elasticsearch

为什么使用 MQ?

主要是为了:

异步
解耦
削峰
失败重试

业务服务不需要等待 ES 更新完成以后才能返回。

但是这种方案依然有一个经典问题:

更新 MySQL 成功
       ↓
准备发送 MQ
       ↓
服务突然宕机

结果:

MySQL 更新成功
MQ 消息没发送
ES 没更新

还是可能不一致。

因此又出现了一种非常常见的方案:

Canal / CDC


九、方案三:Canal + MQ + ES

Canal 的核心思想非常简单:

不让业务代码主动告诉我们 MySQL 发生了变化,而是直接监听 MySQL 的 binlog。

整体结构:

业务服务
   ↓
MySQL
   ↓
binlog
   ↓
Canal
   ↓
RabbitMQ / Kafka
   ↓
同步消费者
   ↓
Elasticsearch

十、什么是 binlog?

binlog 可以简单理解成:

MySQL 记录数据变化的日志。

例如执行:

UPDATE product
SET price = 6999
WHERE id = 1001;

MySQL 完成修改以后,会产生对应的 binlog 变化记录。

Canal 就可以监听这些变化。

因此:

MySQL
  │
  │ binlog
  ▼
Canal

Canal 能发现:

product 表发生 UPDATE

id = 1001

price:
7999 → 6999

然后将这个变化继续发送出去。


十一、Canal 和 MQ 是不是重复了?

不是。

这是非常容易混淆的地方。

可以这样理解:

Canal
↓
负责发现 MySQL 发生了什么变化

MQ
↓
负责把变化事件传递出去

Consumer
↓
负责处理变化

ES
↓
保存用于搜索的数据

完整流程:

             MySQL
               │
            binlog
               │
               ▼
             Canal
               │
         捕获数据变化
               │
               ▼
         RabbitMQ / Kafka
               │
          传递变化事件
               │
               ▼
          ES Consumer
               │
               ▼
        Elasticsearch

所以 Canal 和 RabbitMQ 是两个不同角色。


十二、Canal 属于什么技术?

Canal 本质上属于:

CDC

全称:

Change Data Capture

中文:

变更数据捕获

CDC 的核心思想就是:

捕获数据库发生的 INSERT、UPDATE、DELETE 等变化,然后把这些变化同步到其他系统。

因此除了:

MySQL → ES

还可以:

MySQL → 数据仓库

MySQL → Redis

MySQL → Kafka

MySQL → 其他数据库

Canal 只是 CDC 的一种实现方案。

常见的 CDC / 数据同步技术还包括:

Canal
Debezium
Flink CDC

十三、另一种简单增量方案:根据更新时间扫描

还有一种很好理解的方法。

商品表增加:

update_time DATETIME

然后同步程序记录:

上次同步时间:

2026-09-01 10:00:00

下一次查询:

SELECT *
FROM product
WHERE update_time > '2026-09-01 10:00:00';

假设查到:

商品 1001
商品 2002
商品 3003

只同步这些数据。

然后更新同步时间。

结构:

定时任务
   ↓
查询 update_time
   ↓
找出变化数据
   ↓
同步 ES

优点是:

简单
容易实现

但是也有不少问题,例如:

删除数据不好处理
时间边界容易出问题
频繁扫描数据库
实时性比较差

因此比较适合数据量不大、实时性要求不高的系统。


十四、全量同步和增量同步通常要一起使用

这点非常重要。

实际项目通常不是:

全量 or 增量

而是:

全量 + 增量

例如系统第一次上线:

第一步:

MySQL
1000 万数据
    ↓
全量同步
    ↓
ES
1000 万数据

之后系统正常运行:

MySQL
  ↓
INSERT / UPDATE / DELETE
  ↓
增量同步
  ↓
ES

所以整个生命周期可以理解成:

              系统第一次上线
                    │
                    ▼
                全量同步
                    │
                    ▼
MySQL ============================= ES
                    │
              初始化完成
                    │
                    ▼
                增量同步
                    │
        ┌───────────┼───────────┐
        ▼           ▼           ▼
      INSERT      UPDATE      DELETE
        │           │           │
        └───────────┼───────────┘
                    ▼
                   ES

一句话:

全量同步解决“历史数据怎么过去”,增量同步解决“以后变化的数据怎么过去”。


十五、实际项目常见同步方案对比

方案 实现难度 实时性 适合场景
手动/定时全量同步 初始化、小数据量
update_time 定时扫描 一般 简单项目
业务代码直接更新 ES 小项目
业务代码 + MQ 常规业务
Canal + MQ 中高 数据同步、业务解耦
Debezium + Kafka CDC、数据平台
Flink CDC 实时数据处理、复杂数据链路

并不是技术越复杂越好。

如果项目只有几万条数据:

定时任务
+
update_time

可能已经足够。

如果业务规模比较大,希望业务代码和数据同步解耦,可以考虑:

MySQL
 ↓
Canal
 ↓
MQ
 ↓
ES

如果公司本身已经大量使用 Kafka、Flink 等数据基础设施,则可能选择:

MySQL
 ↓
Flink CDC / Debezium
 ↓
Kafka
 ↓
ES

十六、全量同步还有一个现实问题:不能一次查几百万条

例如:

SELECT *
FROM product;

如果商品表有:

1000 万条

直接全部加载到 JVM 内存显然不合适。

所以全量同步一般会:

分批查询
+
批量写入 ES

例如:

MySQL

1 ~ 1000
     ↓
ES Bulk

1001 ~ 2000
     ↓
ES Bulk

2001 ~ 3000
     ↓
ES Bulk

...

通常会利用:

分页 / 游标
+
ES Bulk API

降低数据库、JVM 和 ES 的压力。


十七、MySQL 和 ES 必须强一致吗?

大多数搜索业务并不要求。

例如管理员把商品价格:

7999
 ↓
6999

修改以后:

MySQL:立即变成 6999

几百毫秒后

ES:变成 6999

这段很短的时间内:

MySQL != ES

很多搜索场景是可以接受的。

这种思想叫:

最终一致性

即:

不要求两个系统每一毫秒的数据都完全一致,但最终必须达到一致状态。

因此:

MySQL
 ↓
MQ
 ↓
ES

这种异步架构在搜索系统中非常常见。


十八、为什么一般让 MySQL 做主数据?

因为 ES 的主要职责是搜索,而不是承担所有核心业务数据。

例如:

创建订单
扣库存
支付
账户余额

这些业务通常更依赖:

事务
一致性
约束
可靠更新

所以:

          MySQL
            │
       Source of Truth
            │
            │ 同步
            ▼
            ES
       Search Index

如果 ES 数据出现问题:

ES 索引损坏
ES Mapping 修改
同步程序出现 Bug

还可以:

MySQL
 ↓
重新全量同步
 ↓
重建 ES

因此可以把 ES 看成:

根据 MySQL 主数据构建出来的一份“搜索视图”。


十九、最后用一张图把所有概念串起来

一个比较典型的架构:

                  用户修改商品
                       │
                       ▼
                Product Service
                       │
                       ▼
                     MySQL
                       │
                    binlog
                       │
                       ▼
                     Canal
                       │
                       ▼
              RabbitMQ / Kafka
                       │
                       ▼
                Sync Consumer
                       │
                       ▼
                Elasticsearch
                       ▲
                       │
                    用户搜索

第一次建立 ES:

MySQL
  │
  │ 全量读取
  ▼
同步程序
  │
  │ Bulk
  ▼
Elasticsearch

系统正常运行以后:

MySQL
  │
  │ binlog
  ▼
Canal
  │
  ▼
MQ
  │
  ▼
Consumer
  │
  ▼
Elasticsearch

因此整个同步体系可以压缩成:

历史数据
   ↓
全量同步
   ↓
ES

新增 / 修改 / 删除
   ↓
增量同步
   ↓
ES

二十、总结

学习 MySQL → Elasticsearch 数据同步,只需要先抓住下面几个核心概念:

MySQL
=
业务主数据源

Elasticsearch
=
为了搜索建立的数据副本

全量同步
=
把已有数据全部同步过去

增量同步
=
只同步新增、修改、删除的数据

binlog
=
MySQL 的数据变更日志

Canal
=
监听并解析 MySQL binlog

MQ
=
传递数据变更事件

CDC
=
捕获数据库的数据变化

最终一致性
=
允许短暂不一致,但最终恢复一致

最典型的一套思路就是:

第一次:

MySQL
 ↓
全量同步
 ↓
ES


正常运行:

MySQL
 ↓
binlog
 ↓
Canal
 ↓
RabbitMQ / Kafka
 ↓
Consumer
 ↓
ES

所以,当以后再看到:

MySQL + Canal + MQ + Elasticsearch

不要把它理解成一堆中间件堆在一起。

它们实际上是在分工:

MySQL 负责存数据,Canal 负责发现数据变化,MQ 负责传递变化,Consumer 负责处理变化,ES 负责搜索;全量同步负责初始化历史数据,增量同步负责持续追踪后续变化。

MySQL 数据为什么要同步到 Elasticsearch?全量、增量同步与常见
http://clxhxhhr.top/posts/429/
作者
clxstart
发布于
2026-09-03
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。