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 个副本,既能容灾又能分担读压力。

一个小建议:刚开始不确定分片数的话,宁可设多一点,别设少了。主分片数多了可以合并,少了可没法拆分。

Elasticsearch 核心概念关系图 索引(Index) 文档的容器 文档(Document) 数据的基本单元 映射(Mapping) 字段类型与规则 分片(Shard) 水平扩展的核心单元 包含 定义 切分为 索引包含文档,通过映射定义结构,并切分为分片分布到集群各节点

把这四个概念串起来

现在回头看,ES 的数据组织逻辑其实很清晰:

  • 你有一条审计日志 —— 它就是一条 文档。
  • 所有同类文档放在一起 —— 形成一个 索引。
  • 每个字段存成什么类型、怎么搜索 —— 靠 映射 来定义。
  • 数据量大到一台机器扛不住 —— ES 自动把索引切成 分片,分布到集群里。

这四兄弟配合起来,ES 才能做到既灵活又扛得住海量数据。你在做运维审计日志的时候,脑子里时刻要有这个分层结构——建索引时想好映射,写文档时注意字段类型,规划集群时算好分片数。每一步都踩稳了,后面查日志、做报表、追问题才能信手拈来。