下面介绍索引相关的 CRUD、索引设置、索引别名、映射和索引模版
索引管理
1. Create Index
curl -X PUT "localhost:9200/twitter?pretty"
这将使用所有默认设置创建一个名为 twitter 的索引,返回结果如下:
{
"acknowledged" : true,
"shards_acknowledged" : true,
"index" : "twitter"
}
- acknowledged:表示索引是否在集群中成功创建
- shards_acknowledged:表示索引中的每个分片是否在超时之前启动了必要数量的副本分片
注意:索引名称有如下几个限制条件:
- 必须是小写字母
- 不能包含 \ / * ?” < > | # , 这些特殊字符和空格
- 不能以 - _ + 字符开头
- 不能超过 255 个字节
如果新添加的索引在 Elasticsearch 服务器中已存在,新建同名索引会报索引已存在的异常:
{
"error" : {
"root_cause" : [
{
"type" : "resource_already_exists_exception",
"reason" : "index [twitter/KpzW0ao2QlCwZeIZJJywzQ] already exists",
"index_uuid" : "KpzW0ao2QlCwZeIZJJywzQ",
"index" : "twitter"
}
],
"type" : "resource_already_exists_exception",
"reason" : "index [twitter/KpzW0ao2QlCwZeIZJJywzQ] already exists",
"index_uuid" : "KpzW0ao2QlCwZeIZJJywzQ",
"index" : "twitter"
},
"status" : 400
}
如果在创建索引时想指定该索引关联的特定设置以及特定 Mapping,可通过如下示例创建索引:
curl -X PUT "localhost:9200/twitter?pretty" -H 'Content-Type: application/json' -d'
{
"settings" : {
"number_of_shards" : 1,
"number_of_replicas" : 2
},
"mappings" : {
"properties" : {
"name" : { "type" : "text" }
}
}
}'
2. Get Index
用于查看索引的 Mapping、Setting 及别名等配置信息,如下示例:
curl -X GET "localhost:9200/twitter?pretty"
返回结果如下:
{
"twitter" : {
"aliases" : { },
"mappings" : {
"properties" : {
"message" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"post_date" : {
"type" : "date"
}
}
},
"settings" : {
"index" : {
"routing" : {
"allocation" : {
"include" : {
"_tier_preference" : "data_content"
}
}
},
"number_of_shards" : "1",
"provided_name" : "twitter",
"creation_date" : "1624112514686",
"number_of_replicas" : "1",
"uuid" : "KpzW0ao2QlCwZeIZJJywzQ",
"version" : {
"created" : "7130199"
}
}
}
}
}
此外,我们还可以使用 _all 或者通配符来查看所有索引的配置信息:
curl -X GET "localhost:9200/_all?pretty"
还可以通过 _settings、_mappings、_alias 来单独查看索引配置:
curl -X GET "localhost:9200/twitter/_settings?pretty"
curl -X GET "localhost:9200/twitter/_mappings?pretty"
curl -X GET "localhost:9200/twitter/_alias?pretty"
3. Delete Index
索引的删除只需要使用 DELETE 方法,传入要删除的索引名即可,执行索引删除命令之前要慎重考虑,因为一旦执行删除操作索引中的文档就不复存在,注意在删除重要数据前做好备份工作。
curl -X DELETE "localhost:9200/twitter?pretty"
如果删除成功,会有以下响应:
{
"acknowledged" : true
}
如果删除的索引名称不存在,会报索引未找到异常:
{
"error" : {
"root_cause" : [
{
"type" : "index_not_found_exception",
"reason" : "no such index [twitter]",
"resource.type" : "index_or_alias",
"resource.id" : "twitter",
"index_uuid" : "_na_",
"index" : "twitter"
}
],
"type" : "index_not_found_exception",
"reason" : "no such index [twitter]",
"resource.type" : "index_or_alias",
"resource.id" : "twitter",
"index_uuid" : "_na_",
"index" : "twitter"
},
"status" : 404
}
删除索引时必须指定索引名称,别名不能用于删除索引。与 Get 方法一样,Delete 方法同样支持 _all 和 *。
4. Close、Open
Elasticsearch 中的索引可以进行打开和关闭操作,一个关闭了的索引几乎不占用系统资源,只有维护元数据的开销。 关闭和打开一个索引的命令格式为:/{index}/_close 和 /{index}/_open。已经关闭的索引不能再进行读写操作。直到这个关闭的索引被重新开启。
curl -X POST "localhost:9200/twitter/_close?pretty"
curl -X POST "localhost:9200/twitter/_open?pretty"
索引的关闭或开启支持同时操作多个索引,也支持 _all 和通配符。如果在批量操作时 Elasticsearch 集群中不存在开启、关闭请求中的全部索引,将会抛出索引不存在错误。此时可以将 ignore_unavailable 参数设置为 true 来操作只存在的索引,如下示例:
curl -X POST "localhost:9200/testa,testb,testc/_close?ignore_unavailable=true&pretty"
5. Shrink API
一个索引的分片初始化以后是无法再做修改的,但可以使用 Shrink API 将现有索引收缩为一个主分片数更少的新索引,注意目标索引中的主分片数必须是源索引中的分片数的因数。比如有 8 个主分片的索引可以缩小为 4 个、 2 个或 1 个主分片,有 15 个主分片的索引可以缩小为 5 个、3 个或 1 个。如果索引中的分片数为素数则只能将其缩减为单个主分片。在收缩前,索引中的每个分片(主分片或副本分片)必须出现在同一节点上。
收缩的工作过程如下:
- 首先创建一个与源索引定义相同的目标索引,但主分片的数量更少。
- 然后将源索引的 Segment 文件硬链接(hard-links)到目标索引中。如果文件系统不支持硬链接,那么所有的 Segment 文件会被复制到新的索引中,这是一个非常耗时的过程。
- 最后恢复目标索引,就像刚打开一个被关闭的索引一样。
在执行 Shrink API 之前,索引必须被标记为只读,索引中的每个分片(主分片或副本分片)必须重新定位到相同的节点上,并且节点健康状态为绿色。这两个条件可通过以下要求实现:
curl -X PUT "localhost:9200/my_source_index/_settings?pretty" -H 'Content-Type: application/json' -d'
{
"settings": {
"index.routing.allocation.require._name": "shrink_node_name",
"index.blocks.write": true
}
}'
- 第一个属性表示将源索引中的每个分片的副本重新定位 shrink_node_name 的节点上。
- 第二个属性表示禁止对源索引执行写操作,但仍允许进行元数据更改
准备工作完成后,就可以执行 Shrink API 了,在执行 Shrink API 时也可指定目标索引的分片数、副本数、别名等配置信息,但注意目标索引的分片数必须是源索引中分片数的一个因数,命令如下:
curl -X POST "localhost:9200/my_source_index/_shrink/my_target_index?pretty" -H 'Content-Type: application/json' -d'
{
"settings": {
"index.number_of_replicas": 1,
"index.number_of_shards": 1,
"index.codec": "best_compression"
},
"aliases": {
"my_search_indices": {}
}
}'
Mapping 管理
1. Put Mapping
可以向现有索引中添加字段或者更改现有字段的搜索设置,如下示例向 twitter 索引添加了一个新的 email 字段且为 keyworld 类型:
curl -X PUT "localhost:9200/twitter/_mapping?pretty" -H 'Content-Type: application/json' -d'
{
"properties": {
"email": {
"type": "keyword"
}
}
}'
返回结果如下:
{
"acknowledged" : true
}
如果想同时向多个索引中添加字段,可以使用逗号分隔的索引列表:
curl -X PUT "localhost:9200/twitter1,twitter2/_mapping?pretty" -H 'Content-Type: application/json' -d'
{
"properties": {
"email": {
"type": "keyword"
}
}
}'
对已有字段,一旦已经有数据写入,就不再支持修改字段定义。因为 ES 是基于 Lucene 实现的倒排索引,一旦生成就不允许修改,如果希望修改字段类型,必须使用 Reindex 的 API 对索引重建。
{
"error" : {
"root_cause" : [
{
"type" : "illegal_argument_exception",
"reason" : "mapper [email] cannot be changed from type [keyword] to [text]"
}
],
"type" : "illegal_argument_exception",
"reason" : "mapper [email] cannot be changed from type [keyword] to [text]"
},
"status" : 400
}
但这条规则也是有一些例外情况的:
- 可以向 object 类型的字段添加新的属性
- 可以修改字段的 ignore_above 属性
如下示例,向 name 字段中添加了新的属性并更新了 user_id 字段的 ignore_above 值:
curl -X PUT "localhost:9200/my_index?pretty" -H 'Content-Type: application/json' -d'
{
"mappings": {
"properties": {
"name": {
"properties": {
"first": {
"type": "text"
}
}
},
"user_id": {
"type": "keyword"
}
}
}
}'
curl -X PUT "localhost:9200/my_index/_mapping?pretty" -H 'Content-Type: application/json' -d'
{
"properties": {
"name": {
"properties": {
"last": {
"type": "text"
}
}
},
"user_id": {
"type": "keyword",
"ignore_above": 100
}
}
}'
2. Get Mapping
获取索引的 mapping 配置,支持用逗号分隔的索引列表及 _all。
curl -X GET "localhost:9200/twitter/_mapping?pretty"
curl -X GET "localhost:9200/twitter,twitter1/_mapping?pretty"
curl -X GET "localhost:9200/_all/_mapping?pretty"
curl -X GET "localhost:9200/_mapping?pretty"
除了获取整个索引的 mapping 设置,还可以单独获取一个字段或多个字段的 mapping 定义。如下示例,只返回了 twitter 索引中的 email 字段的 mapping 定义:
curl -X GET "localhost:9200/twitter/_mapping/field/email?pretty"
返回结果如下:
{
"twitter" : {
"mappings" : {
"email" : {
"full_name" : "email",
"mapping" : {
"email" : {
"type" : "keyword"
}
}
}
}
}
}
如果想要获取多个索引中的多个字段的 mapping 定义,可以通过如下示例获取:
curl -X GET "localhost:9200/twitter,twitter1/_mapping/field/email?pretty"
curl -X GET "localhost:9200/_all/_mapping/field/email,name?pretty"
curl -X GET "localhost:9200/_all/_mapping/field/*.id?pretty"
Setting 管理
Elasticsearch 支持通过 /_settings 更新所有索引的配置或通过 /{index}/_settings 更新指定索引的配置。如下示例把索引的副本数设置为 2:
curl -X PUT "localhost:9200/twitter/_settings?pretty" -H 'Content-Type: application/json' -d'
{
"index" : {
"number_of_replicas" : 2
}
}'
如果想要把配置项重置为默认值,可以设置为 null:
curl -X PUT "localhost:9200/twitter/_settings?pretty" -H 'Content-Type: application/json' -d'
{
"index" : {
"refresh_interval" : null
}
}'
此外,还可以为已存在的索引定义新的 analyzer,但是需要先关闭索引,然后更新设置后再打开它:
curl -X POST "localhost:9200/twitter/_close?pretty"
curl -X PUT "localhost:9200/twitter/_settings?pretty" -H 'Content-Type: application/json' -d'
{
"analysis" : {
"analyzer":{
"content":{
"type":"custom",
"tokenizer":"whitespace"
}
}
}
}'
curl -X POST "localhost:9200/twitter/_open?pretty"
Alias
在实际应用中,我们不应该向单个索引持续写数据,直到它的分片巨大无比。巨大的索引会在数据老化后难以删除,以 id 为单位删除文档不会立刻释放空间(标记删除),删除的 doc 只在 Lucene 分段合并时才会真正从磁盘中删除。即使手工触发分段合并,仍然会引起较高的 I/O 压力,并且可能因为分段巨大导致在合并过程中磁盘空间不足(分段大小大于磁盘可用空间的一半)。
因此建议你周期性地创建新索引。比如每天创建一个,如果索引名称为 website,则可以将该索引命名为 website_20210601,然后创建一个名为 website 的索引别名来关联这些索引。这样对于业务方来说,读取时使用的名称不变,当需要删除数据时,可以直接删除整个索引。
索引别名就像一个快捷方式或软链接,不同的是它可以指向一个或多个索引,但是只能指定一个索引用于写入数据。可用于实现索引分组或索引间的无缝切换。但周期性创建新索引带来的问题是集群整体分片数量较多,集群管理的总分片数越多压力越大。我们可以使用 Shrink API 来缩减主分片数量,降低集群整体负载。
新增索引别名:
curl -X POST "localhost:9200/_aliases?pretty" -H 'Content-Type: application/json' -d'
{
"actions" : [
{ "add" : { "index" : "test1", "alias" : "alias1" } }
]
}'
如果是为单个索引新增别名,还可以简化为:
curl -X PUT "localhost:9200/test1/_alias/alias1?pretty"
移除索引别名:
curl -X POST "localhost:9200/_aliases?pretty" -H 'Content-Type: application/json' -d'
{
"actions" : [
{ "remove" : { "index" : "test1", "alias" : "alias1" } }
]
}'
如果是移除单个索引别名,还可以简化为:
curl -X DELETE "localhost:9200/test1/_alias/alias1?pretty"
重命名索引别名,内部是先删除后新增,该操作是原子的:
curl -X POST "localhost:9200/_aliases?pretty" -H 'Content-Type: application/json' -d'
{
"actions" : [
{ "remove" : { "index" : "test1", "alias" : "alias1" } },
{ "add" : { "index" : "test2", "alias" : "alias1" } }
]
}'
将别名与多个索引关联:
curl -X POST "localhost:9200/_aliases?pretty" -H 'Content-Type: application/json' -d'
{
"actions" : [
{ "add" : { "index" : "test1", "alias" : "alias1" } },
{ "add" : { "index" : "test2", "alias" : "alias1" } }
]
}'
curl -X POST "localhost:9200/_aliases?pretty" -H 'Content-Type: application/json' -d'
{
"actions" : [
{ "add" : { "indices" : ["test1", "test2"], "alias" : "alias1" } }
]
}'
查看索引关联的别名信息:
curl -X GET "localhost:9200/twitter/_alias?pretty"
查看 Elasticsearch 集群上所有索引的别名:
curl -X GET "localhost:9200/_alias?pretty"
Index Template
索引模版用于在创建新索引时自动应用的模版,模版可以包含 settings、mappings、aliases 以及一个索引匹配列表,如果新创建的索引匹配该索引列表,则为新创建的索引自动应用该索引模版。模版仅在一个索引被新创建时才会产生作用,修改模版不会影响已经创建的索引。
Index Template 的工作方式:
当一个索引被新创建时
- 首先会使用 Elasticsearch 中默认的 Settings 和 Mappings 设置。
- 然后会使用 order 数值低的 Index Template 中的设定。
- 再使用 order 数值高的 Index Template 中的设定,之前的设定会被覆盖。
- 最后会去看索引创建时,用户有没有指定 Settings 和 Mappings 设置,有则覆盖之前模版中的设定。
创建索引模版:
curl -X PUT "localhost:9200/_template/template_1?pretty" -H 'Content-Type: application/json' -d'
{
"index_patterns": ["te*", "bar*"],
"settings": {
"number_of_shards": 1
},
"mappings": {
"_source": {
"enabled": false
},
"properties": {
"host_name": {
"type": "keyword"
},
"created_at": {
"type": "date",
"format": "EEE MMM dd HH:mm:ss Z yyyy"
}
}
}
}'
删除索引模版:
curl -X DELETE "localhost:9200/_template/template_1?pretty"
查询索引模版(支持逗号分隔的索引列表):
curl -X GET "localhost:9200/_template/template_1?pretty"
curl -X GET "localhost:9200/_template?pretty"
在新增索引时可能同时匹配到了多个索引模板,在这种情况下,settings 和 mappings 都会合并到索引的最终配置中。可以使用 order 参数控制合并的先后顺序,order 较高的配置会覆盖较低的配置,例如:
curl -X PUT "localhost:9200/_template/template_1?pretty" -H 'Content-Type: application/json' -d'
{
"index_patterns" : ["*"],
"order" : 0,
"settings" : {
"number_of_shards" : 1
},
"mappings" : {
"_source" : { "enabled" : false }
}
}'
curl -X PUT "localhost:9200/_template/template_2?pretty" -H 'Content-Type: application/json' -d'
{
"index_patterns" : ["te*"],
"order" : 1,
"settings" : {
"number_of_shards" : 1
},
"mappings" : {
"_source" : { "enabled" : true }
}
}'
索引信息监控
1. _stats
可以通过 _stats 来查看索引上不同操作的统计信息,该 API 提供了索引级范围的统计信息:
curl -X GET "localhost:9200/_stats?pretty"
curl -X GET "localhost:9200/twitter,twitter1/_stats?pretty"
curl -X GET "localhost:9200/twitter/_stats?pretty"
默认情况下,该 API 会返回所有统计数据。也可以在 URI 中指定只返回特定的统计数据。具体可以返回的统计数据如下所示:
- docs:当前索引的文档数及已删除文档数(已删除但还未进行段合并的文档数)
- store:索引大小
- indexing:索引统计信息
- get:get 操作的统计数据,包括 get missing 的统计数据
- search:search 操作的统计数据
- segments:Segment 的内存使用情况
- flush:Flush 操作的统计数据
- merge:Merge 操作的统计数据
- refresh:Refresh 操作的统计数据
- translog:Translog 操作的统计数据
2. _segments
通过 _segments 可以查看在构建 Lucene 索引时其内部更底层的 Segments 使用情况,包括在每个分片上的每个 Segment 的使用情况:
curl -X GET "localhost:9200/_segments?pretty"
curl -X GET "localhost:9200/twitter,twitter1/_segments?pretty"
curl -X GET "localhost:9200/twitter/_segments?pretty"
返回结果如下:
{
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"indices" : {
"twitter" : {
"shards" : {
"0" : [
{
"routing" : {
"state" : "STARTED",
"primary" : true,
"node" : "7f-bO8KZQj-bjxa6Wxlt0A"
},
"num_committed_segments" : 1,
"num_search_segments" : 1,
"segments" : {
"_2" : {
"generation" : 2,
"num_docs" : 1,
"deleted_docs" : 0,
"size_in_bytes" : 4715,
"memory_in_bytes" : 1876,
"committed" : true,
"search" : true,
"version" : "8.8.2",
"compound" : true,
"attributes" : {
"Lucene87StoredFieldsFormat.mode" : "BEST_SPEED"
}
}
}
}
]
}
}
}
}
从返回结果中可以看到,twitter 索引一共有 1 个分片,该分片内部有一个 Segment
- _2:Segment 的名称,在分片目录下所有以该名称开头的文件都属于此段
- num_docs:存储在该 Segment 中的未删除文档的数量
- deleted_docs:存储在该 Segment 中的已删除文档的数量
- size_in_bytes:该 Segment 使用的磁盘大小
- memory_in_bytes:该 Segment 使用的内存大小,Segment 会将一些数据存储在内存中以加速搜索
- committed:该 Segment 是否已在磁盘上同步,为 true 则已持久化到磁盘上了,即使为 false 未提交的数据也会存储在 translog 中,在 ES 下次重启时会被重放
- search:该 Segment 是否可搜索。为 false 很可能意味着该段已被写入磁盘,但此后没有发生刷新以使其可搜索。
- compound:该 Segment 是否存储在复合文件中。为 true 时意味着 Lucene 将来自该 Segment 的所有文件合并为一个文件,以节省文件描述符的使用。
索引状态管理
1. Clear Cache
我们可以手动清除一个或多个索引关联的所有缓存或特定缓存,具体如下:
curl -X POST "localhost:9200/twitter/_cache/clear?pretty"
curl -X POST "localhost:9200/twitter,twitter1/_cache/clear?pretty"
curl -X POST "localhost:9200/_cache/clear?pretty"
默认情况下,该 API 将清除索引关联的所有缓存。通过在 URI 中将 query、fielddata 或 request 参数设置为 true 可以显式清理特定的缓存:
curl -X POST "localhost:9200/twitter/_cache/clear?query=true&pretty"
curl -X POST "localhost:9200/twitter/_cache/clear?request=true&pretty"
curl -X POST "localhost:9200/twitter/_cache/clear?fielddata=true&pretty"
2. Flush
我们可以通过 Flush API 刷新一个或多个索引。索引的 flush 过程可以确保任何当前只持久化在事务日志中的数据也会永久地持久化在 Lucene 中。这减少了恢复时间,因为在打开 Lucene 索引后,不需要从事务日志重新索引数据了。默认情况下,Elasticsearch 会根据需要自动触发刷新。很少有用户需要直接调用 API。
curl -X POST "localhost:9200/twitter/_flush?pretty"
3. Refresh
通过 Refresh API 允许显式刷新一个或多个索引,使上一次刷新之后执行的所有操作都可用于搜索。
curl -X POST "localhost:9200/twitter/_refresh?pretty"
4. Force Merge
Elasticsearch 会定期进行 merge。此外通过该 API 可以强制合并一个或多个索引,强制合并允许通过合并分段来减少分段的数量,调用该操作将阻塞直到合并完成。如果 http 连接丢失,请求将在后台继续,任何新的请求都将被阻塞,直到前一次强制合并完成。强制合并应该只对只读索引调用。
curl -X POST "localhost:9200/twitter/_forcemerge?pretty"