Py学习  »  Elasticsearch

Elasticsearch 避坑指南:我在项目中总结的 14 条实用经验

稀土掘金技术社区 • 5 天前 • 143 次点击  

图片(💰TRAE SOLO实战赛强势上线,速戳上图了解详情🔍)

刚开始接触 Elasticsearch 时,我觉得它就像个黑盒子——数据往里一扔,查询语句一写,结果就出来了。直到负责公司核心业务的搜索模块后,我才发现这个黑盒子里面藏着无数需要注意的细节。


今天就把我在实际项目中积累的 ES 使用经验分享给大家,主要从「索引设计」「字段类型」「查询优化」「集群管理」「架构设计」这几个方面来展开。

20251102-4.jpg

索引设计:从基础到进阶

1. 索引别名(alias):为变更留条后路

刚开始做项目时,我习惯直接用索引名。直到有一次需要修改字段类型,才发现 「ES 不支持直接修改映射,也不支持修改主分片数」,必须重建索引。(**新增字段是可以的)

「解决方案很简单」:使用「索引别名」。业务代码中永远使用别名,重建索引时只需要切换别名的指向,整个过程用户无感知。

这就好比给索引起了个"外号",里面怎么换内容都不影响外面的人称呼它。

2. Routing 路由:让查询更精准

在做 「SaaS 电商系统」时,我发现查询某个商家的订单数据特别慢。原来,「默认情况下ES根据文档ID的哈希值分配分片」,导致同一个商家的数据分散在不同分片上。

「优化方案」:使用商家 ID 作为 「routing key」,存储和查询数据时指定routing key。这样,同一个商家的所有数据都会存储在同一个分片上。

「效果对比」

  • 优化前:查询要扫描所有分片(比如3个分片都要查)
  • 优化后:只需要查1个分片
  • 「结果」:查询速度直接翻倍,资源消耗还更少

3. 分片拆分:应对数据增长

当单个索引数据量持续增长时,单纯增加分片数并不是最佳方案。

「我的经验是」

  • 「业务索引」:单个分片控制在 10-30GB
  • 「搜索索引」:10GB 以内更合适
  • 「日志索引」:可以放宽到 20-50GB

对于 SaaS 系统,ES单索引数据较大,且存在“超级大商户”,导致数据倾斜严重时,可以按「商家ID%64取模」进行索引拆分,比如 orders_001 到 orders_064,每个索引包含部分商家的数据,然后再根据商户ID指定routing key。

请根据「业务数据量」「业务要求」,选择最适合的「分片拆分规则」 和「routing key路由算法」,同时不要因为拆分不合理,导致ES节点中存在大量分片。

ES默认「单节点分片最大值为1000」(7.0版本后),可以参考ES官方建议,「堆内存分片数量」维持大约「1:20」的比例


字段类型:选择比努力重要

4. Text vs Keyword:理解它们的本质区别

曾经有个坑:用户手机号用 「text 类型」存储,结果搜索完整的手机号却搜不到。原来 text 类型会被分词,13800138000 可能被拆成 13800138000 等片段。

「正确做法」

  • 需要分词搜索的用 「text」(如商品描述)
  • 需要精确匹配的用  「keyword」(如订单号、手机号),适合 「term、terms」 等精确查询
  • 「效果」:keyword 类型的 term 查询速度更快,存储空间更小

5. 多字段映射(multi-fields):按需使用不浪费

ES 默认会为 text 字段创建 keyword 子字段,但这并不总是必要的。

「我的选择」

  • 确定字段需要「精确匹配和聚合」时:启用 「multi-fields」
  • 只用于全文搜索时:禁用 multi-fields
  • 「好处」:节省存储空间,提升写入速度

6. 排序字段:选对类型提升性能

用 keyword 字段做数值排序是个常见误区。比如价格排序,100 会排在 99 前面,因为它是按字符串顺序比较的。

「推荐做法」

  • 数值排序:用 「long、integer」 类型
  • 时间排序:用 「date」 类型
  • 「提升效果」:排序速度提升明显,内存占用也更少

查询优化:平衡速度与精度

7. 模糊查询:了解正确的打开方式

在 「ES 7.9 之前」「wildcard 查询」是个性能陷阱。它基于正则表达式引擎,前导通配符会导致全量词项扫描。

「现在的方案」

  • 「ES7.9+」:使用 「wildcard 字段类型」
  • 「优势」:底层使用优化的 「n-gram」 + 「二进制 doc value」 机制,性能提升显著

提示:ES7.9前后版本wildcard的具体介绍,可以参考我的上一篇文章

《与产品经理的“模糊”对决:Elasticsearch实现MySQL LIKE '%xxx%'》

8. 分页查询:避免深度分页的坑

产品经理曾要求实现"无限滚动",我展示了深度分页的性能数据后,大家达成共识:「业务层面避免深度分页才是根本解决方案」。就像淘宝、Google 这样的大厂,也都对分页做了限制,这不仅是技术考量,更是用户体验的最优选择。

「技术方案」(仅在确实无法避免时考虑):

  • 「浅分页」:使用 from/size,适合前几页的常规分页
  • 「Scroll」:适合大数据量导出,但需要维护 scroll_id 和历史快照,对服务器资源消耗较大
  • 「search_after」:基于上一页最后一条记录进行分页,但无法跳转任意页面,且频繁查询会增加服务器压力

需要强调的是,这些技术方案都存在各自的局限性,「业务设计上的规避始终是最佳选择」


集群管理:保障稳定运行

9. 索引生命周期:自动化运维

日志数据的特点是源源不断,如果不加管理,磁盘很快就会被撑满。

「我的做法」

  • 按天创建索引(如 log_20231201)
  • 设置保留策略(保留7天或30天)
  • 结合模板自动化管理

10. 准实时性:理解刷新机制

很多新手会困惑:为什么数据写入后不能立即搜索?

「原理」「ES 默认 1 秒刷新一次索引」,这是为了在实时性和写入性能之间取得平衡。

「调整建议」

  • 实时性要求高:保持 1s
  • 写入量大:适当调大 refresh_interval

「补充说明」:如果需要更新后立即能查询到,通常有两种方案:

  1. 让前端直接展示刚提交的数据,等下一次调用接口时再查询 ES
  2. 更新完后,前端延迟 1.5 秒后再查询

「关键点」:业务需求不一定都要后端实现,可以结合前端一起考虑解决方案。

11. 内存配置:32G 限制的真相

为什么 ES 官方建议不要超过 32G 内存?

「技术原因」:Java 的「压缩指针技术」在 32G 以内有效,超过这个限制会浪费大量内存。

「实践建议」:单个节点配置约50%内存,留出部分给操作系统。


架构设计:合理的分工协作

12. ES 与数据库:各司其职

曾经试图在 ES 里存储完整的业务数据,结果遇到「数据一致性」问题。

「现在的方案」

  • 「ES」:存储搜索条件和文档 ID
  • 「数据库」:存储完整业务数据
  • 「查询」:ES 找 ID,数据库取详情

「好处」:既享受 ES 的搜索能力,又保证数据的强一致性。

13. 嵌套对象:保持数据关联性

处理商品规格这类数组数据时,用普通的 「object 类型」会导致数据扁平化,破坏对象间的关联。

「解决方案」:使用 「nested 类型」,保持数组内对象的独立性,确保查询结果的准确性。

14. 副本配置:读写平衡的艺术

副本可以提升查询能力,但也不是越多越好。

「经验值」

  • 大多数场景:「1 个副本」足够
  • 高查询压力:可适当增加
  • 「注意」:副本越多,写入压力越大

写在最后

这些经验都是在解决实际问题中慢慢积累的。就像修路一样,开始可能只是简单铺平,随着车流量的增加,需要不断优化——设置红绿灯、划分车道、建立立交桥。使用 ES 也是同样的道理,随着业务的发展,需要不断调整和优化。

最大的体会是:「理解原理比记住命令更重要」。只有明白了为什么这样设计,才能在遇到新问题时找到合适的解决方案。

如果有人问我:"ES 怎么才能用得更好?"我的回答是:"先理解业务场景,再选择技术方案。就像我们之前做的模糊搜索,不是简单地用 wildcard,而是根据 ES 版本选择最优解。"

「技术的价值不在于多复杂,而在于能否优雅地解决实际问题。与大家共勉。」

图片
"" ~

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/189375