做搜索功能这些年,被问得最多的问题永远是“为什么不能用数据库的 LIKE 查询,非得单独搞一套搜索引擎”。等真正接过一个内容量过千万、检索逻辑复杂的项目后,你就明白了:搜索引擎从来不是依赖型组件,而是独立的基础设施。ElasticSearch 就是这套基础设施里目前最主流的实现,没有之一。这篇文章不打算做官方文档的搬运工,而是把从本地 Windows 开发环境搭建、云主机部署、索引设计、查询调优、集群维护到问题排查的完整链路串一遍,按我实际做项目的顺序来讲,希望能帮正在选型和已经踩坑的同学少走点弯路。
如果你是刚接触 ElasticSearch 的读者,这篇文章可以当作一份带避坑指南的入门手册;如果你已经在生产环境玩过一段时间,可以直接跳到后面的分片策略和排查章节,那部分有过线上事故的教训。整体内容围绕“搜索引擎能解决什么问题”和“ES 怎么落地解决问题”两条主线,尽量说人话,把为什么这么做讲透。
1. 为什么业务量一上来,数据库查询就撑不住搜索场景
1.1 从“搜索”和“查询”的本质区别说起
很多团队上 ElasticSearch 的起因特别简单:MySQL 里有个几千万行的内容表,用户在前端输个关键词,后端拼一条WHERE title LIKE '%关键词%',结果全表扫描,慢得页面超时。这里要澄清一个概念:从用户视角看,这都叫“搜索”,但从技术视角看,数据库的 LIKE 是查询,ElasticSearch 做的是全文检索,二者底层逻辑完全不同。
数据库的 B+Tree 索引擅长等值匹配和范围匹配,但遇到%关键词%这种前后模糊匹配就废了,索引直接失效,只能逐行扫描。ES 则使用倒排索引,它把每个文档先做分词,再建立“词条 -> 文档列表”的映射关系。查询的时候只需要在词条字典里找到对应的词,就能立刻拿到包含这个词的所有文档 ID,再根据相关性打分排序。这个差异在千万级数据量下会被放大得非常明显:LIKE 查询是秒级甚至分钟级,ES 的全文检索是毫秒级。
1.2 ElasticSearch 到底解决了哪几类问题
拿我经手的内容管理平台举例,核心诉求有三个。第一个是全文检索,用户输入任意关键词,系统要能快速找到标题、正文、标签里匹配的内容,并且相关度高的排前面。第二个是聚合分析,运营要按分类统计内容数量、按时间维度看发布趋势、按作者维度看贡献排行,这些如果用数据库来做,SQL 写起来很痛苦,ES 的聚合框架一条 DSL 就能搞定。第三个是灵活过滤,产品端经常要叠加各种筛选条件,比如“近一周内、科技分类、阅读量大于一万、关键词包含AI”,这种多条件组合查询在 ES 里就是几个 filter 组合的事,性能和代码复杂度都可控。
所以,如果你只是做个几千条数据的公司官网搜索,用 SQL LIKE 没毛病,不必为了用而用。但如果数据量到了百万以上,或者查询模式多样、对响应时间敏感,那 ES 这类专业搜索引擎就是绕不开的方案了。
2. 环境准备与部署落地:从 Windows 本机到云主机生产环境
2.1 Windows 环境下的快速启动
开发阶段我习惯先在本地 Windows 上把逻辑跑通,再去服务器部署。ElasticSearch 的 Windows 启动非常简单,但有几个点很容易被忽视。
首先去官网下载对应版本的压缩包,解压后进入bin目录,双击或命令行执行elasticsearch.bat启动。这里有个版本坑,新版 ES 对 JDK 版本要求严格,比如 8.x 要求 JDK 17 以上,9.x 要求 JDK 21 以上。ES 自带了一个捆绑的 JDK,正常情况下用自带的就行,但如果你系统里同时配了JAVA_HOME环境变量且版本不对,启动时会直接报错。解决方法是把JAVA_HOME临时指向 ES 自带的 jdk 目录,或者直接在你启动的终端里set JAVA_HOME=你解压的ES目录\jdk,然后再启动。
启动成功的标志是控制台出现 “started” 日志,监听端口默认是 9200。此时浏览器访问http://localhost:9200能返回 JSON 信息,就说明服务起来了。Windows 上的第二个坑是防火墙弹窗,要记得允许 Java 通过专用网络访问,不然本机能访问但局域网其他机器访问不了。第三个坑是路径不能包含中文或空格,我之前把 ES 解压到D:\软件\es,启动直接报找不到模块,改成纯英文路径就好了,这个坑在 Windows 上很普遍。
2.2 云主机 Linux 环境的完整部署流程
生产环境我推荐用 Linux 云主机,性能和稳定性远好于 Windows。这里以 CentOS 7.9 和 ES 9.4.0 为例,讲一遍完整部署。
第一步,创建专用用户。ES 明确规定不能用 root 用户启动,这是出于安全设计,你用 root 跑会直接启动失败。执行useradd esuser创建用户,把安装包解压后chown -R esuser:esuser /usr/local/elasticsearch授权。
第二步,配置系统参数。ES 对文件描述符数量和虚拟内存有硬性要求,不配置会遇到各种诡异问题。编辑/etc/security/limits.conf,加入:
esuser soft nofile 65536 esuser hard nofile 131072 esuser soft nproc 4096 esuser hard nproc 4096编辑/etc/sysctl.conf,加入:
vm.max_map_count=262144然后sysctl -p使配置生效。max_map_count这个参数很隐蔽,ES 的 Lucene 底层会创建大量内存映射文件,默认值 65530 在启动时通常够用,但索引一多或者数据量一涨就会报 “max virtual memory areas vm.max_map_count [65530] is too low”,需要先预防性调高。
第三步,修改 ES 的 JVM 堆内存。修改config/jvm.options里的-Xms和-Xmx,官方建议两个值设为相同,避免运行期动态扩容导致性能抖动。堆内存大小不要超过物理内存的一半,也不要超过 31GB,因为超过 31GB 后 JVM 指针压缩会失效,内存白白浪费。比如 16G 内存的机器,设成 8g 通常是比较稳的。
第四步,启动并验证。切到 esuser 用户,执行:
/bin/su - esuser -c "/usr/local/elasticsearch/bin/elasticsearch -d -p /usr/local/elasticsearch/elasticsearch.pid"-d表示后台运行,-p记录进程号方便后续管理。启动后用curl http://localhost:9200验证。如果是云主机,还要在安全组里放行 9200 端口,这个经常被忽略,排查半天发现是安全组没开。
2.3 单机多节点模拟集群的配置思路
如果需要在有限资源下测试集群特性,可以在一台机器上启动多个 ES 实例。核心是每个实例要有独立的data目录、logs目录和不同的http.port、transport.port。以两节点为例,第一个节点用默认端口 9200,第二个节点配置:
cluster.name: my-es-cluster node.name: node-2 path.data: /home/esuser/es-data-2 path.logs: /home/esuser/es-logs-2 network.host: 0.0.0.0 http.port: 9202 transport.port: 9302 discovery.seed_hosts: ["127.0.0.1:9300", "127.0.0.1:9302"] cluster.initial_master_nodes: ["node-1", "node-2"]两个节点cluster.name必须一致,才能自动组成集群。这种单机多实例方式适合学习和测试,生产环境还是建议真正多台机器,毕竟单机多实例如果宿主机挂了,所有节点一起挂,高可用等于没有。
3. 索引设计:ES 性能的胜负手
3.1 Mapping 设计决定数据存储方式
很多人第一次用 ES,图省事直接往索引里写数据,让 ES 自动生成 mapping。这在原型阶段没问题,生产环境这么干就是埋雷。自动 mapping 会把你所有的字符串字段都映射成text类型,也就是全文检索类型,但要聚合、排序、精确匹配的时候,text根本干不了这活。
我举一个具体场景。内容表里有category字段表示分类,值可能是“科技”“体育”。如果它是text类型,ES 会分词,聚合统计 Category 的时候得到的是“科技”“体”“育”这种词条,排序更是不可用。正确做法是映射成keyword类型,keyword不分词,整体作为一个词条存储,才能做精确过滤、聚合和排序。
所以在建索引的时候一定要显式定义 mapping。一个简化版的示例:
PUT /article_index { "mappings": { "properties": { "title": { "type": "text", "analyzer": "ik_max_word", "fields": { "keyword": { "type": "keyword" } } }, "content": { "type": "text", "analyzer": "ik_max_word" }, "category": { "type": "keyword" }, "publish_time": { "type": "date", "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis" }, "read_count": { "type": "integer" } } } }这里title同时配置了text和keyword子字段,一个用于搜索,一个用于排序和聚合。category用keyword用于精确筛选。publish_time指定了日期格式,适配多种格式输入。这个 mapping 的设计思路是:每个字段按照它在业务中的使用方式来决定存储类型,而不是一刀切。
3.2 中文分词器的选择与安装
ES 自带的标准分词器对中文的处理就是逐字切分,比如“搜索引擎技术”会被分成“搜”“索”“引”“擎”“技”“术”,查询时用户体验很差。中文搜索场景下,绝大多数项目会用 IK 分词插件。
IK 支持两种分词模式:ik_smart和ik_max_word。ik_smart做粗粒度切分,比如“搜索引擎技术”分成“搜索引擎/技术”;ik_max_word做细粒度切分,尽可能分出更多词,比如“中华人民共和国”会被拆分成“中华人民共和国/中华人民/中华/华人/人民/共和国”等。搜索效果上,ik_max_word召回率高,但索引体积大;ik_smart更精确,但可能漏掉一些组合词。我的习惯是索引用ik_max_word,查询用ik_smart,这样既保证召回率又提升匹配精度。
安装 IK 插件需要和 ES 版本严格对应。下载对应版本的插件压缩包后,放到 ES 目录的plugins子目录下,解压成一个ik目录,重启 ES 即可。如果版本不匹配,ES 启动时会直接拒绝加载插件,报错信息里会提示版本不一致。另外,IK 自带的主词典是有限的,针对业务特殊词,比如产品名、人名、专业术语,需要在 IK 配置目录下的IKAnalyzer.cfg.xml里配置自定义词典,效果立竿见影。
3.3 分片和副本的数量规划
分片(shard)是 ES 数据存储的最小单位,一个索引的数据会被打散到多个分片上。副本(replica)是分片的拷贝,用于高可用和提升读性能。分片数量的确定有几个经验原则。
第一,分片数在索引创建后不能修改,所以必须提前规划好。你可以在索引创建后调整副本数,因为副本数可以动态修改。第二,单分片的容量建议控制在 30GB 到 50GB 之间,太大则单分片恢复慢,重新分配时容易出问题;太小则分片数量太多,管理开销大。第三,分片总数尽量等于或小于集群的可用数据节点数乘以节点可承载的 CPU 线程数,不要盲目地多分片。
举个例子,你预估一年数据量 300GB,集群有 3 个数据节点,那么可以设置 5 个主分片,每个分片 60GB,副本设 1 份。这样每个节点分摊的分片负载比较均衡。切记不要把分片数设成 1 然后又期望它在集群里分散存储——单分片索引只能存在一个节点上,其他节点只放副本,写性能和存储能力都会受限。我见过最典型的案例是,有人建索引时图省事不指定分片数,默认 5 分片,但集群只有 2 个节点,结果一个索引在节点 A 存 3 个分片、节点 B 存 2 个分片,数据倾斜严重,查询时快时慢。提前按集群规模规划好分片,能规避很多后期问题。
4. 数据写入与查询实战:从 DSL 语法到搜索引擎搜索技巧
4.1 写入链路与 refresh 机制
ES 写入数据走的是 “buffer -> segment” 的链路:数据先写入内存 buffer,同时写入 translog 日志保证崩溃恢复,默认每隔一秒把 buffer 中的数据 refresh 到内存中的 segment,此时数据变得可被搜索。所以刚写入的数据,最迟 1 秒后就能搜到,这就是 ES 的“近实时”特性。
如果业务要求更高的实时性,比如商品上下架后立即生效,可以调用 refresh API 强制刷新,或者把索引的refresh_interval调成true或者更小的值。但要注意,频繁刷新会产生大量小 segment,后续 merge 的开销会变大。反过来,如果你做的是日志批量写入场景,可以暂时把refresh_interval调到30s甚至-1(完全关闭自动刷新),写入吞吐量会有明显提升,等数据写完再手动 refresh。
写入性能优化还有几个实用技巧。批量接口_bulk永远比单条写入效率高,建议每条请求包含 1000 到 5000 个文档,如果单条文档较大就适当减少数量。禁用_source的存储可以省磁盘,但要确认查询结果确实不需要原文档内容。副本数在初始写入时可以临时设为 0,写完再恢复成 1,因为写副本会增加一次网络传输和写盘开销,这个操作在生产切流前做收益很明显。
4.2 查询 DSL 核心语法速查
ES 查询 DSL 是 JSON 风格,刚开始接触会觉得嵌套结构复杂,但只要抓住几个核心查询类型,大部分场景都能覆盖。
match是最常用的全文检索查询,会对查询语句做分词,然后按相关度打分。这是标题、正文检索的首选。term是精确匹配查询,适用于keyword字段,比如分类、状态、ID 的精确筛选。这里很多人栽过跟头:用term查一个text字段,搜出来结果是 0 条,原因是text字段被分词了,存进去的内容和查询词条对不上。bool查询用于组合多个条件,里面包含must(必须满足)、should(满足任意一个加分)、filter(必须满足但不打分)。range用于数值和日期范围查询。
一个综合的例子,实现“搜索关键词,筛选科技分类,发布时间在最近一周,阅读量大于 1000,按相关度排序”:
POST /article_index/_search { "query": { "bool": { "must": [ { "match": { "title": "搜索引擎" } } ], "filter": [ { "term": { "category": "科技" } }, { "range": { "publish_time": { "gte": "now-7d/d" } } }, { "range": { "read_count": { "gte": 1000 } } } ] } }, "from": 0, "size": 20, "highlight": { "fields": { "title": {} } }, "sort": [ { "_score": { "order": "desc" } } ] }这个例子里filter内部的条件不会参与相关度打分,ES 会缓存它们的查询结果,所以后面同样的过滤条件再次出现时性能会快很多。这是用filter代替普通查询的最重要理由。
4.3 搜索技巧:相关性调优与容错
搜索引擎的“好用”不单是结果多,更重要的是结果相关。ES 默认的相关度算法基于 TF-IDF 和 BM25 的改进版,能处理大部分场景,但我们可以主动调优几个点。
第一个是字段权重。标题匹配的文档应该比正文匹配的文档相关性更高,因为标题往往更核心。在查询的时候用boost提升标题字段权重:
{ "query": { "multi_match": { "query": "搜索引擎", "fields": ["title^3", "content"] } } }这里title^3表示标题字段的相关性得分乘以 3,搜索结果更倾向于标题命中的内容。这个技巧做搜索产品必用,效果立竿见影。
第二是模糊容错。用户经常写错别字,比如搜“ElasticSearch”写成“ElasticSarch”,用fuzziness可以允许编辑距离范围内的错配:
{ "query": { "match": { "title": { "query": "ElasticSarch", "fuzziness": "AUTO" } } } }AUTO会根据词条长度自动决定允许的编辑距离,简单词条不容错,长词条允许一个字符的错配,在召回率和精确性之间取得平衡。还有通配符查询wildcard可以支持*和?模糊匹配,但性能较差,不建议在前端搜索用,适合后台管理系统的简单过滤。
第三是分页方式的选型。常规的前端翻页用from+size没问题,但页数很深时,ES 需要先把前 N 条都查出来再截断,非常消耗资源。超过 10000 条的深分页建议用search_after或scroll。search_after适合实时搜索页面的“加载更多”场景,scroll适合数据导出、批量处理的场景。我曾经处理过一个导出全部数据的需求,直接用from+size循环拉,拉到 3 万多条时整个集群 CPU 飙高,换成scroll后一分钟内拉完几十万条数据,资源占用还很低。
4.4 聚合分析:从搜索到统计的一步到位
ES 的聚合框架强大到可以作为轻量级 BI 工具使用。terms聚合相当于 SQL 的GROUP BY,按字段分组统计数量;date_histogram按时间间隔统计,可以生成趋势图数据;avg、sum、max、min则是常规统计函数。
一个按分类统计内容数量并按阅读量求平均值的例子:
POST /article_index/_search { "size": 0, "aggs": { "by_category": { "terms": { "field": "category", "size": 10 }, "aggs": { "avg_read_count": { "avg": { "field": "read_count" } } } } } }size: 0表示不返回文档明细,只返回聚合结果,这样能大幅减少数据传输量。聚合字段必须是keyword或数值类型,如果发现聚合出来的桶数据不对,先检查字段类型是不是text,这个坑我在生产环境踩了不止一次。
5. 集群运维、性能优化与常见问题排查
5.1 集群健康度检查与节点角色规划
部署完成后第一件事就是看集群健康状态,用curl localhost:9200/_cluster/health返回的status字段,绿色表示所有主分片和副本都正常,黄色表示有副本未分配,红色表示有主分片缺失。黄色通常发生在节点数少于副本数的场景,比如你设置了 1 个副本但集群只有 1 个节点,属于预期内;红色则必须立即处理,一般是因为磁盘空间不足、节点宕机或分片分配策略限制。
ES 9.x 支持节点角色分离,把节点配置成node.roles指定的类型。常见角色有master(负责集群管理)、data(负责存储数据)、ingest(负责写入预处理)、ml(机器学习)。生产环境的最佳实践是用 3 个专用 master 节点承载集群元数据管理,用数据节点扛读写流量,这样数据节点宕机不会影响集群管理,master 节点也不会因为数据压力而卡顿。小规模场景可以合并角色,比如 3 节点集群每个节点同时承担 master 和 data 职责,依然可用,但我建议主节点至少要有 3 个,避免脑裂问题。minimum_master_nodes必须配置为 master 候选节点数的一半加一,这是防止集群出现多个 master 的关键配置。网络抖动的时候,如果这个参数没设对,两个节点可能各自认为自己是 master,出现脑裂,写请求会被分散到两个“集群”里,数据在后续恢复时可能冲突。
5.2 磁盘、内存和 JVM 调优实录
ES 是内存和磁盘都很吃紧的应用。磁盘方面,最优先的配置是给path.data使用独立的数据盘,不要把系统和数据放同一个分区。ES 在磁盘使用率超过 85% 时会对相关节点自动分配只读索引块,这个默认阈值可以改,但不建议调得太高,因为磁盘塞满后分片迁移、segment merge 都会失败,集群会进入恶性循环。我经历过一次事故,日志索引没有限制滚动策略,三个月后整个数据盘被塞满,ES 自动把索引设为只读,业务方反馈“所有写入都失败”,排查下来才发现磁盘已经 96% 了。所以有没有磁盘水位告警,直接在监控面板上配上。
内存方面,JVM 堆内存和操作系统文件缓存需要平衡。ES 强烈依赖 OS 的文件缓存来加速查询,把堆内存设得太大反而会挤占文件缓存空间。一般建议堆内存设为物理内存的一半,剩下的留给 OS 做 page cache。另外,不要同时装多个占用服务器大内存的组件(比如在同一台机器上跑 MySQL 和 ES),内存资源竞争会直接影响查询延迟。
jvm.options里还有 GC 日志参数可以打开,定位 stop-the-world 问题时 GC 日志是第一手资料。线上实例如果感觉查询越来越慢,先看节点 JVM 的 old 区占用是否持续高位,如果堆内存频繁 GC 甚至 OOM,优先检查是否存在字段类型映射导致的超大内存消耗,比如某个text字段配了高开销的分词器却没有实际搜索需求。
5.3 典型故障清单与排查命令
运维 ES 时间久了,会发现常见问题高度集中。我整理了一个速查表,按出现频率排序:
| 故障现象 | 常见原因 | 排查命令/方法 |
|---|---|---|
| 启动失败,报 vm.max_map_count 太低 | 系统参数未修改 | sysctl vm.max_map_count改为 262144 |
| 启动失败,报 can not run as root | 使用 root 用户启动 | 切换 esuser 用户启动 |
| 内存溢出 OOM | 堆内存设置过大或过小 | 检查 jvm.options,适配物理内存 |
| 索引写入报 FORBIDDEN/12/index read-only | 磁盘水位超过阈值 | 清理磁盘或调高水位(不建议超过90%) |
| 查询速度突然变慢 | 段数量过多或节点负载不均 | _cat/segments查看段数量,考虑 force merge |
| 分片未分配,集群 yellow/red | 节点数不足或磁盘空间不足 | _cat/shards查看未分配分片及原因 |
| 聚合结果不对 | 字段类型是 text | 检查 mapping,改成 keyword |
| 脑裂风险 | minimum_master_nodes 配置错误 | 修改 discovery 配置,重启集群 |
排查的通用思路是:先在_cluster/health看整体状态,再用_cat/nodes看节点 CPU、内存、磁盘,用_cat/indices看各索引体量和状态,用_cat/shards定位具体分片是否有异常。这套链路下来,大部分问题都能定位到根因。
日志模块也是 ES 运维里藏得深的坑。默认配置下 ES 的elasticsearch.log会记录所有运行日志,但业务索引的慢查询日志默认不开。要定位搜索接口“为什么慢”,在索引级别开慢查询日志:
PUT /article_index/_settings { "index.search.slowlog.threshold.query.warn": "1s", "index.search.slowlog.threshold.fetch.warn": "1s", "index.search.slowlog.level": "info" }开了之后,慢查询的执行详情会输出到logs目录下的index_search_slowlog.log文件,能清楚看到具体是哪个查询超时了,执行计划是什么样的。这个日志在优化查询时价值极大,排查问题的时候记得先看它。
5.4 索引生命周期管理与冷热架构
数据量持续增长是常态,不能总靠扩容硬扛。ES 的 Index Lifecycle Management(ILM)可以自动完成索引的滚动、备份和删除,按阶段配置不同策略。比如热数据阶段保留最近 7 天,写入和查询都在高性能节点;温数据阶段保留最近 30 天,定期合并 segment 降低存储开销;冷数据阶段超过 30 天的数据迁移到冷节点或者只读归档,节省机器成本。日志类、订单流水类、监控数据类业务强烈建议配 ILM,能在保留数据的同时有效控住存储成本。
冷热架构部署也很重要。给节点打box_type属性标签,比如node.attr.box_type: hot和node.attr.box_type: cold,然后在 ILM 策略里指定分配到哪个属性节点。这样热节点可以选高性能 SSD,冷节点配大容量机械盘,综合成本能省下不少。分片分配过滤也支持按标签做精细控制,比如某些索引只允许在指定的数据节点上运行,这在多租户环境里很有用。
6. 实用工具与生态组件配套
6.1 可视化工具和日常维护面板
命令行操作 ES 虽然万能,但日常巡检、看到直观的信息、调试复杂查询,还是得靠工具。Kibana 是 ES 官方配套的可视化面板,功能最全,支持数据探索、仪表盘、索引监控、日志分析,但重量级,资源占用和部署复杂度都高。
轻量级的替代方案有人用 Elasticvue 浏览器插件,也有人用基于浏览器部署的 ElasticHD,这都是开源项目,部署成本近乎零。日常管理如果只是看看索引状态、跑几个查询,这类轻工具足够用。我个人的习惯是:装机部署看 Kibana,日常开发调试用 REST 客户端(Postman 或 VS Code 的 REST Client 插件),服务器上快速验证直接走 curl。REST Client 插件特别好用,支持把 ES 查询保存成文件、用变量切换环境,适合团队分享文件、跨环境复用。
6.2 数据同步方案选型
业务数据存在 MySQL 里,需要同步到 ES,这是最常见的架构。同步方案有三个层级,按复杂度递增。
最简单的是业务代码双写,在写入 MySQL 的同时调用 ES 接口写入索引。优点是逻辑简单直接,适合数据量小、对一致要求不高的场景;缺点是如果 ES 写入失败,会造成两边数据不一致,得靠补偿任务兜底。
更可靠的是订阅 MySQL 的 binlog,通过 Canal 这类中间件把变更事件转发到 ES。优点是延迟低(毫秒到秒级)、不侵入业务代码、基本实时同步;缺点是要额外部署 Canal 和相关组件,维护成本更高。
还有一种方式是用 Logstash 定时增量抽取,适合离线同步场景或对实时性要求不高的业务,比如每 5 分钟同步一次运营报表数据。选型原则很朴素:不要为了追求技术栈的“高级感”上重方案,先看业务对数据延迟的容忍度。
6.3 搜索引擎应用实践里的非技术因素
最后聊点非技术的东西。ElasticSearch 很强大,但它不是银弹,也不是装了 ES 就能自动获得好的搜索体验。真正决定搜索质量的,是分词词典的完善程度、相关性调优的持续迭代、以及线上数据质量的把控。运营团队录入了大量残缺数据、分类字段胡乱填写,搜索引擎再强也没办法返回准确结果。所以做搜索项目,一定要有数据规范意识:字段必填、类型统一、分类一致、脏数据及时清洗。否则,每次查出来的结果有问题,用户下意识觉得是搜索系统不行,其实根源可能在数据源头。
另外,搜索引擎上线后要持续关注用户的查询日志。哪些词搜出来没结果、哪些词搜索次数很多但点击率低,这些都是搜索优化的金矿。通过分析查询日志,可以持续完善同义词表、扩展自定义词典、调整字段权重。搜索引擎不是一个一次性开发完就交付的系统,它更像一个需要持续运营的产品,花在上面的心思越多,业务侧体感越好。
7. 写在最后:一点个人经验
ElasticSearch 这技术,门槛不算高,入门很容易,但到了生产环境,真正拉开差距的地方全在细节里:映射设计是否合理、分片规划是否匹配集群规模、磁盘水位有没有监控、查询有没有走 filter 缓存、慢查询日志有没有开。我在实际项目中踩过的最大一个坑就是低估了 mapping 设计的重要性,前期图省事用动态映射,等数据量上来了发现字段类型不对,重建索引要迁移几百 GB 数据,那种痛希望大家不用经历。所以强烈建议所有准备上 ES 的项目,立项第一天就把 mapping 设计文档写好,把分片数和数据量预估对齐,哪怕后面有调整,也好过上线后再推倒重来。
最后再分享一个小技巧:每次对 ES 集群做配置变更之前,先用_cluster/health、_cat/indices、_cat/shards把当前状态完整记录一遍。变更后如果出现异常,对比前后状态能帮你快速定位是哪个环节出了问题。这个习惯陪了我好几年,救场无数次,希望对你也有用。