
# 优化读写性能
在向量检索场景下，随着向量维度和数据规模的激增，集群会面临写入吞吐量低、查询时延（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）
- 方案介绍 在批量导入数据完成后，执行forcemerge操作强制段合并可以减少segment数量。该操作一般在写入后、查询前执行（如定时批量导入后的检索准备）。
  
- 操作 执行forcemerge操作：
  ```
  POST {index_name}/_forcemerge?max_num_segments=1
  ```
  
- 效果 将多个segment合并为1，减少搜索时文件扫描开销，提升查询速度。
  
 
方案二：调整自动Merge策略
- 方案介绍 在实时写入和更新场景中，通过调整自动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阶段开销，提升查询性能。
  
 
