最佳实践与陷阱
Elasticsearch 全手册的 🚨 陷阱、💡 最佳实践和 🔬 深层原理汇总,按主题索引。
安装与配置
| # | 类型 | 描述 | 参考 |
|---|---|---|---|
| 1 | 🚨 | vm.max_map_count 过低 → ES 启动失败 |
00-安装与配置 |
| 2 | 🚨 | 宿主机数据目录权限不足(需要 chown 1000:1000) |
00-安装与配置 |
| 3 | 🚨 | 安装 IK / 拼音插件后未重启 → 不生效 | 00-安装与配置 |
| 4 | 🚨 | 插件版本与 ES 不匹配 → 节点启动失败 | 00-安装与配置 |
| 5 | 🚨 | -Xms 与 -Xmx 不相等 → JVM 动态 resizing 导致 GC 停顿 |
00-安装与配置 |
| 6 | 💡 | JVM 堆内存不超过物理内存 50%,且不超过 32GB(指针压缩边界) | 00-安装与配置 |
| 7 | 💡 | 剩余 50% 内存留给 OS 文件系统缓存(Lucene 严重依赖页缓存) | 00-安装与配置 |
| 8 | 💡 | 生产环境启用 X-Pack 安全认证;永远不要用 elastic 超级用户跑应用 |
00-安装与配置 |
| 9 | 💡 | 使用 Docker Compose 统一管理 ES + Kibana,避免手动 docker run |
00-安装与配置 |
核心概念与 Mapping
| # | 类型 | 描述 | 参考 |
|---|---|---|---|
| 1 | 🚨 | Type 已废弃(ES 6.x deprecated,7.x 移除) | 01-核心概念 |
| 2 | 🚨 | Dynamic Mapping 猜错类型——第一个值决定字段类型,后续不一致报错 | 01-核心概念 |
| 3 | 🚨 | index: false 字段不可搜索(但 _source 中仍可见) |
01-核心概念 |
| 4 | 🚨 | enabled: false 令整个 object 变成不索引的 blob,与 index 不同 |
01-核心概念 |
| 5 | 🚨 | doc_values: false 则字段不可排序/聚合 |
01-核心概念 |
| 6 | 🚨 | 关闭 _source → 无法 Reindex / 无法 _update |
01-核心概念 |
| 7 | 🚨 | 数组元素类型不一致([1, "two"])报错 |
01-核心概念 |
| 8 | 💡 | 几乎所有 text 字段都应配 .keyword 子字段(排序/聚合/精确过滤) |
01-核心概念 |
| 9 | 💡 | 生产环境用显式 Mapping + Dynamic Template,避免动态推断 | 01-核心概念 |
| 10 | 🔬 | 倒排索引记录词条 → 文档映射 + 位置 + 偏移量(phrase match 和 highlight 的基础) | 01-核心概念 |
| 11 | 🔬 | Refresh(1s)= 可搜索;Flush(30min)= 持久化;Merge = 清理碎片 | 01-核心概念 |
索引与文档操作
| # | 类型 | 描述 | 参考 |
|---|---|---|---|
| 1 | 🚨 | 索引删除不可恢复(物理删除,无回收站机制) | 02-索引与文档操作 |
| 2 | 🚨 | Mapping 已有字段不可修改(类型/分词器锁定)→ 只能 Reindex | 02-索引与文档操作 |
| 3 | 🚨 | PUT 全量替换 → 未传字段被删除;用 POST /_update 做增量 |
02-索引与文档操作 |
| 4 | 🚨 | _update 要求文档存在(不存在报 404)→ 用 upsert |
02-索引与文档操作 |
| 5 | 🚨 | Bulk API index vs create:index 覆盖旧文档,create 遇重复 ID 则报错 |
02-索引与文档操作 |
| 6 | 🚨 | 分片数创建后不可改 → 前期规划不足必须 Reindex | 02-索引与文档操作 |
| 7 | 💡 | 让 ES 自动生成文档 ID(POST 不带 ID),避免 ID 查重开销 | 02-索引与文档操作 |
| 8 | 💡 | 大批量写入前关闭 refresh_interval(-1),写完恢复 |
02-索引与文档操作 |
| 9 | 💡 | 零停机切换 = Alias 原子切换(remove old + add new) | 02-索引与文档操作 |
| 10 | 💡 | 用 Index Template + Component Template 统一管理 Mappings | 02-索引与文档操作 |
| 11 | 🔬 | ES 文档不可变 → _update 底层仍是"标记删除 + 写入新文档" |
02-索引与文档操作 |
查询
| # | 类型 | 描述 | 参考 |
|---|---|---|---|
| 1 | 🚨 | term 对 text 字段查不到(text 写入时已分词,term 不分词) |
03-查询详解 |
| 2 | 🚨 | match 默认 OR 关系 → "mill lane" 匹配 mill 或 lane |
03-查询详解 |
| 3 | 🚨 | filter 不参与评分 → 需要排序的场景必须用 must |
03-查询详解 |
| 4 | 🚨 | prefix / wildcard / regexp 是昂贵的前缀扫描,大索引上极慢 |
03-查询详解 |
| 5 | 🚨 | object 嵌套查询可能误匹配(字段交叉匹配到不同子对象)→ 用 nested |
03-查询详解 |
| 6 | 🚨 | hits.total 超过 10000 不精确 → 设 track_total_hits: true |
03-查询详解 |
| 7 | 🚨 | function_score 不加 max_boost → 热门文档压制所有其他结果 |
03-查询详解 |
| 8 | 💡 | 精确值过滤用 filter(自动缓存 bitset),全文相关性排序用 must |
03-查询详解 |
| 9 | 💡 | minimum_should_match 控制精度-召回平衡 |
03-查询详解 |
| 10 | 🔬 | BM25 引入词频饱和度——词频 1000 并非词频 100 的 10 倍权重 | 03-查询详解 |
排序、分页与高亮
| # | 类型 | 描述 | 参考 |
|---|---|---|---|
| 1 | 🚨 | from + size > 10000 直接报错(max_result_window 限制) |
04-排序分页与高亮 |
| 2 | 🚨 | search_after 排序值不唯一 → 数据丢失;末尾加 _id 保底 |
04-排序分页与高亮 |
| 3 | 🚨 | scroll 不清理 → 内存泄漏;用完务必 DELETE | 04-排序分页与高亮 |
| 4 | 🚨 | 高亮字段与搜索字段不同名 → require_field_match: false |
04-排序分页与高亮 |
| 5 | 💡 | 实时深度分页用 PIT + search_after(ES 7.10+),批量导出用 scroll | 04-排序分页与高亮 |
| 6 | 💡 | 指定 sort 后 ES 自动停止打分(_score: null),能显著提升查询性能 |
04-排序分页与高亮 |
| 7 | 💡 | 对频繁高亮的字段启用 term_vector: with_positions_offsets,加速高亮 |
04-排序分页与高亮 |
| 8 | 🔬 | 深度分页性能差的根源:每个分片返回 from+size 条 → 协调节点对 分片数 × (from+size) 条排序 |
04-排序分页与高亮 |
数据聚合
| # | 类型 | 描述 | 参考 |
|---|---|---|---|
| 1 | 🚨 | text 字段不可聚合 → 用 .keyword 子字段 |
05-数据聚合 |
| 2 | 🚨 | Terms 聚合默认只返回 Top 10 → 设置 size |
05-数据聚合 |
| 3 | 🚨 | 不设 size: 0 → 同时返回文档和聚合结果,浪费带宽 |
05-数据聚合 |
| 4 | 🚨 | cardinality 是近似计数(HyperLogLog++,约 2% 误差) |
05-数据聚合 |
| 5 | 🚨 | date_histogram 用错间隔类型(DST 日不应用 fixed_interval) |
05-数据聚合 |
| 6 | 🚨 | 子聚合排序语法 "子聚合名.指标"(如 "stats_age.avg")易错 |
05-数据聚合 |
| 7 | 💡 | 先过滤再聚合(query 缩小范围),而非聚合后再过滤桶 |
05-数据聚合 |
| 8 | 🔬 | Terms 聚合有分布式计数误差(各分片独立 Top N → 协调节点汇总) | 05-数据聚合 |
搜索补全
| # | 类型 | 描述 | 参考 |
|---|---|---|---|
| 1 | 🚨 | 搜索时也使用拼音分词器 → 噪音结果 | 06-搜索补全 |
| 2 | 🚨 | keep_original: false → 纯汉字搜索无法命中 |
06-搜索补全 |
| 3 | 💡 | 索引时拼音分词 + 搜索时 IK 分词(分析器分离策略) | 06-搜索补全 |
| 4 | 💡 | 中文场景用拼音分词方案,英文/标签场景用 Completion Suggester(FST) | 06-搜索补全 |
集群架构
| # | 类型 | 描述 | 参考 |
|---|---|---|---|
| 1 | 🚨 | 分片数创建后不可改(路由算法绑定 num_primary_shards) |
07-集群架构 |
| 2 | 🚨 | 默认节点角色混杂 → 生产应分离 master / data / coordinating | 07-集群架构 |
| 3 | 🚨 | master eligible 偶数 → 2 节点集群宕机一台即不可用 | 07-集群架构 |
| 4 | 🚨 | 单节点集群副本始终 unassigned → 集群状态 yellow |
07-集群架构 |
| 5 | 🚨 | 集群 yellow 忽视 → 副本无冗余,再宕一台即数据丢失 | 07-集群架构 |
| 6 | 🚨 | 未配置 Rack Awareness → 所有副本可能在同一物理机架 | 07-集群架构 |
| 7 | 💡 | 每个索引至少 1 个副本(number_of_replicas: 1),确保宕机不丢 |
07-集群架构 |
| 8 | 💡 | 定时快照 + SLM 自动管理快照生命周期 | 07-集群架构 |
| 9 | 💡 | 时序数据用 Hot-Warm-Cold 分层 + ILM 自动迁移 | 07-集群架构 |
| 10 | 🔬 | ES 7.0+ 内置 Raft-like 协调机制,脑裂问题一般不会发生 | 07-集群架构 |
性能调优 checklist
| # | 类别 | 建议 | 影响 |
|---|---|---|---|
| 1 | 写入 | 批量写入时 refresh_interval: -1,写完恢复 |
写入吞吐提升 2-5x |
| 2 | 写入 | 使用 _bulk API,批次 5-15MB,注意 ES 的 HTTP 体大小限制 |
减少网络往返 |
| 3 | 写入 | number_of_replicas: 0 先写再恢复副本(大批量导入) |
写入吞吐翻倍 |
| 4 | 写入 | 让 ES 自动生成 _id(避免 ID 查重) |
降低写入延迟 |
| 5 | 查询 | filter 优先于 must(filter 自动缓存) |
重复查询 10-100x 加速 |
| 6 | 查询 | size: 0 用于纯聚合查询,不返回 hits |
减少网络传输 |
| 7 | 查询 | 限制 from + size,用 search_after 做深度分页 |
避免 OOM |
| 8 | 索引 | 用 _source 过滤只返回需要的字段 |
减少 IO 和网络 |
| 9 | 索引 | doc_values: false 用于不需要排序/聚合的字段 |
节省磁盘 |
| 10 | 硬件 | JVM 堆不超过 32GB(指针压缩边界),剩余内存留给 OS 页缓存 | 查询性能核心 |
| 11 | 硬件 | 使用 SSD,尤其是 Hot 节点 | IOPS 决定写入和查询延迟 |
| 12 | 架构 | Hot-Warm-Cold 分层 + ILM 自动管理 | 节省 50-70% 硬件成本 |
安全加固 checklist
| # | 建议 |
|---|---|
| 1 | 启用 X-Pack Security(ES 8.x+ 默认开启) |
| 2 | 永远不要用 elastic 超级用户运行应用程序 |
| 3 | 为每个应用创建最小权限的 API Key 或角色 |
| 4 | 生产环境启用 HTTPS(xpack.security.http.ssl.enabled: true) |
| 5 | 节点间通信启用 TLS(xpack.security.transport.ssl.enabled: true) |
| 6 | Kibana 不暴露公网 → 使用 SSH 隧道或 VPN |
| 7 | 定期审计 GET /_security/_authenticate |
常用监控命令
# 集群健康
GET /_cluster/health?level=indices
# 节点统计
GET /_nodes/stats/indices,jvm,os,fs
# 索引信息
GET /_cat/indices?v&s=store.size:desc
# 分片信息
GET /_cat/shards?v
# 慢查询
GET /_cluster/settings?include_defaults=true&filter_path=*.search.slowlog
# 待处理任务
GET /_cluster/pending_tasks
# Recovery 进度
GET /_cat/recovery?v
# 线程池统计
GET /_cat/thread_pool?v
# 段信息(检查 Merge 情况)
GET /_cat/segments?v
# Fielddata 使用情况
GET /_cat/fielddata?v