Elasticsearch 是搜索引擎和数据分析平台的事实标准 —— 日志检索、产品搜索、统计分析、可观测性、推荐召回背后都能看到它。但很多人对 ES 的使用停留在"当数据库用",写入慢、查询不准、集群崩溃然后吐槽 ES 难。这篇文章把 ES 的核心架构、倒排索引、相关性评分、分片复制、性能调优一次讲透。
倒排索引:ES 快的根基
传统数据库按"行"组织,要找包含 "kubernetes" 的文档,要扫所有行。倒排索引反过来:
原始文档:
doc 1: "kubernetes is a container orchestrator"
doc 2: "docker is a container platform"
doc 3: "kubernetes uses docker"
倒排索引(term -> postings):
"kubernetes" -> [doc 1, doc 3]
"is" -> [doc 1, doc 2]
"a" -> [doc 1, doc 2]
"container" -> [doc 1, doc 2]
"orchestrator" -> [doc 1]
"docker" -> [doc 2, doc 3]
"platform" -> [doc 2]
"uses" -> [doc 3]
查 "kubernetes" 只要看"kubernetes"那一行的 postings 列表 —— O(1) 命中。查 "kubernetes AND docker" 就求两个 postings 的交集。再加上 postings 内部按 doc_id 排序、用 skip list 加速、用压缩省空间 —— ES 单机就能扛亿级数据的检索。
分词:中文与英文的差异
建倒排索引前要先把文本切成 token。英文按空格 + 标点切就行;中文必须用专门的分词器。
# 默认 standard 分词器对中文是按字切,效果差
PUT /test/_analyze
{ "analyzer": "standard", "text": "我爱北京天安门" }
-> ["我", "爱", "北京", "天", "安", "门"] # 错的!
# 用 IK 分词器(中文必备)
PUT /test/_analyze
{ "analyzer": "ik_max_word", "text": "我爱北京天安门" }
-> ["我", "爱", "北京", "天安门"] # 对的
# 创建索引时指定分词器
PUT /articles
{
"mappings": {
"properties": {
"title": { "type": "text", "analyzer": "ik_max_word", "search_analyzer": "ik_smart" }
}
}
}
IK 有两个模式:ik_max_word 切得细(索引用,提高召回),ik_smart 切得粗(查询用,提高准确)。这种"indexer 用细分词,searcher 用粗分词"是中文搜索的常用做法。
相关性评分:TF-IDF 与 BM25
ES 默认用 BM25 算分,它是 TF-IDF 的进化版。核心思想:
- TF(词频):文档里这个词出现越多,越相关。但有上限(避免长文档天然占便宜)。
- IDF(逆文档频率):越多文档包含这个词,这个词的区分度越低。
- 文档长度归一化:同样的词在短文档里更"突出"。
# 查"kubernetes container",ES 给每篇匹配文档算分:
score = sum over each query term:
IDF(term) × (tf(term, doc) × (k1+1)) / (tf(term, doc) + k1 × (1-b + b × len(doc)/avg_len))
# k1 控制 tf 的影响(默认 1.2),b 控制长度归一化(默认 0.75)
你不需要记公式,但要知道:BM25 让搜索结果按相关性排序,这是 ES 区别于"WHERE 条件匹配"的根本。
常用 DSL 查询
# match:全文搜索
{
"query": {
"match": { "title": "kubernetes container" }
}
}
# match_phrase:必须相邻出现
{ "query": { "match_phrase": { "title": "kubernetes container" }}}
# term:精确匹配(不分词,常用于 keyword 字段)
{ "query": { "term": { "status": "active" }}}
# bool:组合查询
{
"query": {
"bool": {
"must": [{ "match": { "title": "k8s" }}], # 必须匹配
"filter": [{ "term": { "status": "published" }}], # 必须匹配但不算分
"should": [{ "match": { "tags": "devops" }}], # 锦上添花
"must_not": [{ "term": { "deleted": true }}] # 必须不匹配
}
}
}
# 范围查询
{ "query": { "range": { "created_at": { "gte": "2026-01-01", "lt": "2026-06-01" }}}}
# 地理空间
{ "query": { "geo_distance": { "distance": "10km", "location": "39.9,116.4" }}}
Aggregations:数据分析
# 按 tag 分组统计
{
"size": 0,
"aggs": {
"by_tag": {
"terms": { "field": "tags.keyword", "size": 20 }
}
}
}
# 嵌套聚合:每个 tag 下的平均价格
{
"size": 0,
"aggs": {
"by_category": {
"terms": { "field": "category" },
"aggs": {
"avg_price": { "avg": { "field": "price" }}
}
}
}
}
# 日期直方图(看每天发文数)
{
"size": 0,
"aggs": {
"daily": {
"date_histogram": { "field": "created_at", "calendar_interval": "day" }
}
}
}
这种"近实时数据分析"能力,让 ES 不只是搜索引擎,更是日志分析、监控、商业智能的核心组件。Elastic Stack(ELK)= Elasticsearch + Logstash + Kibana。
分片与副本
ES 把索引数据切成 shards 分布到多个节点。每个 shard 有 1 或多个 replica:
PUT /articles
{
"settings": {
"number_of_shards": 5,
"number_of_replicas": 1
}
}
# 5 主分片 + 5 副本 = 10 个 shard 散在集群
# 写入:发到主分片 -> 复制到副本
# 查询:任一 shard(主或副本)都能服务
注意:主分片数一旦设定不能改(因为 hash 路由)。replica 数可以动态调。常见错误是初期分片设太少,数据涨上来扩不动。规划时按"未来 1-2 年数据量 / 单 shard 50GB"估算 shard 数。
写入与查询流程
写入
1. 客户端发文档,ES 计算 hash(doc_id) % shard_count 决定主 shard
2. 主 shard 写入(内存 + translog 日志)
3. 转发给所有副本同步
4. 副本 ack 后,主 shard 给客户端返回成功
5. 后台周期性 refresh(默认 1 秒)生成倒排索引段,文档变可搜索
6. 后台周期性 flush 把内存数据写到磁盘段
查询
1. 客户端发查询(可以发到任意节点,该节点变成"协调节点")
2. 协调节点 fan-out 到所有相关 shard(主或副本)
3. 各 shard 在本地查询、算分、返回 top N 候选
4. 协调节点合并所有候选,全局排序取 top N
5. 对最终 top N 再"获取真实文档"(fetch phase)
6. 返回客户端
这种"查询分发 + 合并"是 ES 高吞吐的关键 —— 但也是性能瓶颈的来源。shard 太多导致每次查询要 fan-out 到太多节点,反而慢。
性能调优
1. 不要过度建索引
每个字段都索引会膨胀几倍。不查询的字段设 "index": false,只存不索引。
2. 用 keyword 还是 text
"name": {
"type": "text", # 用于全文搜索,会分词
"fields": {
"keyword": { "type": "keyword", "ignore_above": 256 } # 精确匹配 / 排序 / 聚合用
}
}
3. bulk 写入
POST /_bulk
{ "index": { "_index": "articles" }}
{ "title": "...", ... }
{ "index": { "_index": "articles" }}
{ "title": "...", ... }
# 批量比逐条快几十倍
4. 调 refresh_interval
对实时性要求不高的场景把 refresh_interval 从 1s 调到 30s,写入性能能提升 50%。
5. 冷热架构
近期热数据放 SSD 节点,旧冷数据迁到 HDD 节点,几个月前的数据归档到 S3(ES 7.10+ 支持 searchable snapshots)。日志场景非常实用。
常见坑
坑 1:把 ES 当主数据库。 ES 不保证 ACID,主从同步是异步的,故障切换可能丢数据。重要数据放 MySQL / PG,ES 是检索层。
坑 2:Mapping 一开始随便设。 ES 自动检测类型("123" 自动变 long、"2026-01-01" 自动变 date),后续想改要 reindex。生产前必须显式定义 mapping。
坑 3:深分页。 from=10000 + size=20 会让每个 shard 都返回 10020 个候选给协调节点。改用 search_after 或 scroll API。
坑 4:分词器选错。 中文用 standard 分词器搜索效果灾难。务必用 IK / Pinyin / 自定义。
坑 5:JVM heap 配太大。 ES 推荐 heap 不超过 32GB(过了会破坏 JVM 的 compressed oops 优化)。物理内存剩下的留给 OS file system cache —— Lucene 严重依赖它。
实战:用 ES 做日志聚合(ELK)
Elastic Stack 是日志分析的事实标准。整个流程:
应用产生日志(JSON 格式)
↓
Filebeat / Fluentd / Vector 采集
↓ (有时经过 Kafka 缓冲)
Logstash 或 Ingest Pipeline 解析、过滤、富化
↓
Elasticsearch 索引
↓
Kibana 可视化 / 告警
关键设计:
- 索引按时间切:每天一个索引(
logs-2026.05.15),老索引可以整体迁冷节点或归档。 - 用 ILM(Index Lifecycle Management):声明式管理索引的 hot / warm / cold / delete 各阶段。
- 日志结构化:不要存"整行原始文本",拆成字段(level、service、trace_id、user_id)。后续筛选 / 聚合都靠这些字段。
OpenSearch:ES 的开源分叉
2021 年 Elasticsearch 改用 SSPL / Elastic License,不再是纯开源。AWS 分叉出 OpenSearch(Apache 2.0)。现在主要选择:
- Elasticsearch 商业版:功能最全,有 ML、SQL、安全等高级功能。
- OpenSearch:免费开源,功能跟进略慢但够用。
- 云托管:AWS OpenSearch Service、Elastic Cloud、阿里云 ES,免运维。
向量搜索:ES 也能做
ES 8.0+ 内置 dense_vector 字段,支持 ANN(近似最近邻)向量搜索 —— 你不需要单独的 Milvus / Pinecone 也能做语义检索:
PUT /products
{
"mappings": {
"properties": {
"title": { "type": "text" },
"embedding": {
"type": "dense_vector",
"dims": 768,
"index": true,
"similarity": "cosine"
}
}
}
}
# 向量检索 + 关键词检索混合
POST /products/_search
{
"knn": {
"field": "embedding",
"query_vector": [0.1, 0.2, ...],
"k": 50, "num_candidates": 200
},
"query": { "match": { "title": "无线耳机" } }
}
这种"混合检索"是 RAG 系统的现代标配 —— ES 充当向量库 + 倒排索引一体方案,运维简单。
常用 API 速查
# 集群健康
GET /_cluster/health
# 看所有节点
GET /_cat/nodes?v
# 看所有索引
GET /_cat/indices?v
# 看 shard 分布
GET /_cat/shards?v
# 看慢查询(配置后)
GET /_nodes/stats/indices/search
# 强制段合并(老索引整理空间)
POST /old-index/_forcemerge?max_num_segments=1
# 创建快照
PUT /_snapshot/my_backup/snapshot_1?wait_for_completion=true
写在最后
ES 是看似简单实则深不见底的系统。"把数据扔进去能搜"几分钟就能上手,但要做出准确、快速、稳定的搜索,涉及分词、mapping、相关性调优、shard 规划、集群运维一连串学问。生产用 ES 的团队,务必有专人深入研究 —— 当作"把它当个黑盒数据库"用,迟早出事。
—— 别看了 · 2026