创建索引的方式
三种创建索引的方式
| 方式 | 说明 | 适用场景 |
|---|---|---|
| 默认创建 | kibana里面直接运行 PUT /my_index,ES 会使用默认配置,并启用动态映射自动识别字段类型。 |
测试、快速原型开发。 |
| 自定义配置创建 | 创建索引时,在请求体中明确指定 settings(分片、副本等)和 mappings(字段类型、分析器等)。 |
生产环境,需要对性能和数据类型精细控制。 |
| 使用模板创建 | 预先定义好 index_patterns(如 logs-*),当创建匹配模式的索引时,模板配置会自动应用。 |
数据量很大(如日志、事件数据),需要自动管理大量索引。 |
例如,在kibana 的dev tools运行命令
PUT /product_info
{
"settings": {
"number_of_shards": 3, // 设置主分片数,设置后不能修改,除非reindex,需提前规划
"number_of_replicas": 2 // 设置副本数,用于提高数据可用性和查询吞吐量,可动态调整
},
"mappings": {
"properties": {
"product_id": {
"type": "keyword" // 关键词类型,用于精确匹配
},
"name": {
"type": "text", // 文本类型,用于全文搜索
"fields": {
"keyword": {
// 为 name 字段增加一个子字段
"type": "keyword",
"ignore_above": 256 // 如果字符串的长度 超过当前设定的值,那么该字符串不会被索引,也不会被存储到倒排索引中
}
}
},
"price": {
"type": "double" // 双精度浮点数
},
"in_stock": {
"type": "boolean" // 布尔类型
},
"created_date": {
"type": "date", // 日期类型
"format": "yyyy-MM-dd HH:mm:ss||epoch_millis"
}
}
}
}
索引参数优化
主分片数 number_of_shards 值的选择。
首先,分片的大小建议为 10GB ~ 50GB。分片太多(如每个分片只有几百 MB)会产生管理开销;分片太大(如 >100GB)会导致故障恢复慢。
分片数应 ≥ 节点数,才能充分利用所有节点的计算资源。例如有 3 个数据节点,分片数可以设为 3 或 6 等。shard_count = max(节点数, ceil(预估总数据量 / 30GB))。
副本数 number_of_replicas 值的选择。
副本是主分片的完整拷贝,用于高可用和扩展查询吞吐。但是每个副本都会消耗和主分片一样多的磁盘。副本数越多,存储成本线性增加。
动态调整 PUT /index/_settings
至少 1 个副本(即副本数 ≥ 1)才能容忍一个节点宕机而不丢数据。
如果读请求远多于写请求,增加副本数可以让查询分散到更多分片上,减轻主分片压力。
测试环境为0或1,生产环境至少为1,通常1或2即可。
索引参数优化示例
计算示例
假如,数据量为500 GB,集群有五个数据节点,每个节点磁盘2TB。
500GB / 30GB ≈ 17个分片。选择节点数的倍数,15或者20都可以。
副本数1。
总分片数 = 15(主分片) (1 + 1(副本数)) = 15 2 = 30
最终每个节点承担6个分片,包括5主1副本。
reindex操作
reindex 是将一个索引(或跨多个索引)中的数据复制到另一个索引(或数据流)的操作。常用于:
- 修改映射(如字段类型错误需要重建)。
- 更改分片数(
number_of_shards不可直接修改,必须 reindex 到新索引)。 - 升级索引版本(如从 6.x 到 7.x 需要重新索引)。
- 合并或过滤数据。
例如下面的命令是将老索引的数据复制到新索引
POST _reindex
{
"source": {
"index": "old_index"
},
"dest": {
"index": "new_index"
}
}
reindex参数调优
一些可以调优的参数
- slcies:任务拆分成多个并行子任务,每个子任务处理一部分数据,大幅加速迁移
- auto:自动确定切片数,小数据量
- 手动指定正整数N,大数据量,N = 主分片数(1~2倍)
- 不设置,单线程执行,相当于1
POST _reindex { "source": { "index": "big_logs" }, "dest": { "index": "big_logs_new" }, "slices": 6 // 分成6个并行子任务 }
requests_per_second:限制reindex每秒处理的文档操作数(包括索引、更新、删除),避免过度消耗集群资源。-
- 正整数:每秒不超过该数目。
-1或省略:不限速。0:暂停任务(恢复时设为正数)。
一开始的时候指定值
后续打算修改值(动态调整),POST _reindex { "source": { "index": "old" }, "dest": { "index": "new" }, "requests_per_second": 1000 // 每秒最多 1000 个文档 }task_id可以从GET _tasks获得。POST _reindex/<task_id>/_rethrottle { "requests_per_second": 500 }
-
监控所有reindex任务
查询所有正在运行(或最近完成)的 reindex 任务,可以监控进度、限速状态、错误等。
GET _tasks?detailed=true&actions=*reindex
一些需要关注的字段及其含义。
| 字段 | 含义 |
|---|---|
action |
任务类型,如 indices:data/write/reindex。 |
status.total |
需要处理的总文档数(如果源索引能提前获取)。 |
status.created |
已经成功创建的文档数(目标索引新增)。 |
status.updated |
已经更新的文档数(覆盖写入)。 |
status.version_conflicts |
版本冲突数(例如目标索引已存在同 _id 文档且未覆盖)。 |
status.retries.bulk |
批次写入失败重试次数。 |
status.throttled_millis |
该任务因限速而主动等待的总毫秒数。 |
requests_per_second |
当前设定的限速值。 |
running_time_in_nanos |
任务已经运行的纳秒数。 |
GET _tasks/<task_id>– 查看特定任务。POST _tasks/<task_id>/_cancel– 取消一个正在运行的 reindex。
reindex之后删除老索引
reindex不会删除老索引,要想删除老索引,使用下面的语句,最好确认后删除
DELETE /old_index
如何判断reindex成功
先比较文档数,看是否一样,这一步之后还是需要检查id的,数量一样并不能完全确认成功
GET /old_index/_count
GET /new_index/_count
查看 _id,scroll只返回 _id,不返回 _source
GET /old_index/_search?scroll=1m&filter_path=hits.hits._id
{
"size": 1000,
"_source": false
}
reindex的影响(重要)
| 影响维度 | 说明 |
|---|---|
| 集群资源消耗 | reindex 会占用大量 CPU、内存 和 磁盘 I/O,尤其在数据量大时。 |
| 写入压力 | 目标索引的写入速度可能影响其他正常写入操作。建议在低峰期执行。 |
| 源索引性能 | reindex 使用滚动搜索(scroll)读取源索引,可能增加源索引的堆内存压力。 |
| 外部查询 | 源索引和目标索引在 reindex 期间均可正常查询(但目标索引数据不完整时可能返回部分结果)。 |
| 操作可中断/恢复 | reindex 支持 _rethrottle 调整速率,也支持 task API 取消任务。如果中途失败,已写入的数据不会回滚,建议使用 op_type: create 避免重复。 |
索引数据结构(type)
除了以上type,还有其他的type,
例如:ip(用于存储和管理 IPv4 或 IPv6 地址)、geo_point (专门存储经纬度,用于地理位置相关的搜索和分析)、object (JSON 对象的默认映射方式。索引时会被“扁平化”处理。如果你的对象是一个数组且内部元素需要保持独立性,建议使用 nested 类型)、wildcard类型(7.9版本引入,专门用于高效通配符/正则表达式匹配)
object与nested
object扁平化处理示例
这是原先的
{
"users": [
{
"name": "Alice", "age": 25},
{
"name": "Bob", "age": 30}
]
}
默认object数组会变成
users.name = ["Alice", "Bob"]
users.age = [25, 30]
对应的数据失去了关联
例如查询 users.name=Alice AND users.age=30 会错误地匹配到该文档(因为两个条件分别从不同对象中取值)。
nested类型:将数组中的每个对象独立索引为一个 隐藏的嵌套文档,保持对象内部的关联性。
"users": {
"type": "nested",
"properties": {
"name": {
"type": "text" },
"age": {
"type": "integer" }
}
}
注意,查询时必须使用
nested查询
{
"nested": {
"path": "users",
"query": {
"bool": {
"must": [
{
"match": {
"users.name": "Alice" } },
{
"term": {
"users.age": 25 } }
]
}
}
}
}
这个存储类型,也不是万能的。适合用于每个对象独立、需要同时关联多个字段条件的情况(如商品规格列表、订单明细等场景)。
- ✅ 保留了对象内字段的逻辑独立性,避免“跨对象匹配”。
- ❌ 存储和查询开销更大(每个嵌套对象单独存储,join 成本高)。
- ❌ 写入性能有一定损耗。
wildcard
定义方式
{
"mappings": {
"properties": {
"path": {
"type": "wildcard"
}
}
}
}
- 需要频繁使用
*前缀、后缀或中间通配符的字段,如 URL 路径、文件路径、用户输入的模糊搜索词。
注意:
wildcard不适合完全精确匹配(用keyword更高效),也不适合全文搜索(用text)。
查询示例
{
"query": {
"wildcard": {
"path": {
"value": "*/user/*/profile*"
}
}
}
}
比 keyword 占用更多空间,因为需要存储自动机结构。
索引映射方式
es索引的映射方式有两种,分为动态和显式两种方式。
- 动态映射 (Dynamic Mapping):当索引一个包含新字段的文档时,ES 会自动推测并添加字段映射。适合快速迭代,但在生产环境中过度依赖可能导致类型错误,建议仅在开发测试环境使用或谨慎开启。
- 显式映射 (Explicit Mapping):即上面代码的内容,在创建索引时手动定义清楚所有字段属性。虽然工作量稍大,但能带来数据类型精确、搜索性能可控、避免意外字段的好处,是生产环境的推荐做法。
索引进阶用法
索引模板
这里的是 7.8+ 版本的 可组合索引模板
API 端点为 _index_template,例如 PUT _index_template/my_template
当你想要按天、按月动态生成的索引(如日志数据),手动逐个创建显然太麻烦,这时候可以用索引模板(Index Templates) 来批量自动管理。
PUT _index_template/order_logs_template // 模板名称,自定义
{
"index_patterns": ["order-logs-*"], // 索引模式,匹配的索引会自动应用此模板
"priority": 200, // 模板优先级,数字越大优先级越高
"template": {
// 具体配置,和普通索引一样
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1
},
"mappings": {
"properties": {
"timestamp": {
"type": "date" },
"message": {
"type": "text" }
}
}
}
}
配置好之后,如果你创建了 order-logs-2024-01-01 这个索引,它会自动应用上述配置,非常方便。
索引别名
索引别名(Index Alias) 像一个指向真实索引的指针,可以实现应用程序与底层索引的解耦。例如在数据迁移时,可以通过原子操作切换别名,实现服务的无缝过渡。
POST /_aliases
{
"actions": [
{
"remove": {
"index": "logs_v1", "alias": "app_logs" } },
{
"add": {
"index": "logs_v2", "alias": "app_logs" } }
]
}
和索引相关的常用命令
- *查询索引配置 :GET /product_info
- *查询索引字段映射:GET /product_info/_mapping
- *查询索引状态:GET /_cat/indices/product_info?v