上一篇【第01篇】Elasticsearch是什么
下一篇【第03篇】Elasticsearch快速入门实战
摘要
本文是系列第二篇,系统性地剖析Elasticsearch的核心概念体系。关键词包括:Index、Document、Shard、Replica、Mapping、Node、Cluster。文章从传统关系数据库的概念出发,通过类比帮助读者理解ES中的Index(索引)、Document(文档)、Field(字段)三要素。随后深入讲解分片(Shard)和副本(Replica)的分布式机制,最后介绍Mapping与Schema的对比以及Node(节点)与Cluster(集群)的关系。以电商商品数据为例,贯穿所有概念,让你对ES的数据模型建立清晰、完整的认知。
一、从关系数据库到ES:概念类比
如果你有MySQL或类似关系数据库的使用经验,理解Elasticsearch的核心概念会轻松很多。下面是两者的概念对照:
| 关系数据库(MySQL) | Elasticsearch | 说明 |
|---|---|---|
| Table(表) | Index(索引) | 数据的逻辑分组 |
| Row(行) | Document(文档) | 一条数据记录 |
| Column(列) | Field(字段) | 数据的属性 |
| Schema | Mapping | 数据结构定义 |
| SQL | Query DSL | 查询语言 |
| SELECT | Search API | 数据检索 |
| INSERT/UPDATE | Index API | 数据写入 |
这个类比非常直观,但要记住:ES本质上不是关系数据库,它们的底层实现完全不同。这些概念类比只是为了帮助理解,不能混为一谈。
二、Index(索引)详解
2.1 Index是什么
在Elasticsearch中,Index是具有相似特性的文档集合。举个例子,一个电商系统可以创建三个索引:
products——存储商品数据orders——存储订单数据users——存储用户数据
在概念上,Index类似于MySQL中的数据库,而不是Table。ES6.x之前曾经有Type的概念(每个Index下可以有多个Type,类似表),但从7.0版本开始Type已被彻底移除,现在一个Index对应一类数据是最佳实践。
2.2 Index的构成
每个Index由以下部分组成:
Index(索引) ├── 分片0(Shard 0) │ ├── 主分片(Primary Shard) │ └── 副本分片(Replica Shard) ├── 分片1(Shard 1) │ ├── 主分片 │ └── 副本分片 └── ...2.3 Index命名规则
- 只能使用小写字母
- 不能以
_、-、+开头 - 不能包含
\、/、*、?、"、<、>、|、空格、逗号、#、: - 长度不能超过255字节
最佳实践:使用全小写的英文单词,用下划线或横线连接,如product_catalog、order-2024。
三、Document(文档)详解
3.1 Document是什么
Document是ES中可以被索引的基本信息单元,用JSON格式表示。一个Document属于一个Index。
以下是一个电商商品文档的示例:
{"id":"P001","name":"索尼WH-1000XM5 头戴式降噪耳机","brand":"索尼","price":2499.00,"category":"耳机","tags":["降噪","蓝牙","头戴式"],"stock":150,"rating":4.8,"description":"业界领先的降噪技术,30小时续航,佩戴舒适","create_time":"2024-01-15T10:30:00Z"}3.2 Document的字段类型
ES中的字段支持丰富的类型,远不止关系数据库的那几种:
| 字段类型 | ES数据类型 | 示例 |
|---|---|---|
| 字符串(全文搜索) | text | "description": "业界领先的降噪技术" |
| 字符串(精确匹配) | keyword | "brand": "索尼" |
| 整数 | long / integer | "stock": 150 |
| 浮点数 | float / double | "price": 2499.00 |
| 布尔值 | boolean | "on_sale": true |
| 日期 | date | "create_time": "2024-01-15" |
| 数组 | 由第一个元素类型决定 | "tags": ["降噪", "蓝牙"] |
| 对象 | object | "specs": {"color": "黑", "weight": "250g"} |
| 地理位置 | geo_point | "location": {"lat": 39.9, "lon": 116.4} |
3.3 Document ID
每个文档都有一个唯一标识符(_id),可以自己指定,也可以让ES自动生成:
# 指定ID为P001PUT /products/_doc/P001{"name":"索尼降噪耳机","price":2499}# 让ES自动生成ID(使用POST)POST /products/_doc{"name":"索尼降噪耳机","price":2499}四、Field(字段)详解
Field是Document中的属性,类似于关系数据库中的列。但ES的Field有更强大的能力:
4.1 text vs keyword
这是初学者最容易混淆的地方,也是ES相比MySQL最重要的区别之一。
| 特性 | text | keyword |
|---|---|---|
| 分词 | 会分词处理 | 不处理,原样存储 |
| 用途 | 全文搜索 | 精确匹配、排序、聚合 |
| 示例 | 产品描述、文章内容 | 品牌名、标签、状态码 |
# 搜索"降噪耳机"# text字段:分词后搜索 → "降噪"和"耳机"分别匹配# keyword字段:只匹配完整字符串 "降噪耳机"如果你的product的category字段是text类型,搜索"耳机"可能会匹配到"蓝牙耳机"、“头戴式耳机"等。如果使用keyword类型,只精确匹配"耳机”。
最佳实践:对于需要全文搜索的内容(如商品描述),使用text类型;对于枚举值、精确标识符(如品牌名、类别名、订单号),使用keyword类型。ES会自动为text字段创建一个同名的keyword子字段(通过fields映射),方便在聚合时使用.keyword后缀。
五、Shard(分片)与Replica(副本)详解
这是Elasticsearch分布式能力的基础,也是Elastic(弹性)这个名字的由来。
5.1 为什么需要分片
假设你有一个存储了10亿个商品的Index,单台服务器的磁盘可能放不下,即使放下了,搜索性能也会很差。分片机制就是用来解决这个问题的——将一个Index水平切分为多个子Index,分布到不同节点上。
5.2 主分片(Primary Shard)
- 主分片是数据写入的第一入口
- 创建Index时指定,创建后不可修改
- 默认数量:1(ES7.x及以上)
# 创建索引时指定3个主分片PUT /products{"settings":{"number_of_shards":3,"number_of_replicas":1}}5.3 副本分片(Replica Shard)
副本是主分片的拷贝,提供两个关键功能:
- 高可用:当主分片所在节点宕机时,副本可以升级为主分片,保证数据不丢失
- 提升查询性能:搜索请求可以在所有副本上并行执行,增加查询吞吐量
关键规则:主分片和它的副本永远不会分配到同一个节点上。这很容易理解——如果主分片和副本在同一台机器上,机器宕机时两个一起挂,副本就失去了容灾的意义。
5.4 分片与副本的数量计算
# 示例:3个主分片 + 1个副本# 总共 = 3个主分片 + 3个副本分片 = 6个分片如果集群有3个节点,每个节点会分配到2个分片(1个主分片 + 1个副本分片)。
5.5 分片数的选择策略
| 场景 | 建议分片数 | 说明 |
|---|---|---|
| 数据量<1GB | 1个主分片 | 小数据量不需要分片 |
| 1GB-100GB | 1-3个主分片 | 常规业务数据 |
| 100GB-1TB | 3-5个主分片 | 大数据量场景 |
| >1TB | 5+个主分片 | 需要评估节点数 |
重要提示:分片数量不是越多越好。每个分片都是一个完整的Lucene索引,会占用内存和文件句柄。过多的小分片反而会降低性能。一般建议每个分片大小控制在10GB-50GB之间。
六、Mapping(映射)详解
6.1 Mapping是什么
Mapping定义了Index中Document的字段结构,类似于关系数据库中的Schema定义。
6.2 动态Mapping vs 显式Mapping
ES支持两种方式定义Mapping:
动态Mapping(自动推断):
# 不定义Mapping,直接写入文档POST /products/_doc{"name":"蓝牙耳机","price":299,"create_time":"2024-01-15"}# ES会自动推断:# name → text + keyword子字段# price → float# create_time → date显式Mapping(手动指定):
PUT /products{"mappings":{"properties":{"name":{"type":"text","analyzer":"ik_max_word"},"price":{"type":"float"},"category":{"type":"keyword"},"description":{"type":"text","analyzer":"ik_smart"},"create_time":{"type":"date","format":"yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"}}}}6.3 Mapping vs MySQL Schema
| 对比维度 | MySQL Schema | ES Mapping |
|---|---|---|
| 是否强制 | 是(必须先定义表结构) | 否(动态Mapping可以自动创建) |
| 修改灵活性 | ALTER TABLE | 已有字段不可修改类型,只能新增字段 |
| 嵌套结构 | 不支持 | 支持object和nested类型 |
| 多类型 | 不支持 | 一个字段可以有text+keyword两种类型 |
| 分析器 | 没有 | 可以为text字段指定分词器 |
经验之谈:开发阶段可以使用动态Mapping快速迭代,但生产环境一定要显式定义Mapping。动态Mapping可能会把"2024-01-15"这样的字符串错误地推断为text类型而不是date类型。
七、Node(节点)与Cluster(集群)
7.1 Node
Node是ES的一个运行实例,本质就是一个Java进程。每个Node由一个名称标识,默认是启动时随机生成的UUID。
一台物理服务器上可以运行多个Node(但不建议生产环境这样做)。
7.2 Cluster
Cluster是一个或多个Node的集合,共同持有完整的数据,并提供联合索引、搜索和分析功能。
Cluster由唯一的名称标识(cluster.name),Node通过这个名称加入集群。
7.3 Node角色
| 角色 | 参数 | 职责 |
|---|---|---|
| Master | node.master: true | 管理集群元数据(创建/删除索引、分配分片) |
| Data | node.data: true | 存储数据和执行数据操作 |
| Ingest | node.ingest: true | 预处理文档(管道) |
| Coordinating | 仅转发请求 | 路由请求到正确的节点 |
一个Node可以同时承担多个角色。默认情况下,每个Node都是Master候选者和Data节点。
7.4 集群健康状态
ES集群有三种健康状态:
| 状态 | 颜色 | 含义 |
|---|---|---|
| Green | 绿色 | 所有主分片和副本都已分配,集群完全正常 |
| Yellow | 黄色 | 所有主分片已分配,但部分副本未分配(单节点集群的默认状态) |
| Red | 红色 | 部分主分片未分配,部分数据不可用 |
八、实战:电商商品数据的概念串联
让我们用一个电商系统的实例,把上述概念全部串起来。
场景描述
某电商平台需要管理以下商品数据。这是一个有3个节点的ES集群。
概念关系图
Cluster: "ecommerce-cluster" │ ├── Node 1: "es-node-1" (Master + Data) │ ├── Shard 0 (Primary) ← products索引的主分片0 │ └── Shard 2 (Primary) ← products索引的主分片2 │ ├── Node 2: "es-node-2" (Data) │ ├── Shard 0 (Replica) ← 主分片0的副本 │ └── Shard 1 (Primary) ← products索引的主分片1 │ └── Node 3: "es-node-3" (Data) ├── Shard 1 (Replica) ← 主分片1的副本 └── Shard 2 (Replica) ← 主分片2的副本 Index "products" (3主分片 + 1副本 = 6分片) │ ├── Document 1: { "name": "索尼降噪耳机", "price": 2499, "category": "耳机" } │ Fields: name(text), price(float), category(keyword) │ ├── Document 2: { "name": "Apple AirPods Pro", "price": 1899, "category": "耳机" } │ └── Document 3: { "name": "小米手环8", "price": 239, "category": "智能穿戴" }数据写入过程
当用户写入一个商品文档时:
- 请求到达任意一个Node(假设Node 1)
- Node 1作为协调节点,根据文档ID计算路由(
shard = hash(_id) % number_of_primary_shards) - 确定文档应写入Shard 1,将请求转发到Node 2(Shard 1的主分片所在节点)
- Node 2写入主分片后,将数据同步到Node 3的副本分片
- 副本确认成功后,Node 2返回成功给Node 1,Node 1返回给客户端
搜索过程
当用户搜索"耳机"时:
- 请求到达任意Node(协调节点)
- 协调节点将搜索请求广播到所有相关分片(主分片或副本均可)
- 各分片独立执行搜索,返回结果给协调节点
- 协调节点合并结果、排序,返回给客户端
九、总结与最佳实践
核心概念速查
| 概念 | 一句话说明 |
|---|---|
| Index | 一类文档的集合,类似数据库 |
| Document | 一条JSON数据记录 |
| Field | 文档中的一个属性 |
| Shard | Index的水平切分,实现分布式的核心 |
| Replica | 分片的拷贝,保障高可用和性能 |
| Mapping | 字段的类型和属性定义 |
| Node | 一个ES进程实例 |
| Cluster | 一组协同工作的Node集合 |
最佳实践
- 分片数提前规划:分片数创建后不可修改(常规方式),要根据数据量预估
- 生产环境使用显式Mapping:避免类型推断错误,精确控制字段行为
- 区分text和keyword:搜索用text,聚合/排序用keyword,或使用fields双字段映射
- 副本数至少为1:防止单点故障导致数据丢失
- 不同环境使用不同cluster.name:避免开发环境节点意外加入生产集群
- 节点名称要语义化:使用如
es-data-node-1、es-master-node-1之类的命名,便于排查问题
上一篇【第01篇】Elasticsearch是什么
下一篇【第03篇】Elasticsearch快速入门实战