Elasticsearch 存算分离功能 POC 方案

2023-11-03 16:25:31 浏览数 (1)

说明

本文描述问题及解决方法适用于 腾讯云 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腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!

0 人点赞