
Elasticsearch 入门教程核心原理、Docker 部署与中文搜索实战做全文搜索功能折腾了一圈 Elasticsearch从装环境到调分词再到上线同步方案踩了不少坑。这份 Elasticsearch 入门教程整理了从零到跑通的全部经历希望能帮你少走弯路。点个收藏我们开始。一、Elasticsearch 是什么不只是搜索引擎Elasticsearch简称 ES是一个开源的分布式搜索与分析引擎基于 Apache Lucene 构建。它存数据但真正擅长的是对海量文本做近实时的全文检索——你可以把它理解为给数据库装上了百度搜索。如果把 ES 和 MySQL 做一层概念映射对应关系大致如下MySQLElasticsearch说明DatabaseCluster一个 ES 集群对应一个数据库实例TableIndex索引数据存储的逻辑单元RowDocument文档最小数据单位JSON 格式ColumnField字段文档中的属性SchemaMapping映射定义字段类型和索引方式ES 7.x 之后Type 的概念被废弃一个 Index 下默认只保留_doc类型架构比 6.x 清爽了不少。ES 有几个让我觉得真香的特性分布式架构天生支持横向扩展、数据写入后近实时可搜默认 1 秒 refresh、RESTful API 让任何语言都能调用、插件化设计可以按需扩展。这几个特性叠加在一起让 ES 成了全文搜索领域的事实标准。二、倒排索引为什么 ES 搜得这么快讲 ES 绕不开倒排索引——它是全文搜索能力的根基不理解它后面的分词器和查询优化都只能死记硬背。2.1 什么是倒排索引传统的正排索引是文档 → 关键词的映射就像翻到一本书的某页去看这页写了什么。而倒排索引Inverted Index反其道而行之是关键词 → 文档的映射——就像书末的索引页查分布式三个字直接告诉你出现在哪几页。举个例子假设有 3 篇文档文档 1Elasticsearch 是分布式引擎文档 2分布式系统的核心是搜索文档 3搜索引擎的未来是智能化经过分词和归一化处理后倒排索引长这样关键词文档 IDelasticsearch1分布式1, 2系统2搜索2, 3引擎1, 3当用户搜索引擎时ES 直接查表定位到文档 1 和 3不需要扫描全部文档——时间复杂度从 O(n) 降到了 O(1)。2.2 倒排索引的构建过程第一步分词Tokenization。把原始文本拆成独立的词条。比如Elasticsearch支持全文检索拆成[elasticsearch, 支持, 全文, 检索]。第二步归一化Normalization。对分词结果做标准化——转小写Elasticsearch → elasticsearch、去掉的是这类停用词、做词干提取running → run。这一步直接决定了后续搜索的召回质量。第三步创建倒排列表。为每个词项建立到文档 ID 的映射关系持久化存储。最终结构类似{elasticsearch: [1], 分布式: [1, 2], ...}。2.3 用户搜索时发生了什么当用户输入分布式搜索时ES 的执行链路是这样的查询词同样被分词分布式 搜索→ 分别查倒排索引分布式 → [1,2]搜索 → [2,3]→ 按 AND/OR 逻辑合并结果 → 用 TF-IDF 或 BM25 算法算相关性评分并排序。整个链路在毫秒级完成这就是倒排索引的威力。三、分词器搜索质量的命门倒排索引的质量很大程度上取决于分词器Analyzer。分词器决定了文本怎么拆、怎么存、怎么搜——它是在字符写入索引之前必须要经过的安检通道。ES 的 Analyzer 由三个组件串联而成字符过滤器Character Filters预处理比如去掉 HTML 标签、替换特殊字符如把转成and分词器Tokenizer将文本按规则切分成一个个词项这是整个流程中唯一必须的组件词项过滤器Token Filters对词项做后处理——转小写、去停用词、词干提取等ES 内置了多种分词器我整理了一张场景对照表分词器分词方式中文效果适用场景standard按词边界切分转小写逐字拆分差英文通用场景simple按非字母字符切分逐字拆分简单英文文本whitespace仅按空格切分不分词大小写敏感场景keyword不分词整个输入作为单个词项不分词精确匹配ID、状态码stopstandard 过滤停用词逐字拆分英文长文搜索注意到没上面所有内置分词器对中文的处理都是逐字拆分——standard 会把秋季穿搭指南切成秋、季、穿、搭、指、南六个单字。这意味着搜秋季时term 查询可能颗粒无收后面第六章会详细演示这个坑。这就是为什么必须装 IK 分词器。四、Docker 部署 ES Kibana十分钟跑起本地环境开发环境用 Docker 跑 ES 最省心——一行命令启动不想要了直接docker rm -f清掉。以下基于 ES 7.3.02026 年 7 月实测可用。4.1 拉取镜像并单节点启动# 拉取镜像dockerpull elasticsearch:7.3.0# 单节点启动dockerrun-d--namees7\-p9200:9200-p9300:9300\-ediscovery.typesingle-node\-eES_JAVA_OPTS-Xms1024m -Xmx1024m\elasticsearch:7.3.09200REST API 端口外部调用走这个9300集群内部节点间通信端口单节点用不到但建议暴露discovery.typesingle-node跳过主节点选举单机开发必备ES_JAVA_OPTS设 JVM 堆内存为 1GB建议不超过宿主机物理内存的一半浏览器访问http://localhost:9200返回 JSON 格式的集群信息就说明跑起来了。4.2 挂载配置与数据防止容器删除数据丢失容器一删数据就没了必须把关键目录挂载出来dockercpes7:/usr/share/elasticsearch/config E:\docker\elasticsearchdockercpes7:/usr/share/elasticsearch/data E:\docker\elasticsearchdockercpes7:/usr/share/elasticsearch/plugins E:\docker\elasticsearch编辑config/elasticsearch.yml加上跨域配置cluster.name:my-es-clusternetwork.host:0.0.0.0http.cors.enabled:truehttp.cors.allow-origin:*http.cors.allow-headers:Authorization,X-Requested-With,Content-Length,Content-Type删掉旧容器重新用带-v挂载的参数启动dockerrm-fes7dockerrun-d--namees7\-p9200:9200-p9300:9300\-ediscovery.typesingle-node\-eES_JAVA_OPTS-Xms1024m -Xmx1024m\-vE:\docker\elasticsearch\config:/usr/share/elasticsearch/config\-vE:\docker\elasticsearch\data:/usr/share/elasticsearch/data\-vE:\docker\elasticsearch\plugins:/usr/share/elasticsearch/plugins\elasticsearch:7.3.04.3 搭上 KibanaKibana 是 ES 官方的可视化和分析工具它自带的 Dev Tools 是最好的 ES 查询练习环境。版本号必须和 ES 保持一致否则有兼容性问题dockerpull kibana:7.3.0dockerrun-d--namekibana-p5601:5601 kibana:7.3.0同样把配置文件复制出来编辑kibana.ymlserver.name:kibanaserver.host:0elasticsearch.hosts:[http://172.17.0.1:9200]# 宿主机 IP不是 localhosti18n.locale:zh-CN注意Kibana 在容器内访问 ES 时localhost指向的是容器自身必须用宿主机的实际 IP。Windows 下通常为172.17.0.1用docker inspect es7可以确认。dockerrm-fkibanadockerrun-d--namekibana\-p5601:5601\-vE:\docker\kibana\config:/usr/share/kibana/config\kibana:7.3.0访问localhost:5601看到中文界面的 Kibana 控制台就成功了。左侧导航栏找到 Dev Tools 入口后面所有查询都在这里写。五、IK 中文分词器让 ES 真正认识中文最早用默认分词器建索引上线后发现中文搜索效果很差——搜美食出来一堆不相关的结果。排查后才发现美食被 standard 分词器拆成了美和食两个单字匹配逻辑全乱了。IK 分词器IK Analyzer是专为中文设计的 Lucene 分词插件通过内置词典识别中文词组。你可以理解为给 ES 装了一本现代汉语词典——它知道美食是一个完整的词而不是美“食”。5.1 安装与持久化# 进入 ES 容器dockerexec-ites7 /bin/sh# 安装 IK版本号与 ES 一致bin/elasticsearch-plugininstallhttps://get.infini.cloud/elasticsearch/analysis-ik/7.3.0安装时提示确认输入y回车。看到- Installed analysis-ik就装好了。我踩过的坑插件安装在容器内部容器一删就没了。安装完一定要把插件文件复制到宿主机挂载目录dockercpes7:/usr/share/elasticsearch/plugins/analysis-ik E:\docker\elasticsearch\pluginsdockercpes7:/usr/share/elasticsearch/config/analysis-ik E:\docker\elasticsearch\config这样配合第四章的-v挂载插件就能持久化保留了。重启后查日志确认dockerrestart es7dockerlogs-fes721|grepanalysis-ik看到loaded plugin [analysis-ik]就说明插件已生效。5.2 ik_smart vs ik_max_wordIK 提供两种模式选择困难症友好——记住一句话建索引用 ik_max_word细粒度搜索时用 ik_smart智能分词。模式策略输入深度学习框架使用指南的产出适用场景ik_smart智能粗粒度[深度学习, 框架, 使用指南]搜索查询时使用ik_max_word最细粒度穷举[深度学习, 深度, 学习, 框架, 使用, 指南]建索引时使用用_analyzeAPI 实际验证一下左边 IK、右边 standard差距一目了然# IK 智能分词POST /_analyze{analyzer:ik_smart,text:这个技术博客写得怎么样}# 结果这 / 个 / 技术 / 博客 / 写 / 得 / 怎么样# 默认 standard 分词POST /_analyze{analyzer:standard,text:这个技术博客写得怎么样}# 结果这 / 个 / 技 / 术 / 博 / 客 / 写 / 得 / 怎 / 么 / 样IK 能识别技术博客等中文词组而 standard 只会逐字拆分——这在生产环境中意味着完全不同的搜索体验。六、ES 基础查询语法从增删改查到聚合搜索理论都讲完了下面该写查询了。6.1 结构化建索引先建一个笔记索引指定每个字段的分词器PUT /note{settings:{number_of_shards:1,number_of_replicas:1},mappings:{properties:{title:{type:text,analyzer:ik_max_word},content:{type:text,analyzer:ik_max_word},create_time:{type:date,format:yyyy-MM-dd HH:mm:ss}}}}Mapping映射相当于 MySQL 的建表语句——定义每个字段的类型和索引方式。这里title和content都用ik_max_word意味着写入时会按最细粒度分词提高搜索召回率。插入几条测试数据PUT /note/_doc/1{title:夏日旅游攻略,content:去海边玩耍的必备攻略推荐三亚和青岛。,create_time:2024-08-16 16:49:35}PUT /note/_doc/2{title:美食笔记,content:推荐几家成都的火锅店真的很辣很过瘾,create_time:2024-11-02 17:00:36}PUT /note/_doc/3{title:秋季穿搭指南,content:分享适合秋天的服装搭配技巧风衣必不可少。,create_time:2024-11-03 16:59:58}6.2 增删改查速览几个常用操作一张代码块看完# 全量更新文档存在则覆盖不存在则插入PUT /note/_doc/1{title:更新后的标题,content:新内容}# 只更新指定字段推荐POST /note/_update/1{doc:{title:只更新标题}}# 仅创建已存在则报错PUT /note/_create/4{title:新文档,content:测试}# 删除DELETE /note/_doc/1# 查看单条GET /note/_doc/2# 搜索全部GET /note/_search6.3 搜索match vs term一个常踩的坑全文搜索用match——搜索词会先被分词再匹配GET /note/_search{query:{match:{title:穿搭}}}# 穿搭分词后匹配 → 命中文档 3精确匹配用term——搜索词不经过分词直接去索引里比对GET /note/_search{query:{bool:{filter:{term:{title:秋季}}}}}可能有人会问用 term 搜秋季文档 3 标题明明包含秋季穿搭指南为什么一条结果都没有在项目中真遇到过的问题。原因出在分词环节文档入库时title用了ik_max_word分词“秋季穿搭指南被拆成了多个词项。但term查询不做分词直接用秋季这个完整字符串去索引里找——索引里存的是被 IK 拆散后的词项很可能不包含完整的秋季”。换成match就能搜到因为match会先把秋季分词后再分别匹配索引。一句话概括match 分词后搜索覆盖面广term 原样匹配精准但可能漏。6.4 分页、排序、聚合# 分页from (page-1) * sizeGET /note/_search{query:{match_all:{}},from:0,size:2}# 按发布时间倒序GET /note/_search{query:{match_all:{}},sort:[{create_time:{order:desc}}]}# 聚合按发布时间分组统计文档数类似 SQL 的 GROUP BYGET /note/_search{query:{match_all:{}},aggs:{group_by_create_time:{terms:{field:create_time}}}}补充一点上面 term 查询用filter而非must是因为filter不计算相关性评分_score性能更好——适合状态等于已发布价格在 100-200 之间这类纯过滤场景。七、数据增量同步怎么把 MySQL 的数据搬到 ES生产环境中MySQL 通常作为主存储ES 作为搜索的从库。如何把 MySQL 的变更实时同步到 ES业内主要两条路。7.1 方案一MQ 通知业务服务在写 MySQL 的事务内同时发一条 MQ 消息。搜索服务作为消费者监听消息收到后更新 ES 索引。这个方案的优势是灵活——消息体可以只携带变更字段减少数据传输MQ 自带持久化能力搜索服务挂了消息不会丢。但代价是侵入性强——每个涉及搜索的业务点都要写 MQ 发送逻辑团队大、服务多时维护成本不低。另外数据库写成功但 MQ 发送失败这种分布式事务问题也需要补偿机制兜底。7.2 方案二CanalCanal是阿里巴巴开源的一个 MySQL binlog 增量订阅中间件。它的原理很巧妙——把自己伪装成 MySQL 的从库监听主库的 binlog解析出 INSERT/UPDATE/DELETE 事件后推送给下游消费者。你可以理解为给 MySQL 接了一个监听器数据一变它就通知你。Canal 最大的卖点是零侵入——业务代码一行不用改直接订阅数据库日志就行。一致性也天然优于 MQ 方案因为数据来源就是 binlog。但代价是运维成本高Canal Server 需要独立部署和高可用而且它只能感知到数据库层面的变更——如果你的同步逻辑涉及 Redis 缓存或外部 API 的数据加工Canal 就无能为力了。7.3 选型对比对比维度MQ 通知方式Canal实时性较高受消费速率影响近乎实时数据一致性需要事务消息或补偿机制与数据库强一致业务侵入性高需在业务代码中埋点零侵入开发成本需开发生产 消费两端逻辑需开发 binlog 解析与格式转换运维成本中等MQ 需高可用部署较高Canal Server 需独立部署与维护适用场景多数据源、复杂业务逻辑单一 MySQL 数据源、简单同步逻辑可能有人会问两种方案到底怎么选我的建议很简单如果你只有一个 MySQL 数据源同步逻辑就是MySQL 多了/改了什么ES 就跟着改直接上 Canal省心。但如果数据源不止 MySQL比如还有 MongoDB、外部 API或者同步前需要做字段拼装、多表关联等业务加工那就选 MQ 方案——灵活性更高。我个人两个方案都在项目里用过早期团队小、业务简单时用 Canal 确实省了不少事后来数据源多了、同步逻辑复杂了才切到 MQ。所以这不是谁更好的问题而是什么阶段适合什么方案。从倒排索引原理到 Docker 部署从分词器到查询语法再到数据同步方案这篇 Elasticsearch 入门教程帮你把入门的坑都踩了一遍。这些内容在实际项目中够用了——剩下的高级功能高亮搜索、Suggest 建议器、嵌套聚合等建议在用到的场景下查官方文档记忆效率更高。系列下一篇计划写 Spring Boot 如何集成 Elasticsearch覆盖 RestHighLevelClient 和 Spring Data Elasticsearch 两种方式感兴趣的可以关注。参考链接Elasticsearch 7.3 官方文档搜Elasticsearch 7.3 ReferenceIK 分词器 GitHub搜elasticsearch-analysis-ikCanal GitHub搜alibaba canalKibana 7.3 官方文档搜Kibana 7.3 Guide如果对你有帮助欢迎点赞、收藏、转发。