说明
本文描述问题及解决方法适用于 腾讯云 Elasticsearch Service(ES)。
一、方案说明
此方案基于存算分离内核版本,评估ES存算分离版本的基础功能。
二、测试标准
项目 | 推荐 |
---|---|
测试组件 | Elasticsearch |
测试基准 | 自定义语句 |
测试方法 | 1. 业务验证1~2T数据的沉降、集群开销 2. 业务对比本地数据和cos数据的查询耗时 |
测试资源 | CPU和内存比 1:4,计算:192C 内存768G |
三、组件版本(腾讯侧)
名称 | 版本 |
---|---|
ES | 7.14.2 云原生存算分离版 |
四、机型推荐
ES 高IO 本地盘
节点类型 | 数量 | 配置 |
---|---|---|
Master节点 | 3 | ES标准型SA2CPU:32核,内存:64GES.SA2.8XLARGE64-32核64G |
Data节点 | 6 | ES高IO型CPU:32核 内存:128GB数据盘: NVMeSSD 3570GB x 2 |
五、测试步骤
1. 特性说明
支持将索引数据下沉、卸载到远程共享存储,副本和主分片共享一份数据,本地仅保留少量meta数据,降低磁盘占用。
2. 使用方式
存算分离特性需要在索引创建时选择打开或者关闭,不可动态修改。而下沉、卸载的时间都可以动态设置。
2.1. 存量索引切到存算分离
对于普通索引,可以按照下面的方式从普通索引转换到存算分离索引(不能从存算分离转换到普通索引)
对于自治索引或date stream,可以按照如下方法对后备索引逐个转换。
代码语言:javascript复制# 关闭索引,索引处于close状态不支持读写
POST ${index}/_close
# 设置为存算分离类型, 主分片48小时卸载,副本24小时卸载
PUT ${index}/_settings
{
"index.store.type":"hybrid_storage",
"index.hybrid_storage.segment.retention_period":"48h",
"index.hybrid_storage.segment.replica.retention_period":"24h"
}
# 打开索引。
POST ${index}/_open
# 验证:检查setting中的store.type属性是否为"hybrid_storage"
GET ${index}/_settins
2.2. 普通索引
2.2.1. 创建时指定
代码语言:javascript复制PUT /${index}?pretty {
"mappings":{
...
},
"settings":{
"index.store.type": "hybrid_storage"
}
}
settings中同时可以携带下沉、卸载参数。
2.2.2. 通过模版自动应用
索引模版创建后,如果有新建的索引名称能匹配上,则会应用模版中包含的mapping、settings。
例如创建名称为“default_open_hs_template”的模版,后续名称为"log-xxx"的新索引创建时都会应用此模版设置的内容:
代码语言:javascript复制PUT _template/default_open_hs_template
{
"index_patterns": [
"log-*"
],
"settings": {
"index.store.type": "hybrid_storage"
}
}
"index_patterns"可设置为"*"做全量匹配(无法匹配到data_stream或自治索引),模版"settings"中可同时携带其他索引级别下沉、卸载参数
2.3. 自治索引(未使用自治索引可忽略)
2.3.1. 创建时指定
在settings字段,增加"index.store.type":"hybrid_storage"的设定。
代码语言:javascript复制PUT /_data_stream/mylog
{
"mappings": {
...
},
"settings": {
...
"index.store.type": "hybrid_storage"
...
},
"policy": {
...
},
"options": {
...
}
}
2.3.2. 针对新滚动的后备索引
自治索引指向多个后备索引,已经滚动出来的后备索引需要通过close reopen的方式切换。对于后续新滚动的后备索引,可以更新自治索引的settings来修改:
代码语言:javascript复制PUT /_data_stream/${自治索引名称}/_update
{
"settings":{
"index.store.type":"hybrid_storage"
}
}
动态设置后,后续新滚动的索引均为存算分离类型。
2.3.3. 通过模版设定
自治索引也可以单独设置模版,通过名称匹配。例如,创建一个名为“default_data_stream_open_hs_template”的自治索引,同时创建可以匹配"*"的自治索引模版
(注:当前创建自治索引模版的接口和创建自治索引模版的接口一样,创建自治索引模版会同时创建一个后备索引,此自治索引不可以删除!!)
使用模版时,为避免普通索引在写入触发创建的场景自动变成自治索引,需要更新集群级别的settings
代码语言:javascript复制PUT _cluster/settings
{
"persistent":{
"action.auto_create_data_stream.use_default_write_mode": true
}
}
然后,设置新的自治索引。
代码语言:javascript复制PUT /_data_stream/default_data_stream_open_hs_template
{
"mappings": {
...
},
"settings": {
...
"index.store.type":"hybrid_storage"
...
},
"policy": {
...
},
"options": {
....
"template.patterns": [ # 设置模版匹配的patterns
"*"
],
"template.priority": 100000 # 模版的优先级
}
}
示例:
1. 创建自治索引模版
2. 创建自治索引,不包含任何settings,创建出来后可以看到新后备索引应用了模版settings
2.4. segment未卸载配置排查
索引配置retention_period是执行卸载的阈值,内部有一些检查索引是否停写的逻辑,因此配置为24h,并不一定会严格在24h后卸载:
代码语言:javascript复制"index.hybrid_storage.segment.retention_period":"24h",
"index.hybrid_storage.segment.replica.retention_period":"24h"
一些影响卸载触发的配置:
配置名称 | 解释 | 默认值 | 是否可以动态调整 |
---|---|---|---|
index.hybrid_storage.read.only.period | 分片数据写入后,过了这么久的时间还没有新的写入,便认为达到了readonly状态 | 24h | 是 |
index.hybrid_storage.uninstall.check_readonly.enable | readonly状态是否作为segment卸载的前提条件 | true | 是 |
index.hybrid_storage.uninstall.check_shard_uploaded.enable | (仅针对primary)分片上的segment卸载前,是否要求当前分片的所有segment都已经是下沉状态。如果设置为true,主分片上只要存在一个还未下沉的segment,该分片上的所有segment都不会卸载 | true | 是 |
index.hybrid_storage.segment.upload_period.min | 有一些小segment达不到下沉的阈值(默认1G),尝试强制merge后做下沉,要满足两个条件:条件一:索引从创建开始算起,生存的时间处于[min, max]之间条件二:达到了readonly状态。举例:按照默认配置[min, max] = [24h, ~],readonly=24h索引创建时间是2023.08.18T00:00:00,写入持续到了2023.08.18T06:00:00 那么:在2023.08.19T00:00:00 处于[min, max]之间,满足了第一个条件在2023.08.19T06:00:00 最后一次写入后过了24h,满足了第二个条件会把所有小segment做强制合并后下沉。 | 24h | 是 |
index.hybrid_storage.segment.upload_period.max | Long.MAX_VALUE h | 是 |
3. 测试用例
3.1. 创建索引
代码语言:javascript复制PUT /hybrid?pretty {
"settings":{
"index":{
"number_of_shards":3, // 每个节点一个分片
"number_of_replicas":1 // 1个副本
},
"index.store.type": "hybrid_storage", // 指定索引为混合存储的索引,静态配置
"index.hybrid_storage.segment.retention_period": "10m", // segment 从索引创建到达卸载的时间周期,动态配置
"index.hybrid_storage.segment.replica.retention_period": "5m", // 副本分片segment卸载时间周期
"index.hybrid_storage.read.only.period": "5m", //分片数据写入后,过了这么久的时间还没有新的写入,便认为达到了readonly状态
"index.hybrid_storage.uninstall.check_readonly.enable": true // 卸载 segment 之前检查其是否超过只读检查时间
}
}
3.2. 数据准备
单条数据插入,共插入 188 条数据。
代码语言:javascript复制curl -X POST "/hybrid/_doc/?pretty" -H 'Content-Type: application/json' -d'
{
"@timestamp": "2022-11-15T13:12:00",
"message": "GET /search HTTP/1.1 200 1070000",
"user": {
"id": "elastic"
}
}
'
3.3. Segment 类型说明
通过 _cat/segments/index?v 查看指定索引的 segment 列表及状态,最后一列为状态列。状态分类:
3.2.1. DEFAULT
默认状态。存储类型未配置为 hybrid_storage 的索引,或者配置为 hybrid_storage 的索引但 segment 还没有达到下沉阈值的状态。
3.2.2. READY_UPLOAD
待下沉状态。Segment 达到配置的下沉阈值,冻结不再参与合并,等待下沉。
3.2.3. UPLOADING
正在下沉状态,文件向cos上传进行中
3.2.4. UPLOADED
已下沉状态。Segment 已被下沉到对象存储,本地磁盘上同时也有一份。
3.2.5. UNINSTALLED → OFFLOADED
已卸载状态。Segment 已下沉到 COS 且超过了配置的卸载时间周期,卸载删除本地数据文件,查询依赖缓存和对象存储。
3.4. 基础流程验证
查看segment: GET _cat/segments/${index}
3.4.1. 未冻结状态
未配置为 hybrid_storage 类型或配置了未进入冻结状态:
3.4.2. 超过配置阈值,进入冻结待下沉状态
3.4.3. 已经上传对象存储
3.5. 卸载后查询验证
卸载之前后索引 188 条数据,保持不变。
3.5.1. match all 查询
3.5.2. count 查询
3.6. 获取索引、分片级别下沉、卸载 segment 数量和百分比
打开集群级别设置:
代码语言:javascript复制PUT _cluster/settings
{
"persistent": {
"cluster.nodes.hybrid_storage_stats.enable": "true"
}
}
GET _cat/indices/hybrid*?h=index,segments.count,segments.uploaded_count,segments.uninstalled_count,segments.uploaded_percent,segments.uninstalled_percent&v
GET _cat/shards/hybrid*?h=index,segments.count,segments.uploaded_count,segments.uninstalled_count,segments.uploaded_percent,segments.uninstalled_percent&v
或:uninstalled → offloaded
代码语言:javascript复制GET _cat/indices/hybrid*?h=index,segments.count,segments.uploaded_count,segments.offloaded_count,segments.uploaded_percent,segments.offloaded_percent&v
GET _cat/shards/hybrid*?h=index,segments.count,segments.uploaded_count,segments.offloaed_count,segments.uploaded_percent,segments.offloaded_percent&v
代码语言:javascript复制#总数量
segments.count:segment
#已下沉到 cos 的 segment 数量,已卸载的也会计算进来(卸载必定已下沉)。
segments.uploaded_count
#已卸载的 segment 数量。
segments.uninstalled_count
#已下沉到 cos 的 segments 百分比,已卸载的也会计算进来,计算公式:(segments.uploaded_count/segments.count) *100。
segments.uploaded_percent
#已卸载的 segment 百分比,计算公式:(segments.uninstalled_count/segments.count) *100。
segments.uninstalled_percent
3.7. 缓存验证
3.7.1. 查询验证
统计信息字段说明
查看全部节点:
代码语言:javascript复制GET _hybrid_storage/cache/stats
参数 | 含义 |
---|---|
total_cache_size_in_bytes | 稀疏缓存文件的大小(参数indices.hybrid_storage.cache.size设置的) |
actual_cache_size_in_bytes | 缓存文件中实际缓存的索引数据的大小(索引segment中各个文件被缓存的范围之和) |
total_regions | 稀疏缓存文件中总共的region个数 |
used_regions | 稀疏缓存文件中已使用的region个数 |
region_size_in_bytes | region的大小 |
read_count | 从磁盘读取数据的次数 |
read_total_bytes_in_bytes | 总共lucene需要读取的数据范围之和 |
read_cache_bytes_in_bytes | 总共lucene需要读取的数据范围之和中从缓存文件读取了多少 |
write_count | 将数据写入磁盘的次数 |
write_bytes_in_bytes | 从节点启动,总共写入到缓存文件的字节数之和(包括被驱逐的) |
evict_count | 被驱逐的region个数之和(每次驱逐都会累加,可能大于总共region的个数) |
查看全部索引:
代码语言:javascript复制GET _hybrid_storage/stats
参数 | 含义 |
---|---|
file_ext | 文件后缀 |
num_files | 针对单个分片,快照中该文件后缀的共有多少个文件(其实就是多少个segment) |
open_count | 文件打开的次数(openInput) |
close_count | 文件关闭的次数(IndexInput.close) |
total_in_bytes | 针对单个分片,快照中该文件后缀的所有文件长度之和 |
min_in_bytes | 针对单个分片,快照中该文件后缀的所有文件长度最小值 |
max_in_bytes | 针对单个分片,快照中该文件后缀的所有文件长度最大值 |
average_in_bytes | 针对单个分片,快照中该文件后缀的所有文件长度平均值 |
contiguous_bytes_read | 连续读取的总次数、总大小、最小值、最大值 |
non_contiguous_bytes_read | 非连续读取的总次数、总大小、最小值、最大值 |
cached_bytes_read | 从缓存文件读取(包括文件头的缓存文件)的 总次数、总大小、最小值、最大值 |
index_cache_bytes_read | 从索引缓存中读取(从索引缓存中查找时说明文件头缓存文件不存在)的 总次数、总大小、最小值、最大值 |
cached_bytes_written | 将数据写入到缓存文件(包括文件头的缓存文件)的 总次数、总大小、最小值、最大值 |
direct_bytes_read | 当从缓存文件读取数据失败时,直接从cos读取数据的大小 |
optimized_bytes_read | 暂时没用到 |
forward_seeks | 向后跳转文件指针 |
forward_seeks.small | seek的跨度小于8M的跨度累计 |
forward_seeks.large | seek的跨度大于8M的跨度累计 |
backward_seeks | 向前跳转文件指针 |
backward_seeks.small | seek的跨度小于8M的跨度累计 |
backward_seeks.large | seek的跨度大于8M的跨度累计 |
blob_store_bytes_requested | 当缓存文件没有该索引相应文件的缓存时从cos读取的数据大小 |
lucene_bytes_read | lucene总共需要读取的大小 |
current_index_cache_fills | 当前有多少请求正在向索引缓存中填充缓存blob |
3.7.2. 校验查询是否经过了cache
节点统计信息:
查看全部节点:
代码语言:javascript复制GET _hybrid_storage/cache/stats
查看指定节点:
代码语言:javascript复制GET _hybrid_storage/{nodeId}/cache/stats
文件统计信息
查看全部索引:
代码语言:javascript复制GET _hybrid_storage/stats
查看指定索引:
代码语言:javascript复制GET {index}/_hybrid_storage/stats
3.7.3. 清空缓存验证
查询节点统计信息:
代码语言:javascript复制GET _hybrid_storage/cache/stats
清空缓存
清空全部索引:
代码语言:javascript复制POST _hybrid_storage/cache/clear
清空指定索引:
代码语言:javascript复制POST {index}/_hybrid_storage/cache/clear
0 说明缓存被驱逐
我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!