Elasticsearch 核心概念:索引、文档、分片、映射类型
刚开始接触 ES 的兄弟,最容易懵的就是这四个词。索引?文档?分片?映射类型?听起来都熟,但跟 MySQL 里那套完全不是一回事。说白了,这四位就是 ES 世界的"底层宪法"。搞懂了它们,后面的所有操作才有了根基。
1. 文档(Document)—— 数据的基本单元
在 ES 里,一条数据就是一个文档。你可以直接把它理解成 JSON 对象。每个文档都有一堆元数据,最常见的是下面这几个:
| 元数据字段 | 说明 | 示例值 |
|---|---|---|
| _index | 文档所属的索引 | audit_logs |
| _type | 类型(7.x 已废弃,保留兼容) | _doc |
| _id | 文档唯一标识 | log-20240115-001 |
| _source | 原始 JSON 数据体 | { "user": "zhangsan", … } |
一条典型的审计日志文档
{
"_index": "audit_logs",
"_type": "_doc",
"_id": "log-20240115-001",
"_score": 1.0,
"_source": {
"timestamp": "2024-01-15T10:30:00+08:00",
"user": "zhangsan",
"action": "login",
"target": "jump_server_01",
"status": "success",
"client_ip": "192.168.1.100",
"duration_ms": 320
}
}
我个人习惯把文档看作 ES 里的"行",虽然这么说不太严谨,但对刚从关系型数据库转过来的运维兄弟来说,好理解。
我的经验:_id 尽量用业务主键,别让 ES 自动生成。比如审计日志可以用 "环境_日期_序号" 格式。查问题的时候,凭 _id 一秒就能定位到具体日志,不用翻半天。
2. 索引(Index)—— 文档的"收纳盒"
索引就是存文档的地方。你想想看,一堆审计日志如果散着放,查起来得疯掉。索引帮我们把同类文档归到一起。
类比一下:
- 索引 ≈ 关系型数据库里的"表"
- 文档 ≈ 表里的"行"
不过 ES 的索引比表灵活得多——它天然支持全文搜索、聚合分析,还能自动处理 schema 变化。
索引命名有硬规矩:全小写,不能包含 \ / * ? " < > | , # 这些特殊字符,长度不超过 255 字节。我一般按 业务_环境_日期 的格式来命名,比如 ops_prod_20240115。这个习惯帮我省了不少事,光看名字就知道数据从哪来、什么时候产生的。
注意:别用大写字母!ES 会把大写自动转成小写,但命名里混了大小写容易把自己搞晕。统一小写,别给自己挖坑。
3. 映射(Mapping)—— 你说了不算,映射说了算
映射定义了文档里每个字段的类型和规则。比如 timestamp 是 date 类型,action 是 keyword 类型,duration_ms 是 integer 类型。没有映射,ES 不知道该怎么存、怎么查。
ES 有两种映射方式:
- 动态映射:自动识别字段类型。省事,但有风险。
- 显式映射:手动定义每个字段。麻烦,但可控。
动态映射确实智能,但坑也不少。我曾经踩过最典型的坑——系统自动把某个数值字段识别成了 float,结果聚合统计时精度丢失,小数点后面几位全对不上。排查了大半天才发现是映射类型搞错了。从那以后,生产环境的索引我都用显式映射,一条一条写清楚。
下面这些字段类型是日常用得最多的:
| 字段类型 | 说明 | 审计日志中的典型场景 |
|---|---|---|
| text | 全文搜索,会被分词 | 日志消息、命令内容 |
| keyword | 精确匹配,不分词 | 用户名、状态码、IP 地址 |
| integer / long | 整数类型 | 执行时长、返回行数 |
| date | 日期 / 时间戳 | 操作时间、登录时间 |
| boolean | 布尔值 | 是否成功、是否告警 |
| object | 嵌套 JSON 对象 | 来源信息、客户端详情 |
再说说"映射类型"这个历史包袱。在 ES 7.x 之前,一个索引下可以有多个"类型"(Type),类似于表里的逻辑分区。但从 7.x 开始,类型被废弃了。现在一个索引只有一个 _doc 类型。说白了,ES 团队觉得 Type 这个抽象层没什么实质意义,反而增加了复杂度。如果你的索引还是在 6.x 版本创建的,迁移到 7.x+ 时一定要注意去掉 Type 字段,不然会报错。
显式映射示例(针对审计日志)
PUT /audit_logs
{
"mappings": {
"properties": {
"timestamp": { "type": "date" },
"user": { "type": "keyword" },
"action": { "type": "keyword" },
"target": { "type": "keyword" },
"status": { "type": "keyword" },
"client_ip": { "type": "ip" },
"duration_ms": { "type": "integer" },
"message": { "type": "text" }
}
}
}
4. 分片(Shard)—— 让数据不再单打独斗
分片是 ES 实现水平扩展的核心机制。一个索引的数据会被切成多个分片,分散到集群的不同节点上。这样做有两个好处:一是数据量可以突破单机限制,二是查询可以并行跑。
分片分两种:
| 分片类型 | 读写能力 | 数量能否修改 | 主要作用 |
|---|---|---|---|
| 主分片(Primary Shard) | 读 + 写 | 创建时指定,不可修改 | 数据写入和索引 |
| 副本分片(Replica Shard) | 只读 | 可动态调整 | 容灾 + 分担读压力 |
主分片数量为什么不能改?因为 ES 用文档的 _id 和分片数做哈希路由:hash(_id) % number_of_primary_shards。改了分片数,路由就全乱了,数据找不到了。
我建议:主分片数按未来的数据量来估算。举个例子——一天产生 10 GB 审计日志,保留 30 天,总量 300 GB。如果集群有 5 个数据节点,分片数可以设为 10~15 个。每个分片 20~30 GB 是比较舒服的量。副本分片就看你多需要高可用了,一般至少 1 个副本,既能容灾又能分担读压力。
一个小建议:刚开始不确定分片数的话,宁可设多一点,别设少了。主分片数多了可以合并,少了可没法拆分。
把这四个概念串起来
现在回头看,ES 的数据组织逻辑其实很清晰:
- 你有一条审计日志 —— 它就是一条 文档。
- 所有同类文档放在一起 —— 形成一个 索引。
- 每个字段存成什么类型、怎么搜索 —— 靠 映射 来定义。
- 数据量大到一台机器扛不住 —— ES 自动把索引切成 分片,分布到集群里。
这四兄弟配合起来,ES 才能做到既灵活又扛得住海量数据。你在做运维审计日志的时候,脑子里时刻要有这个分层结构——建索引时想好映射,写文档时注意字段类型,规划集群时算好分片数。每一步都踩稳了,后面查日志、做报表、追问题才能信手拈来。