在 Elasticsearch 中,**索引(Index)**是核心概念之一,类似于关系型数据库中的“表”。索引用于存储、组织和检索文档(Document)。以下是关于 Elasticsearch 索引的详细解析:
1. 索引的基本概念
- 索引:一个索引是一个逻辑命名空间,用于存储具有相似结构的文档。例如,可以创建一个名为
products
的索引来存储所有产品信息。 - 文档:文档是索引中的基本数据单元,以 JSON 格式存储。例如,一个产品文档可能包含
name
、price
、description
等字段。 - 类型(Type):在 Elasticsearch 6.x 及之前版本中,索引可以包含多个类型(类似于表)。但从 7.x 开始,类型已被弃用,每个索引只能包含一种类型(默认是
_doc
)。 - 分片(Shard):索引被划分为多个分片,分片是数据的物理存储单元。分片可以分布在不同的节点上,从而实现分布式存储和并行处理。
- 副本(Replica):每个分片可以有多个副本,用于提高数据的可用性和容错性。
2. 索引的创建
es 支持动态映射(自动推断字段类型)和显式映射(手动定义字段类型)。
显式创建
PUT /products
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 2
},
"mappings": {
"properties": {
"name": { "type": "text" },
"price": { "type": "float" },
"description": { "type": "text", "analyzer": "english" },
"extra": {
"type": "text",
"index": false
},
"number" : {
"type" : "keyword",
"null_value": "NULL"
}
}
}
}
- 该索引名为
products
,包含 3 个分片和 2 个副本。 - 定义了5个字段:
name
(文本类型)、price
(浮点类型)、description
(文本类型,使用英文分词器)、extra(文本类型,不创建倒排索引)、number(keyword类型,通过NULL可筛选出没有手机号的文档)
筛选出number为空的文档
GET products/_search
{
"query": {
"match": {
"number":"NULL"
}
}
}
动态创建(不建议)
POST /products/_doc/1
{
"name": "Laptop",
"price": 999.99,
"description": "A powerful laptop for professionals."
}
- Elasticsearch 会自动推断字段类型:
name
和description
为文本类型,price
为浮点类型。
动态创建的类型,与实际需要的类型可能不一致,不建议使用
3. 索引维护
查看索引
GET /products
删除索引
DELETE /products
4.索引的性能优化
- 合理设置分片数量:分片数量过多会增加集群负担,过少会影响查询性能。
- 使用副本提高可用性:副本可以提高数据的可用性和查询性能。
- 优化映射:避免使用不必要的字段类型(如
keyword
用于大文本)。 - 定期清理旧数据:使用索引生命周期管理(ILM)自动删除或归档旧数据
避免排序
将term、match转换成filter
使用score计算分数
GET /my_index/_search
{
"query": {
"bool": {
"must": [
{ "term": { "status": "active" } },
{ "match": { "description": "powerful laptop" } }
]
}
}
}
转换成不需要score计算分数
GET /my_index/_search
{
"query": {
"bool": {
"filter": [
{ "term": { "status": "active" } },
{ "match": { "description": "powerful laptop" } }
]
}
}
}
优化排序与聚合
eager_global_ordinals
可以加速 排序、聚合
原理:
global_ordinals
全局序数是一种将字段值映射为连续整数的机制。例如,字段值["apple", "banana", "cherry"]
可能被映射为[0, 1, 2]
默认情况下,全局序数是在查询时动态生成的。这意味着每次执行聚合或排序操作时,Elasticsearch 都需要遍历字段值并生成全局序数。
对于高基数字段(字段值种类多),生成全局序数可能会消耗大量时间和资源。
启用
eager_global_ordinals
时,Elasticsearch 会在索引刷新(Refresh)或段合并(Segment Merge)时预先生成全局序数
适用场景
- 高基数字段
- 频繁聚合或排序
- 实时性要求高
缺点
- 增加索引开销:在索引刷新或段合并时生成全局序数会增加 CPU 和内存开销。
- 占用更多内存:全局序数需要存储在内存中,可能会增加内存使用量
启用eager_global_ordinals
PUT /my_index
{
"mappings": {
"properties": {
"my_field": {
"type": "keyword",
"eager_global_ordinals": true
}
}
}
}
避免不必要的字段类型
字段不创建索引
PUT /my_index
{
"mappings": {
"properties": {
"name": {
"type": "text"
},
"metadata": {
"type": "object",
"enabled": false
},
"description": {
"type": "text",
"index": false
}
}
}
}
name
字段:正常索引,可以用于搜索。
metadata
字段:禁用整个对象的索引功能。
description
字段:禁用索引,无法用于搜索。
禁用索引后的字段特性
无法搜索:禁用索引的字段无法用于查询(如
match
、term
等)。可以存储:字段的值仍然会存储在
_source
中,可以通过_source
获取。可以用于聚合:禁用索引的字段仍然可以用于聚合操作(如
terms
、sum
等),性能会降低。可以用于脚本:字段的值可以在脚本中使用。
禁用索引的适用场景
日志数据:某些字段(如原始日志)可能不需要搜索,但需要存储。
大文本字段:某些大文本字段(如文章内容)可能不需要搜索,但需要存储。
敏感数据:某些敏感数据(如密码、密钥)不需要索引,但需要存储。
注意事项
无法恢复索引:如果需要重新启用索引,必须重新创建索引或使用
reindex
存储开销:禁用索引的字段仍然会占用存储空间,因为其值存储在
_source
中。聚合性能:禁用索引的字段在聚合时可能会影响性能,因为需要从
_source
中提取数据。
不使用fielddata
索引的生命周期管理(ILM)
创建策略
PUT _ilm/policy/my_custom_policy
{
"policy": {
"phases": {
"hot": {
"actions": {
"rollover": {
"max_size": "50GB",
"max_age": "30d"
}
}
},
"delete": {
"min_age": "90d",
"actions": {
"delete": {}
}
}
}
}
}
该策略定义了索引的生命周期:在
hot
阶段,当索引大小超过 50GB 或创建时间超过 30 天时,滚动到新索引;在delete
阶段,当索引创建时间超过 90 天时,删除索引。(场景:删除3年之前的数据)
将策略绑定到索引
PUT /my_index
{
"settings": {
"index.lifecycle.name": "my_custom_policy"
}
}
验证是否生效
GET /my_index/_ilm/explain
其他
添加字段禁止自动创建倒排索引
dynamic的取值类型如下:
取值 | 是否添加字段到映射 | 是否存储字段数据 | 是否抛出异常 | 适用场景 |
---|---|---|---|---|
true |
是 | 是 | 否 | 数据结构不固定,需要动态添加字段。 |
false |
否 | 是 | 否 | 数据结构固定,但需要存储未知字段。 |
strict |
- | - | 是 | 数据结构严格固定,不允许未知字段。 |
true: 默认,添加倒排索引、储存字段
false:只会存储字段
strict:不支持自动添加字段,需要手动维护
(建议:如果字段是动态的,需要自动映射,则使用true;否则使用
strict。避免出现创建多余的映射现象。。原因:索引不容易修改,建议手动创建映射
)
手动添加字段:
PUT /my_index/_mapping
{
"properties": {
"age": { "type": "integer" }
}
}