在向量检索场景下,随着向量维度和数据规模的激增,集群会面临写入吞吐量低、查询时延(P99)抖动剧烈等性能瓶颈。与传统的关键词匹配不同,向量索引的构建是计算密集型的,而检索过程则是访存密集型的,标准的Elasticsearch/OpenSearch配置难以支撑其复杂的拓扑图计算。为了解决这些痛点,CSS向量数据库支持对集群进行性能调优,从写入侧、查询侧提供全栈优化方案,助您在保障召回率的同时,实现系统性能与成本的最佳平衡。
写入性能优化
向量数据写入时涉及副本同步、索引刷新(refresh)和段合并(merge)三大开销。索引实时写入时,频繁的索引刷新会生成大量小segment,触发额外的向量索引构建和合并操作,消耗大量CPU/IO资源。因此,写入性能优化,主要从这几方面入手。
- 方案介绍
在数据导入期间临时关闭副本,待数据导入完成后再重新开启。适用于批量导入历史数据或全量更新场景(如初始化向量数据库)。
- 操作
修改副本数:
PUT {index_name}/_settings
{
"number_of_replicas": 0
} - 效果
避免副本节点的实时向量索引构建开销,提升写入性能。
- 方案介绍
将索引的刷新间隔设置为120s或更大,以减少频繁刷新索引生成的小segment数量,同时降低segment合并带来的向量索引构建开销。也可以直接关闭自动刷新索引,即将索引的刷新间隔设置为“-1”。适用于高吞吐写入场景(如日志型向量数据流)。
- 操作
设置“refresh_interval”:
PUT {index_name}/_settings
{
"refresh_interval": "120s"
} - 效果
减少refresh次数,避免生成大量小segment,降低segment合并开销,提升写入性能。
- 方案介绍
适当增加向量索引构建的线程数可以加速索引构建过程,但是不建议设置过大,避免产生过多的构建线程抢占查询资源。适用于CPU资源充足但写入延迟高的场景(如GPU服务器环境)。
- 操作
向量索引构建的线程数“native.vector.index_threads”默认值为 4,可以根据实际需求进行调整:
PUT _cluster/settings
{
"persistent": {
"native.vector.index_threads": 8
}
} - 效果
并行加速向量索引构建,提升写入并发能力。
查询性能优化
查询性能受segment数量、内存熔断机制及字段召回方式影响。大量segment会降低搜索效率;堆外内存不足时向量索引频繁换入换出;召回全字段会加重fetch阶段负载。因此,查询性能优化,主要从这几方面入手。
- 方案介绍
在批量导入数据完成后,执行forcemerge操作强制段合并可以减少segment数量。该操作一般在写入后、查询前执行(如定时批量导入后的检索准备)。
- 操作
执行forcemerge操作:
POST {index_name}/_forcemerge?max_num_segments=1 - 效果
将多个segment合并为1,减少搜索时文件扫描开销,提升查询速度。
- 方案介绍
在实时写入和更新场景中,通过调整自动Merge策略的相关参数,可以加速碎片化segment的合并进程,形成更大的segment,从而减少segment总数,提升查询性能。
- 操作
调整索引的自动Merge策略:
PUT {index_name}/_settings
{
"index": {
"merge": {
"policy": {
"max_merged_segment": "10gb",
"max_merge_at_once": 10,
"segments_per_tier": 5,
"floor_segment": "200mb"
}
}
}
} 表1 调整自动Merge策略的参数说明 | 参数 | 类型 | 默认值 | 说明 |
| max_merged_segment | String | 5GB | 设置单次合并后生成的Segment最大容量,超过该值的Segment不再参与自动合并(除非执行强制合并)。限制单个文件无限增大,防止超大段合并时阻塞IO链路。对于向量索引可以适当调大该值,以减少Segment数量。 取值格式:正整数+单位 支持的单位:B、KB、MB、GB(不区分大小写) |
| max_merge_at_once | Integer | 10 | 设置单次合并任务允许同时处理的最大Segment数量。该值越大,单次合并的力度越强,能有效减少Segment总数,但会瞬间消耗较多磁盘IO和CPU。 最小值:2 |
| segments_per_tier | Integer | 10 | 每层分段允许容纳的最大Segment数量。该值越小,合并越频繁,查询越快,但写入压力大;值越大,允许存在的段越多,有利于提升写入吞吐,但会牺牲查询性能。适当调小该值可以更快触发自动Merge,减少碎片Segment的数量。 取值范围:大于或等于max_merge_at_once |
| floor_segment | String | 2MB | 设置最小的segment大小阈值,小于该值的segment会被优先合并。适当增大该值可以更快触发自动Merge,减少碎片Segment的数量。 取值格式:正整数+单位 支持的单位:B、KB、MB、GB(不区分大小写) |
- 效果
有效减少segment数量,减少查询计算开销,提升查询速度。
- 方案介绍
如果向量索引所需的堆外内存超过熔断线,查询时索引的缓存管理器会频繁进行索引的换进换出操作,导致查询变慢。通过适当调大熔断线的配置,可以避免因内存不足导致的查询频繁触发熔断(如日志提示“CircuitBreakingException”)。
- 操作
堆外内存熔断线的默认值是80%,可以根据实际需求进行调整:
PUT _cluster/settings
{
"persistent": {
"native.cache.circuit_breaker.cpu.limit": "85%"
}
} - 效果
避免向量索引被换出,减少查询抖动。
- 方案介绍
如果查询结果需要返回的字段较少且均为keyword或数值类型字段,可以通过docvalue_fields配置来召回必要字段。这种方式适用于仅需返回数值/枚举类元数据(如商品ID、分类标签)的场景,可以有效降低fetch阶段的开销。
- 操作
通过docvalue_fields配置仅召回必要字段:
POST {index_name}/_search
{
"size": 2,
"stored_fields": ["_none_"],
"docvalue_fields": ["my_label"],
"query": {
"vector": {
"my_vector": {
"vector": [1, 1],
"topk": 2
}
}
}
} - 效果
跳过_source解析,利用列式存储(docvalues)降低fetch阶段开销,提升查询性能。