核心概念
Elasticsearch 是一款基于 Apache Lucene 构建的实时分布式搜索与分析引擎。理解 Lucene 的基本原理和 ES 的核心概念是高效使用 ES 的前提。
架构全景

Lucene 基础
ES 的底层是 Lucene,了解 Lucene 有助于理解 ES 的行为。
倒排索引(Inverted Index)
倒排索引是搜索引擎的核心数据结构。它将文档 → 词条的正向关系反转为词条 → 文档的映射。
文档1: "Elasticsearch is fast"
文档2: "Elasticsearch is powerful"
倒排索引:
"elasticsearch" → [文档1, 文档2]
"is" → [文档1, 文档2]
"fast" → [文档1]
"powerful" → [文档2]🔬 深入原理:倒排索引不仅记录了词条出现在哪些文档中,还记录了位置(position)和偏移量(offset),这正是短语匹配(match_phrase)和高亮功能的基础。
段(Segment)
- Lucene 将索引数据写入不可变的段(Segment)
- 新文档写入新段,已存在的段不会修改
- 文档更新 = 标记旧文档删除 + 写入新段
- 段的数量过多会影响搜索性能,Lucene 后台自动合并(Merge)
🔬 深入原理:这种"不可变段"设计带来两个核心优势:(1)写操作不需要加锁,天然支持高并发写入;(2)段可以在多个查询间共享文件系统缓存。代价是删除/更新操作不会立即释放磁盘空间——需要通过段合并进行物理清理。
关键写入流程
文档写入 → 内存 Buffer(不可搜索)
→ refresh(每 1s)→ 生成新 Segment → 可搜索
→ translog 持久化 → 防止宕机丢失
→ flush → 提交到磁盘| 操作 | 触发条件 | 效果 |
|---|---|---|
| refresh | 每 1s 自动 | 文档变为可搜索 |
| flush | 每 30min 或 translog 满 | 文档持久化到磁盘 |
| merge | 后台自动 | 合并小段,释放磁盘空间 |
💡 最佳实践:大批量写入时,可临时调高
refresh_interval(如-1关闭),写完再恢复,减少段碎片。
核心名词
Near Realtime(NRT)— 近实时
数据写入后,经过 refresh 即可被搜索(默认 1s 延迟)。ES 不是即时性的 OLTP 数据库,但在搜索场景下这一延迟通常可接受。
Cluster — 集群
- 由一个唯一的集群名称标识,默认
"elasticsearch" - 相同集群名的节点自动组网(通过
discovery.seed_hosts发现) - 集群名在
elasticsearch.yml中配置
Node — 节点
- 集群中的一台服务器实例,存储数据和/或参与查询
- 默认启动时以随机 UUID 前 7 位作为节点名,可通过
node.name自定义 - 通过集群名称在网络中自动发现同伴并组成集群
Index — 索引
- 一个逻辑命名空间,包含一组具有相同结构的文档
- 类比关系数据库的 Database(ES 6.x 之前:Index ≈ Database + Table)
- 一个集群可以有任意多个索引
- 索引命名:小写字母 + 连字符,如
order-logs-2025
Type — 类型(已废弃)
- ES 6.0 起 Deprecated,ES 7.0 起完全移除
- 一个索引只能存一类数据的 Mapping
- 如果你的旧项目使用了
_type,迁移方案:每种类型拆成独立索引
Document — 文档
- ES 中的最小数据单元,以 JSON 格式表示
- 每个文档有唯一的
_id(手动指定或自动生成) - 写入后会被序列化为 JSON 存入
_source字段
Shard — 分片
- 索引在创建时可以指定切分为多个分片
- 每个分片本身是一个独立完整的 Lucene 索引
- 分片可以分布在集群的任意节点上
两种分片:
| 类型 | 说明 | 数量 |
|---|---|---|
| Primary Shard(主分片) | 数据的第一存放位置 | 创建索引时指定,不可修改 |
| Replica Shard(副本分片) | 主分片的复制品 | 可动态修改 |
Replica — 副本
| 作用 | 说明 |
|---|---|
| 高可用 | 主分片宕机时,副本可以提升为主分片 |
| 提升读吞吐 | 副本可以提供读服务,分担查询负载 |
| 不提升写吞吐 | 写入必须先写主分片,再同步到副本 |
💡 最佳实践:ES 自动确保主分片和其副本不会分配到同一节点。生产环境至少设置
number_of_replicas: 1。
数据库类比
| ES | 关系数据库 |
|---|---|
| Index | Database |
| Table | |
| Document | Row |
| Mapping | Schema(DDL) |
| Field | Column |
| Shard | 水平分表 |
| Replica | 读副本 / Standby |
Mapping 映射
Mapping 定义索引中文档的字段结构和行为。ES 支持两种 Mapping 方式:
| 方式 | 说明 | 适用场景 |
|---|---|---|
| Dynamic Mapping | 自动推断字段类型 | 快速上手、日志类数据 |
| Explicit Mapping | 手动定义每个字段 | 生产环境、需要精确控制分词和索引行为 |
🚨 陷阱:Dynamic Mapping 会"猜"字段类型,新字段第一次写入的类型锁定后续所有文档。例如第一次写入
"count": 100,类型锁定为long;后再写入"count": "N/A"会报错。
字段数据类型(完整版)
| 大类 | 具体类型 | 说明 |
|---|---|---|
| 字符串 | text |
全文搜索,会被分词 |
keyword |
精确匹配,不参与分词(用于排序/聚合/过滤) | |
wildcard |
适合模糊匹配的 keyword(ES 7.9+) | |
| 数值 | long / integer / short / byte |
有符号整数 |
double / float / half_float |
浮点数 | |
unsigned_long |
无符号长整型 | |
| 日期 | date |
支持多种格式(时间戳/ISO8601/自定义) |
date_nanos |
纳秒精度日期 | |
| 布尔 | boolean |
true / false |
| 对象/关系 | object |
嵌套 JSON 对象(会被扁平化) |
nested |
独立索引的嵌套对象,保持关联性 | |
join |
父子关系(同一索引内的 join) | |
| 范围 | integer_range / date_range / ip_range 等 |
范围查询专用 |
| IP | ip |
IPv4 / IPv6 地址 |
| 二进制 | binary |
Base64 编码的二进制数据 |
| 地理位置 | geo_point |
经纬度坐标 |
geo_shape |
多边形、线段等复杂地理形状 | |
| 专用 | completion |
搜索自动补全(Completion Suggester) |
dense_vector |
向量存储(用于 kNN 搜索) | |
rank_feature |
用于影响评分但不参与过滤的数字特征 |
常用 Mapping 参数
| 参数 | 说明 | 默认值 |
|---|---|---|
type |
字段数据类型 | — |
index |
是否建立倒排索引(是否可搜索) | true |
enabled |
是否解析该对象字段(false 则整段 JSON 不索引) |
true |
doc_values |
是否启用列存储(用于排序/聚合) | true(text 除外) |
store |
是否独立存储(不依赖 _source) |
false |
analyzer |
索引时分词器 | standard |
search_analyzer |
搜索时分词器 | 同 analyzer |
ignore_above |
超过此长度的字符串不索引 | 256(keyword) |
format |
日期格式(date 类型) | — |
null_value |
字段为 null 时的替代值 | — |
copy_to |
将值复制到目标字段(用于多字段搜索) | — |
Multi-Field(多字段映射)
一个字段同时拥有多种索引方式,常见模式:text(全文搜索)+ keyword(精确匹配/排序/聚合)。
{
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "ik_max_word",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}使用:
- 全文搜索:
title→"match": { "title": "搜索引擎" } - 精确匹配/聚合:
title.keyword→"term": { "title.keyword": "搜索引擎" }
💡 最佳实践:几乎所有
text字段都应该配一个.keyword子字段,用于排序、聚合和精确过滤。
数组字段
ES 没有数组类型。字段声明为 long,写入 [1, 2, 3] 时,ES 会自动将其索引为多个值。
// Mapping
{ "tags": { "type": "keyword" } }
// 写入(两种写法等价)
{ "tags": "es" }
{ "tags": ["es", "search", "database"] }🚨 陷阱:数组中的各元素类型必须一致。
[1, "two"]会报错(第一个元素1将字段类型锁定为long)。
Dynamic Mapping 规则
当未定义 Mapping 时,ES 根据 JSON 值自动推断类型:
| JSON 类型 | ES 类型 |
|---|---|
null |
不添加字段 |
true / false |
boolean |
123 |
long |
123.45 |
float |
"2024-01-01" |
date(可配置格式) |
"foo" |
text + keyword 子字段 |
{} |
object |
Dynamic Template(动态模板)
按字段名模式自动匹配 Mapping 规则:
{
"mappings": {
"dynamic_templates": [
{
"strings_as_keywords": {
"match_mapping_type": "string",
"mapping": { "type": "keyword" }
}
}
]
}
}_source 字段
_source 存储文档的原始 JSON。
| 操作 | 是否依赖 _source |
|---|---|
| 搜索返回结果 | ✅ |
| 高亮显示 | ❌(单独存储位置信息) |
| Reindex | ✅ |
_update |
✅(读取旧值 + 合并新值) |
| 聚合 | ❌(使用 doc_values) |
💡 最佳实践:生产环境保留
_source(默认开启)。关闭可节省磁盘空间,但会失去 Reindex、_update和 debug 能力。如需节省存储,建议使用_source过滤而非完全关闭。
常见陷阱
| 陷阱 | 说明 |
|---|---|
| 🚨 Type 已废弃 | ES 6.0 起一个索引只能存一类数据;7.0 起完全移除 _type |
| 🚨 Dynamic Mapping 猜错类型 | 第一个值决定字段类型,后续类型不一致会报错 |
🚨 index: false 不可搜索 |
设为 false 后字段不参与搜索,但 _source 中仍然可见 |
🚨 enabled: false 整对象不可搜 |
与 index: false 不同,整个嵌套 JSON 变成不索引的 blob |
🚨 doc_values: false 不可排序/聚合 |
text 类型默认关闭 doc_values,需排序时使用 .keyword 子字段 |
| 🚨 数组元素类型不一致 | [1, "two"] 报错,所有元素必须同类型 |
🚨 _source 关闭后无法 Reindex |
没有 _source 无法索引重建 |