深入理解索引、文档、映射和分片机制
索引是Elasticsearch中最核心的数据组织单位,类似于关系数据库中的数据库。但索引不仅仅是数据容器,它还包含了数据的分片策略、副本配置、映射定义等。
每个索引在物理上由多个分片(Shard)组成,每个分片都是一个独立的Lucene索引实例。这种设计使得Elasticsearch能够:
/, \\, *, ?, ", <, >, |, (空格), ,, #-、_、+开头.或..# 1. 创建简单索引(使用默认配置)
PUT /products
# 2. 创建带详细配置的索引
PUT /products
{
"settings": {
"number_of_shards": 3, # 主分片数量
"number_of_replicas": 2, # 每个主分片的副本数量
"refresh_interval": "1s", # 刷新间隔(近实时性)
"index.max_result_window": 10000 # 最大返回结果数
},
"mappings": {
"properties": {
"name": { "type": "text" },
"price": { "type": "float" }
}
}
}
# 3. 创建时间序列索引(适合日志数据)
PUT /logs-2024.01.01
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1
}
}
最佳实践:生产环境应根据数据量和查询负载合理设置分片数量。分片过少会导致性能瓶颈,分片过多会增加管理开销。一般建议每个分片大小在10-50GB之间。
文档是Elasticsearch中的基本数据单元,采用JSON格式表示。每个文档都包含元数据和实际数据两部分。
文档包含以下核心元数据字段:
# 1. 自动生成ID(推荐用于日志等场景)
POST /products/_doc
{
"name": "iPhone 15",
"price": 5999,
"category": "手机"
}
# 响应:自动生成ID,如 "_id": "abc123"
# 2. 指定ID(推荐用于业务数据)
PUT /products/_doc/1
{
"name": "iPhone 15",
"price": 5999,
"category": "手机"
}
# 3. 批量操作
POST /_bulk
{ "index": { "_index": "products", "_id": "1" } }
{ "name": "iPhone 15", "price": 5999 }
{ "index": { "_index": "products", "_id": "2" } }
{ "name": "iPad Pro", "price": 7999 }
# 完整的文档示例(包含元数据)
{
"_index": "products",
"_id": "1",
"_version": 1,
"_seq_no": 0,
"_primary_term": 1,
"found": true,
"_source": {
"name": "iPhone 15",
"price": 5999,
"category": "手机",
"brand": "Apple",
"tags": ["智能手机", "苹果"],
"specifications": {
"screen": "6.1英寸",
"memory": "128GB"
},
"created_at": "2024-01-01T10:00:00Z"
}
}
文档大小限制:单个文档大小默认限制为100MB。对于大文档,建议拆分成多个小文档或使用附件处理。
字段是文档中的键值对,每个字段都有特定的数据类型。正确选择数据类型对搜索性能和存储效率至关重要。
| 类型 | 详细说明 | 适用场景 | 示例 |
|---|---|---|---|
| text | 全文本类型,会被分词器处理,支持全文检索 | 文章内容、商品描述、用户评论 | "这是一段需要全文检索的文本" |
| keyword | 精确值类型,不分词,支持精确匹配、聚合、排序 | 状态码、标签、分类、ID | "iPhone"、"ACTIVE"、"user123" |
| long/integer | 整数类型,支持范围查询、聚合 | 年龄、数量、评分 | 100、-50、999999 |
| float/double | 浮点数类型,支持精确计算 | 价格、评分、百分比 | 99.99、3.14159、-45.67 |
| boolean | 布尔值类型,支持true/false | 开关状态、是否标记 | true、false |
| date | 日期类型,支持多种格式和时区 | 创建时间、更新时间、事件时间 | "2024-01-01"、"2024-01-01T10:00:00Z" |
| geo_point | 地理位置类型,支持距离计算 | 用户位置、商家地址 | {"lat": 40.7128, "lon": -74.0060} |
| ip | IP地址类型,支持CIDR查询 | 用户IP、服务器IP | "192.168.1.1"、"::1" |
| nested | 嵌套对象类型,保持数组元素的独立性 | 订单项、标签数组 | [{"name": "tag1"}, {"name": "tag2"}] |
这是Elasticsearch中最容易混淆的两个类型:
# 错误用法:将需要精确匹配的字段设为text类型
PUT /products
{
"mappings": {
"properties": {
"category": { "type": "text" } # 错误!category应该用keyword
}
}
}
# 正确用法:text用于全文检索,keyword用于精确匹配
PUT /products
{
"mappings": {
"properties": {
"title": { "type": "text" }, # 需要全文检索
"category": { "type": "keyword" }, # 需要精确匹配
"description": {
"type": "text",
"fields": {
"keyword": { "type": "keyword" } # 多字段:同时支持两种方式
}
}
}
}
}
多字段技巧:对于既需要全文检索又需要精确匹配的字段,可以使用多字段(multi-fields)配置,让一个字段同时具备text和keyword特性。
映射定义了文档及其字段的存储和索引方式,类似于数据库的表结构。映射决定了字段如何被分析、索引和存储。
# 1. 动态映射:Elasticsearch自动推断类型
PUT /dynamic_index/_doc/1
{
"title": "Elasticsearch教程", # 推断为text
"price": 99.99, # 推断为float
"category": "技术书籍", # 推断为text(可能不是最佳选择)
"published": true, # 推断为boolean
"publish_date": "2024-01-01" # 推断为date
}
# 查看自动生成的映射
GET /dynamic_index/_mapping
# 2. 显式映射:手动定义最佳类型
PUT /products
{
"mappings": {
"properties": {
"name": {
"type": "text",
"analyzer": "ik_max_word", # 使用中文分词器
"search_analyzer": "ik_smart"
},
"price": {
"type": "float",
"index": true # 可被索引(默认)
},
"category": {
"type": "keyword",
"ignore_above": 256 # 超过256字符的keyword不会被索引
},
"description": {
"type": "text",
"fields": {
"keyword": { "type": "keyword" } # 多字段配置
}
},
"created_at": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
},
"tags": {
"type": "nested", # 嵌套类型,保持数组元素独立性
"properties": {
"name": { "type": "keyword" },
"color": { "type": "keyword" }
}
}
}
}
}
# 查看映射
GET /products/_mapping
重要提醒:一旦索引创建后,映射中的字段类型通常不能修改。如果需要修改字段类型,需要重新创建索引并重新导入数据。
理解Elasticsearch与传统关系数据库的区别,有助于正确选择和使用这两种技术。
| 概念 | Elasticsearch | 关系数据库(MySQL) | 技术差异 |
|---|---|---|---|
| 数据模型 | 文档型,Schema-free | 关系型,固定Schema | Elasticsearch支持动态字段,MySQL需要预定义表结构 |
| 查询语言 | Query DSL(JSON) | SQL | Elasticsearch使用领域特定语言,MySQL使用标准SQL |
| 事务支持 | 有限的事务支持 | 完整的ACID事务 | MySQL适合强一致性场景,Elasticsearch适合搜索分析 |
| 扩展性 | 水平扩展,分布式架构 | 垂直扩展为主 | Elasticsearch天然支持分布式,MySQL扩展复杂 |
| 实时性 | 近实时(1秒内) | 实时 | MySQL写入后立即可读,Elasticsearch有短暂延迟 |
| 全文检索 | 原生支持,性能优秀 | 有限支持,性能一般 | Elasticsearch基于Lucene,搜索能力远超MySQL |
| 聚合分析 | 强大的聚合功能 | 基本的聚合函数 | Elasticsearch支持复杂的数据分析和统计 |
架构选择建议:在实际项目中,通常将Elasticsearch与关系数据库结合使用。关系数据库负责事务处理和核心数据存储,Elasticsearch负责搜索和分析功能。
分片和副本是Elasticsearch分布式架构的核心,理解它们的工作原理对性能优化至关重要。
# 1. 默认配置(5个主分片,1个副本)
PUT /default_index
# 2. 自定义分片配置
PUT /custom_index
{
"settings": {
"number_of_shards": 3, # 主分片数量
"number_of_replicas": 2, # 每个主分片的副本数量
"routing.allocation.total_shards_per_node": 10 # 每个节点最大分片数
}
}
# 3. 查看分片分布
GET /_cat/shards?v&s=node
# 4. 查看分片健康状态
GET /_cat/indices?v&health=red,yellow,green
| 数据量 | 建议分片数 | 说明 |
|---|---|---|
| < 10GB | 1-2个分片 | 小数据量,避免分片过多增加开销 |
| 10GB - 100GB | 3-5个分片 | 中等数据量,平衡性能和开销 |
| 100GB - 1TB | 5-10个分片 | 大数据量,支持并行处理 |
| > 1TB | 10+个分片 | 超大数据量,需要水平扩展 |
分片设计最佳实践:
创建一个名为"library"的索引,要求:
A: 如果需要全文检索(如搜索"苹果手机"能匹配到"苹果"和"手机"),使用text类型;如果需要精确匹配(如分类、标签),使用keyword类型。
A: 一般建议每个分片大小在10-50GB之间。分片过少会导致性能瓶颈,分片过多会增加管理开销。
A: 已存在字段的类型不能修改,但可以添加新字段。如果需要修改字段类型,需要重新创建索引。
A: 当数组中的对象需要保持独立性,并且需要对这些对象进行独立查询时,使用nested类型。
下节课预告:第3课将深入讲解文档的CRUD操作,包括单文档操作、批量操作、版本控制、乐观并发控制等高级功能。