ES+MySQL实时搜索架构实战解析

一、ES概述

ElasticSearch(简称 ES)是一个 分布式、高扩展、高实时的全文搜索引擎 ,同时也是一个开源的 NoSQL 数据库,主要用于 存储、检索和分析海量数据 。它基于 Lucene 搜索引擎库构建,提供了简单易用的 RESTful API,能够快速处理结构化、半结构化和非结构化数据。

ElasticSearch 的核心特性

  1. 全文搜索能力 支持复杂的全文检索,能对文本进行分词、同义词处理、模糊匹配等,比传统数据库的LIKE查询更高效、更精准(例如电商的商品搜索、日志检索)。
  2. 分布式架构数据自动分片存储在多个节点,支持水平扩展(增加节点即可提升容量和性能),并通过副本机制保证高可用(某节点故障时,副本节点可无缝接管)。
  3. 实时性数据写入后几乎可以立即被检索到(延迟通常在毫秒级),适合对实时性要求高的场景(如实时监控、即时推荐)。
  4. 强大的聚合分析内置丰富的聚合功能,可对数据进行统计、分组、计算(如计算某商品类别的销量排名、分析用户行为趋势),常用于数据可视化和业务分析。
  5. 多数据类型支持不仅支持文本,还能处理数字、日期、地理位置等数据类型,甚至可以存储 JSON 格式的复杂结构。

二、MySQL主从架构场景下Canal+ES的架构

在 MySQL 主从架构中,Canal 和 ElasticSearch(ES)的联合使用,主要是为了实现 MySQL 数据实时同步到 ES ,从而利用 ES 强大的全文检索和聚合分析能力。这种架构常被用于构建 "MySQL 存储核心业务数据 + ES 提供高效查询" 的系统(如电商商品搜索、日志检索等)。
架构方案:MySQL 主从 + Canal + Elasticsearch
写入路径(强一致)

  • 订单/库存写 → 直写 MySQL 主库
  • 从库 负责 SQL 读请求(保障读写分离)。
    同步路径(最终一致)
  • MySQL 主库产生 Row-Based Binlog
  • Canal 模拟从库订阅 Binlog,解析出 数据变更事件(Insert/Update/Delete)。
  • Canal Adapter / 自研消费者 将变更数据同步到 Elasticsearch(Upsert 到索引)。
  • ES 中的数据即为库存/商品的近实时视图,延迟通常在毫秒级。
    查询路径(低延迟)
  • 复杂查询/聚合/搜索 → 直接走 Elasticsearch ,单次查询延迟压缩至 100ms 内
  • 简单读(详情、库存校验) → 走从库 MySQL 或 Redis 缓存。

2.1 核心架构原理

MySQL 主从架构提供了数据备份和读写分离能力,而 Canal 可监听 MySQL 的 binlog 日志,将数据变更实时同步到 ES,形成 "MySQL ← Canal → ES" 的数据流闭环。整体架构如下:

  • Canal 的角色:模拟 MySQL 从库的 "slave" 角色,伪装成从库连接主库,订阅并解析 binlog 日志,获取数据变更(增删改)。
  • ES 的角色:存储同步过来的数据,提供全文检索、复杂聚合等查询能力,减轻 MySQL 的查询压力。

2.2、具体实现步骤

1. 准备基础环境
  • MySQL 主库配置 :需开启 binlog(log_bin = ON),并设置binlog_format = ROW(Canal 仅支持 ROW 模式,可获取行级变更详情),同时为 Canal 创建具有REPLICATION SLAVE权限的账号(同主从复制的授权逻辑)。
  • Canal 部署 :下载 Canal(官方地址),配置监听 MySQL 主库的地址、端口、账号密码,以及 binlog 的起始位置(如canal.instance.master.address = 主库IP:3306)。
  • ElasticSearch 部署:搭建 ES 集群(单节点也可测试),创建索引(Index)和映射(Mapping),定义需要同步的字段类型(如文本、数字、日期等)。
2. 核心数据同步流程

Canal 监听 MySQL binlog 后,通过以下步骤将数据同步到 ES:

(1)Canal 解析 binlog 获取数据变更

Canal 启动后,会像 MySQL 从库一样连接主库,获取 binlog 日志并解析为结构化的变更数据(包含操作类型:INSERT/UPDATE/DELETE,以及变更前后的字段值)。
例如,MySQL 执行 UPDATE user SET name='张三' WHERE id=1,Canal 会解析出:

java 复制代码
{
  "table": "user",
  "type": "UPDATE",
  "data": [{"id": 1, "name": "张三", "age": 20}],  // 变更后的数据
  "old": [{"name": "李四"}]  // 变更前的旧数据(仅UPDATE有)
}
(2)数据转换与过滤(关键步骤)

Canal 本身不直接写入 ES,需通过 客户端程序(如 Java、Python 脚本)处理数据:

  • 过滤无关数据:只同步需要索引到 ES 的表或字段(如过滤掉日志表、临时表)。
  • 格式转换 :将 MySQL 的字段类型适配为 ES 的映射类型(如 MySQL 的datetime转为 ES 的date)。
  • 处理复杂逻辑:如关联多表数据(需查询 MySQL 从库补充关联字段)、计算衍生字段等。
(3)写入 ElasticSearch

客户端程序将转换后的数据通过 ES 的 REST API 写入 ES:

  • INSERT :调用POST /索引名/_doc/文档ID新增文档。
  • UPDATE :调用PUT /索引名/_doc/文档ID更新文档(或使用_updateAPI 做部分更新)。
  • DELETE :调用DELETE /索引名/_doc/文档ID删除文档

2.3、注意事项

数据一致性

  • Canal 同步是异步的,存在毫秒级延迟,需业务容忍 "MySQL 与 ES 数据短暂不一致"。
  • 可通过 "双写"(写入 MySQL 的同时调用 ES API)保证强一致,但会增加业务复杂度。
    异常处理
  • 需实现失败重试机制(如 Canal 的ack机制确保未处理的变更不会丢失)。
  • 定期全量同步校验(如每天凌晨对比 MySQL 与 ES 数据,修复不一致)。
    性能优化
  • Canal 客户端批量处理变更(减少 ES 写入次数)。
  • ES 开启批量写入 API(_bulk),提升写入效率。
  • 对大表同步,可先全量导出 MySQL 数据到 ES,再通过 Canal 同步增量变更。
    与 MySQL 从库的关系
  • Canal 监听主库 binlog,与从库同步不冲突(从库仍负责备份和读业务)。
  • 若担心主库压力,Canal 也可监听从库的 binlog(需从库开启log_bin)。

三、倒排索引

倒排索引是基于MySQL正排索引的概念来说的

3.1 正向索引

如果把图书馆的每本书看作 "数据载体","书的编号" 就是主键,"书名、作者、目录、内容摘要" 就是值 ------ 正向索引就像 "书号→书的信息" 的对照表,通过书号能直接查到这本书的所有关键信息。
如果是根据id查询,那么直接走索引,查询速度非常快。
但如果是基于title做 模糊查询,只能是逐行扫描数据,流程如下:

1)用户搜索数据,条件是title符合"%手机%"
2)逐行获取数据,比如id为1的数据
3)判断数据中的title是否符合用户搜索条件
4)如果符合则放入结果集,不符合则丢弃。回到步骤1
逐行扫描,也就是全表扫描,随着数据量增加,其查询效率也会越来越低。当数据量达到数百万时,就是一场灾难。

3.2倒排索引

倒排索引中有两个非常重要的概念:

  • 文档(Document):用来搜索的数据,其中的每一条数据就是一个文档。例如一个网页、一个商品信息
  • 词条(Term):对文档数据或用户搜索数据,利用某种算法分词,得到的具备含义的词语就是词条。例如:我是中国人,就可以分为:我、是、中国人、中国、国人这样的几个词条
    创建倒排索引是对正向索引的一种特殊处理,流程如下:
  • 将每一个文档的数据利用算法分词,得到一个个词条
  • 创建表,每行数据包括词条、词条所在文档id、位置等信息
  • 因为词条唯一性,可以给词条创建索引,例如hash表结构索引
3.3正向索引和倒排索引对比

概念区别:

  • 正向索引 是最传统的,根据id索引的方式。但根据词条查询时,必须先逐条获取每个文档,然后判断文档中是否包含所需要的词条,是根据文档找词条的过程
  • 倒排索引 则相反,是先找到用户要搜索的词条,根据词条得到保护词条的文档的id,然后根据id获取文档。是根据词条找文档的过程
3.4优缺点:

正向索引

  • 优点:
    • 可以给多个字段创建索引
    • 根据索引字段搜索、排序速度非常快
  • 缺点:
  • 根据非索引字段,或者索引字段中的部分词条查找时,只能全表扫描。
    倒排索引
  • 优点:
    • 根据词条搜索、模糊搜索时,速度非常快
  • 缺点:
    • 只能给词条创建索引,而不是字段
    • 无法根据字段做排序

四、ES数据库概念

4.1 mysql与elasticsearch

各自长处:

  • Mysql:擅长事务类型操作,可以确保数据的安全和一致性
  • Elasticsearch:擅长海量数据的搜索、分析、计算
    我们统一的把 mysql与elasticsearch的概念做一下对比

|-----------|-------------------|----------------------------------------------------------|
| MySQL | Elasticsearch | 说明 |
| Table | Index | 索引(index),就是文档的集合,类似数据库的表(table) |
| Row | Document | 文档(Document),就是一条条的数据,类似数据库中的行(Row),文档都是JSON格式 |
| Column | Field | 字段(Field),就是JSON文档中的字段,类似数据库中的列(Column) |
| Schema | Mapping | Mapping(映射)是索引中文档的约束,例如字段类型约束。类似数据库的表结构(Schema) |
| SQL | DSL | DSL是elasticsearch提供的JSON风格的请求语句,用来操作elasticsearch,实现CRUD |

在企业中,往往是两者结合使用:

  • 对安全性要求较高的写操作,使用mysql实现
  • 对查询性能要求较高的搜索需求,使用elasticsearch实现
  • 两者再基于某种方式,实现数据的同步,保证一致性

五、ES的深分页问题

1. 深分页为什么会慢?

  • Elasticsearch 底层是 Lucene,分页使用 from + size。
  • 深分页时,比如 from=100000, size=10:
    • Lucene 需要先取出前 100010 条数据,再丢掉前 100000 条,只返回最后 10 条。
  • 这会导致:
    1. 内存占用大(要维护大量候选结果)。
    2. CPU 消耗高(排序、打分都要计算)。
  1. 延迟急剧上升(越靠后页数,延迟越明显)。

2. 常见延迟表现

  • 前几页(比如 1~100 页,每页 10 条),延迟在 100ms 级别可接受。
  • 深分页(10w 级以上偏移),延迟可能达到秒级,甚至 OOM。

3. 解决策略

(1)Search After(官方推荐)
  • 原理:基于上一页的 sort 值继续往后拉,不允许随机跳页。
  • 使用场景:游标式翻页,用户只需要"下一页/加载更多"。
  • 优点:性能稳定,延迟和页数无关。
  • 缺点:不能直接跳到第 N 页。
    GET sku_index_v1/_search { "size": 10, "sort": { "updatedAt": "asc" }, { "id": "asc" }, "search_after": "2024-09-09T12:00:00", 12345 }

(2)Scroll API
  • 原理:游标方式,快照化查询结果,适合全量扫描
  • 优点:适合日志导出、大批量处理。
  • 缺点:占用内存,不能长时间维持;不适合用户交互翻页。
    GET /sku_index_v1/_search?scroll=1m { "size": 1000, "query": { "match_all": {} } }

(3)分段查询(数据分片 + 范围查询)
  • 原理:通过业务字段(时间/ID 范围)做条件分段,而不是简单的 from+size。
  • 例如:按 id > lastId 来查询下一批。
  • 优点:轻量,避免深分页;性能高。
  • 缺点:需要业务方改造查询逻辑。

(4)预计算与缓存
  • 热门分页 :可以放到 Redis 缓存,直接返回。
  • 大分页导出 :走 离线批处理(Hive/Spark → ES),不要依赖实时分页。

六、ES和MySQL的一致性问题

要保证Elasticsearch(ES)和MySQL之间的数据一致性,可以考虑以下几种方法:

  1. 双写:在应用程序中同时对MySQL和ES进行写操作。这确保了数据同时写入两个系统,从而保持一致性。这种方法简单直接,但会增加写入的延迟和复杂性。
  2. 使用消息队列:将写操作发送到消息队列,然后由消费者分别将数据写入MySQL和ES。这样可以确保写操作被顺序处理,从而保持数据的一致性。常用的消息队列包括Kafka、RabbitMQ等。
  3. 使用数据库的binlog:MySQL的binlog记录了数据库的修改操作,可以通过解析binlog来将数据同步到ES。可以使用工具如Debezium、Maxwell等来实时监听和解析binlog,并将数据同步到ES。
  4. 定期全量同步:定期将MySQL中的数据全量同步到ES。可以使用ETL工具或自定义脚本来实现全量同步。这种方法适用于数据量较小且对实时性要求不高的场景。
相关推荐
童谣122 分钟前
越华环保:美丽蓝天项目申报方案数字化研判架构设计与实践
架构
xxwl58531 分钟前
数据库后端接口测试报告
spring boot·mysql·tomcat
xqqxqxxq1 小时前
MySQL 数据类型笔记
数据库·笔记·mysql
Logintern091 小时前
理解MySQL数据库的“事务与锁”
数据库·mysql
朱容zr3331332 小时前
为什么推荐使用自增主键?使用UUID作为主键的优缺点是什么?
java·运维·数据库·后端·mysql·面试·性能优化
Vince的修炼之路3 小时前
大模型 Skills 技术深度分析
人工智能·架构
Elasticsearch3 小时前
缩小 AI 差距:下一代知识访问如何为政府解锁任务成果
elasticsearch
Dr.kangder3 小时前
嵌入式软件单元测试:从理论到实践
架构·单元测试·嵌入式·测试覆盖率
brave_zhao3 小时前
mysql的安装
数据库·mysql
小马9263 小时前
GPT-6 Swarm 架构深度解析:从单体模型到 Agent 集群协同的范式跃迁
gpt·架构·大模型·openai·agent·分布式ai·swarm架构