【高速缓存】RedisVL 索引迁移指南:安全演进索引结构

实验性功能声明

索引迁移器目前为实验性特性,其 API、CLI 命令以及磁盘存储格式(计划、检查点、备份)在后续版本中可能发生变更。在生产环境应用迁移前,请务必仔细审查迁移计划。

本文将介绍如何使用 RedisVL 的迁移工具安全地修改已有索引的结构,无论是增加字段、删除字段、重命名,还是进行向量量化优化。


快速上手:4 条命令为索引添加新字段

bash 复制代码
# 1. 查看当前存在的所有索引
rvl index listall --url redis://localhost:6379

# 2. 使用交互式向导生成迁移计划
rvl migrate wizard --index myindex --url redis://localhost:6379

# 3. 执行迁移
rvl migrate apply --plan migration_plan.yaml --backup-dir ./migration_backups --url redis://localhost:6379

# 4. 验证迁移结果
rvl migrate validate --plan migration_plan.yaml --url redis://localhost:6379

前置条件

  • 已安装 RedisVLpip install redisvl
  • 运行中的 Redis 实例(推荐 Redis 8.0+,支持全部功能),且已加载 Search 模块(Redis Stack、Redis Cloud 或 Redis Software 均可)
  • 待迁移的已有索引

本地开发环境(快速启动 Redis 8.0):

bash 复制代码
docker run -d --name redis -p 6379:6379 redis:8.0

注意:INT8/UINT8 向量数据类型需要 Redis 8.0+;SVS-VAMANA 算法需要 Redis 8.2+ 及 Intel AVX-512 硬件支持。


迁移工作原理(三阶段流程)

每一次迁移都遵循相同的三阶段流程:描述变更 → 生成计划 → 执行计划。下图清晰展示了单索引迁移的完整链路:
#mermaid-svg-92yMVGA8cK3yzYwl{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-92yMVGA8cK3yzYwl .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-92yMVGA8cK3yzYwl .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-92yMVGA8cK3yzYwl .error-icon{fill:#552222;}#mermaid-svg-92yMVGA8cK3yzYwl .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-92yMVGA8cK3yzYwl .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-92yMVGA8cK3yzYwl .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-92yMVGA8cK3yzYwl .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-92yMVGA8cK3yzYwl .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-92yMVGA8cK3yzYwl .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-92yMVGA8cK3yzYwl .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-92yMVGA8cK3yzYwl .marker{fill:#333333;stroke:#333333;}#mermaid-svg-92yMVGA8cK3yzYwl .marker.cross{stroke:#333333;}#mermaid-svg-92yMVGA8cK3yzYwl svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-92yMVGA8cK3yzYwl p{margin:0;}#mermaid-svg-92yMVGA8cK3yzYwl .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-92yMVGA8cK3yzYwl .cluster-label text{fill:#333;}#mermaid-svg-92yMVGA8cK3yzYwl .cluster-label span{color:#333;}#mermaid-svg-92yMVGA8cK3yzYwl .cluster-label span p{background-color:transparent;}#mermaid-svg-92yMVGA8cK3yzYwl .label text,#mermaid-svg-92yMVGA8cK3yzYwl span{fill:#333;color:#333;}#mermaid-svg-92yMVGA8cK3yzYwl .node rect,#mermaid-svg-92yMVGA8cK3yzYwl .node circle,#mermaid-svg-92yMVGA8cK3yzYwl .node ellipse,#mermaid-svg-92yMVGA8cK3yzYwl .node polygon,#mermaid-svg-92yMVGA8cK3yzYwl .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-92yMVGA8cK3yzYwl .rough-node .label text,#mermaid-svg-92yMVGA8cK3yzYwl .node .label text,#mermaid-svg-92yMVGA8cK3yzYwl .image-shape .label,#mermaid-svg-92yMVGA8cK3yzYwl .icon-shape .label{text-anchor:middle;}#mermaid-svg-92yMVGA8cK3yzYwl .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-92yMVGA8cK3yzYwl .rough-node .label,#mermaid-svg-92yMVGA8cK3yzYwl .node .label,#mermaid-svg-92yMVGA8cK3yzYwl .image-shape .label,#mermaid-svg-92yMVGA8cK3yzYwl .icon-shape .label{text-align:center;}#mermaid-svg-92yMVGA8cK3yzYwl .node.clickable{cursor:pointer;}#mermaid-svg-92yMVGA8cK3yzYwl .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-92yMVGA8cK3yzYwl .arrowheadPath{fill:#333333;}#mermaid-svg-92yMVGA8cK3yzYwl .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-92yMVGA8cK3yzYwl .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-92yMVGA8cK3yzYwl .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-92yMVGA8cK3yzYwl .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-92yMVGA8cK3yzYwl .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-92yMVGA8cK3yzYwl .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-92yMVGA8cK3yzYwl .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-92yMVGA8cK3yzYwl .cluster text{fill:#333;}#mermaid-svg-92yMVGA8cK3yzYwl .cluster span{color:#333;}#mermaid-svg-92yMVGA8cK3yzYwl div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-92yMVGA8cK3yzYwl .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-92yMVGA8cK3yzYwl rect.text{fill:none;stroke-width:0;}#mermaid-svg-92yMVGA8cK3yzYwl .icon-shape,#mermaid-svg-92yMVGA8cK3yzYwl .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-92yMVGA8cK3yzYwl .icon-shape p,#mermaid-svg-92yMVGA8cK3yzYwl .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-92yMVGA8cK3yzYwl .icon-shape .label rect,#mermaid-svg-92yMVGA8cK3yzYwl .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-92yMVGA8cK3yzYwl .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-92yMVGA8cK3yzYwl .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-92yMVGA8cK3yzYwl :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 阶段3: 执行迁移
阶段2: 生成迁移计划
阶段1: 构建变更描述
交互式向导
SchemaPatch YAML
手动编写
Planner.create_plan
MigrationPlan YAML
Executor.apply
MigrationReport YAML


阶段 1:构建 SchemaPatch(变更描述)

SchemaPatch 是一个 YAML 文件,它声明您想要进行的变更,而非完整的最终模式。您可以通过交互式向导生成,也可以手动编写。

Patch 包含五个可选的变更节:

节区 作用
add_fields 向索引添加新字段定义
remove_fields 从索引中移除字段(文档数据仍然保留,只是不再索引)
rename_fields 重命名字段,同时更新索引模式和所有文档(执行 HGET→HSET→HDEL 或 JSON 路径更新)
update_fields 修改字段属性:算法、数据类型、距离度量、是否可排序、分隔符等
index 修改索引名称或键前缀

例如,一个手动编写的 schema_patch.yaml

yaml 复制代码
version: 1
changes:
  add_fields:
    - name: category
      type: tag
      path: $.category
      attrs:
        separator: "|"
  remove_fields:
    - legacy_field
  update_fields:
    - name: embedding
      attrs:
        datatype: float16        # 向量量化
        algorithm: HNSW

阶段 2:生成 MigrationPlan(迁移计划)

Planner 组件连接 Redis,对当前在线索引进行快照 (包括 schema、统计信息、键样本和前缀),然后将 Patch 合并到源 schema 中,生成 merged_target_schema。同时,它会分类每个变更是否为支持阻塞,并提取重命名操作。

生成的计划 YAML 包含:

  • source:计划生成时线上索引的冻结快照(schema、stats、key sample、prefixes)
  • requested_changes:应用的 patch
  • merged_target_schema:源 schema + patch → 迁移后的最终 schema
  • diff_classification:是否支持迁移以及阻塞原因(如有)
  • rename_operations:提取出的索引重命名、前缀变更和字段重命名
  • warnings:重要提示(如是否需要停机、是否有损量化等)

关键点:同一个 Patch 对不同索引会生成不同的计划,因为每个索引的源 schema 不同。


阶段 3:执行迁移(Apply)

执行器读取计划并依次执行以下步骤:
#mermaid-svg-uFAkYVBv1gfLElIy{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-uFAkYVBv1gfLElIy .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-uFAkYVBv1gfLElIy .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-uFAkYVBv1gfLElIy .error-icon{fill:#552222;}#mermaid-svg-uFAkYVBv1gfLElIy .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-uFAkYVBv1gfLElIy .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-uFAkYVBv1gfLElIy .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-uFAkYVBv1gfLElIy .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-uFAkYVBv1gfLElIy .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-uFAkYVBv1gfLElIy .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-uFAkYVBv1gfLElIy .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-uFAkYVBv1gfLElIy .marker{fill:#333333;stroke:#333333;}#mermaid-svg-uFAkYVBv1gfLElIy .marker.cross{stroke:#333333;}#mermaid-svg-uFAkYVBv1gfLElIy svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-uFAkYVBv1gfLElIy p{margin:0;}#mermaid-svg-uFAkYVBv1gfLElIy .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-uFAkYVBv1gfLElIy .cluster-label text{fill:#333;}#mermaid-svg-uFAkYVBv1gfLElIy .cluster-label span{color:#333;}#mermaid-svg-uFAkYVBv1gfLElIy .cluster-label span p{background-color:transparent;}#mermaid-svg-uFAkYVBv1gfLElIy .label text,#mermaid-svg-uFAkYVBv1gfLElIy span{fill:#333;color:#333;}#mermaid-svg-uFAkYVBv1gfLElIy .node rect,#mermaid-svg-uFAkYVBv1gfLElIy .node circle,#mermaid-svg-uFAkYVBv1gfLElIy .node ellipse,#mermaid-svg-uFAkYVBv1gfLElIy .node polygon,#mermaid-svg-uFAkYVBv1gfLElIy .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-uFAkYVBv1gfLElIy .rough-node .label text,#mermaid-svg-uFAkYVBv1gfLElIy .node .label text,#mermaid-svg-uFAkYVBv1gfLElIy .image-shape .label,#mermaid-svg-uFAkYVBv1gfLElIy .icon-shape .label{text-anchor:middle;}#mermaid-svg-uFAkYVBv1gfLElIy .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-uFAkYVBv1gfLElIy .rough-node .label,#mermaid-svg-uFAkYVBv1gfLElIy .node .label,#mermaid-svg-uFAkYVBv1gfLElIy .image-shape .label,#mermaid-svg-uFAkYVBv1gfLElIy .icon-shape .label{text-align:center;}#mermaid-svg-uFAkYVBv1gfLElIy .node.clickable{cursor:pointer;}#mermaid-svg-uFAkYVBv1gfLElIy .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-uFAkYVBv1gfLElIy .arrowheadPath{fill:#333333;}#mermaid-svg-uFAkYVBv1gfLElIy .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-uFAkYVBv1gfLElIy .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-uFAkYVBv1gfLElIy .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uFAkYVBv1gfLElIy .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-uFAkYVBv1gfLElIy .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uFAkYVBv1gfLElIy .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-uFAkYVBv1gfLElIy .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-uFAkYVBv1gfLElIy .cluster text{fill:#333;}#mermaid-svg-uFAkYVBv1gfLElIy .cluster span{color:#333;}#mermaid-svg-uFAkYVBv1gfLElIy div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-uFAkYVBv1gfLElIy .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-uFAkYVBv1gfLElIy rect.text{fill:none;stroke-width:0;}#mermaid-svg-uFAkYVBv1gfLElIy .icon-shape,#mermaid-svg-uFAkYVBv1gfLElIy .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uFAkYVBv1gfLElIy .icon-shape p,#mermaid-svg-uFAkYVBv1gfLElIy .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-uFAkYVBv1gfLElIy .icon-shape .label rect,#mermaid-svg-uFAkYVBv1gfLElIy .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uFAkYVBv1gfLElIy .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-uFAkYVBv1gfLElIy .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-uFAkYVBv1gfLElIy :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是



开始 Apply
枚举所有文档键
执行字段重命名(如有)
是否改变哈希向量类型?
备份原始向量字节到磁盘
跳过向量备份
删除源索引(FT.DROPINDEX)
执行键前缀重命名(如有)
是否改变哈希向量类型?
逐批读取、转换、写回向量
跳过向量量化
创建目标索引(FT.CREATE)
等待后台重建完成
验证结果
生成迁移报告

详细说明:

  1. 枚举键 (修改前):使用 FT.AGGREGATE WITHCURSOR 高效枚举所有文档键;若索引有失败记录则回退到 SCAN
  2. 字段重命名 :若计划中有 rename_fields,则在删除原索引前执行文档字段的重命名(Hash 用管道 HGET/HSET/HDEL,JSON 用路径更新)。
  3. 备份原始向量 (仅当哈希向量类型变更时):将原始向量字节写入 <backup-dir>,用于崩溃恢复和回滚。
  4. 删除源索引 :执行 FT.DROPINDEX仅删除索引结构,底层文档不受影响。此后索引暂时不可用。
  5. 键前缀重命名 (如有前缀变更):执行 RENAME 或(集群环境)DUMP/RESTORE
  6. 量化向量(仅当哈希向量类型变更时):对每个文档读取旧向量,转换数据类型(如 float32→float16),写回原文档。批量处理(默认 500 条/批)。
  7. 创建目标索引 :用 merged_target_schema 执行 FT.CREATE,Redis 开始后台索引已有文档。
  8. 等待索引就绪 :轮询 FT.INFO 直到索引完成重建,此时索引可正常查询。

停机要求(关键!)

drop_recreate 迁移模式下,索引会暂时不可用。您的应用必须:

  • 暂停所有读操作:因为索引被删除期间任何查询都会失败。
  • 暂停所有写操作:因为写入的文档不会被索引(索引已删除);此外,若正在量化向量,并发写入会与迁移冲突。
停机类型 读操作 写操作 是否安全
完全静默(推荐) 停止 停止 ✅ 安全
只读暂停 停止 继续 ❌ 不安全
活动状态 继续 继续 ❌ 不安全

恢复建议 :若迁移中断,重新执行同一命令即可自动从中断点续传(要求使用 --backup-dir)。


向量量化的备份与崩溃安全恢复

当您将哈希向量从高精度类型(如 float32)转换为低精度类型(如 float16 或 int8)时,迁移器会在修改前将原始向量保存到磁盘,以实现崩溃安全恢复和手动回滚。

备份文件结构

复制代码
<backup-dir>/
  migration_backup_<index_name>.header   # JSON: 进度状态、批次计数、字段元数据
  migration_backup_<index_name>.data     # 二进制: 按批次存储的原始向量(长度前缀+pickle)
  migration_backup_<index_name>.manifest # JSON: 多worker分片恢复元数据(workers>1时)
  • 磁盘占用 ≈ 文档数 × 向量维度 × 每个元素字节数
    例如:100万文档 × 768维 × float32(4字节) ≈ 2.9 GB

状态机与续传

.header 文件记录了一个状态机,原子更新每个批次后写入:

复制代码
dump → ready → index_dropped → active → completed → target_created → validated
  • dump:正在读取并备份原始向量
  • ready:备份完成,原索引仍在线
  • index_dropped:原索引已删,但向量尚未全部转换
  • active:正在逐批转换向量
  • completed:所有向量转换完成,等待创建目标索引
  • target_created:目标索引已创建,正在重建或已就绪
  • validated:迁移验证通过

若进程崩溃,重新运行相同命令,迁移器会读取 header,跳过已完成批次,从下一个未完成批次继续。

手动回滚

如需撤销量化迁移并恢复原始向量:

bash 复制代码
rvl migrate rollback --backup-dir /tmp/backups --url redis://localhost:6379

该命令会将备份中的原始向量写回 Redis,但不会恢复索引定义,您需要手动重建原索引。


支持与阻塞的变更一览

变更类型 是否支持 说明
添加 text/tag/numeric/geo 字段 ✅ 支持
移除字段 ✅ 支持
重命名字段 ✅ 支持 同时更新所有文档
更改键前缀 ✅ 支持 执行 RENAME
重命名索引 ✅ 支持 仅索引级别
字段设为可排序 ✅ 支持
更改字段选项(分隔符、词干提取等) ✅ 支持
更改向量算法(FLAT↔HNSW↔SVS-VAMANA) ✅ 支持 仅索引,无需改数据
更改距离度量(COSINE↔L2↔IP) ✅ 支持 仅索引
调整 HNSW 参数(M、EF_CONSTRUCTION) ✅ 支持 仅索引
量化向量(float32→float16/bfloat16/int8/uint8) ✅ 支持 自动重新编码;若同一键被其他索引共享则不支持
更改向量维度 ❌ 阻塞 需要重新嵌入,应使用新模型重新生成数据再迁移
更改存储类型(hash ↔ json) ❌ 阻塞 数据格式不同,需导出转换后重新加载
添加新向量字段 ❌ 阻塞 要求所有文档已有向量,应先填充向量再迁移

单索引迁移 CLI 参考

命令 描述
rvl migrate wizard 交互式构建迁移(推荐)
rvl migrate plan 根据 patch 或目标 schema 生成计划
rvl migrate apply 执行迁移
rvl migrate estimate 估算迁移所需磁盘空间(干运行)
rvl migrate validate 验证迁移结果
rvl migrate rollback 回滚向量数据(需备份目录)

常用参数

  • --url:Redis 连接 URL
  • --index:待迁移索引名
  • --plan / --plan-out:计划文件路径
  • --backup-dir必需的备份目录,用于存放恢复数据
  • --batch-size:每批处理的键数(默认 500)
  • --workers:并行量化工作线程数(默认 1)
  • --async:使用异步执行(适用于大规模迁移)
  • --report-out:输出验证报告
  • --benchmark-out:输出性能指标

批量迁移:一次 patch 应用于多个索引

当需要为多个索引应用相同变更(如统一量化所有索引的向量)时,使用批量迁移。

快速开始

bash 复制代码
# 1. 创建共享 patch
cat > quantize_patch.yaml << 'EOF'
version: 1
changes:
  update_fields:
    - name: embedding
      attrs:
        datatype: float16
EOF

# 2. 生成批量计划(匹配所有后缀为 _idx 的索引)
rvl migrate batch-plan \
  --pattern "*_idx" \
  --schema-patch quantize_patch.yaml \
  --plan-out batch_plan.yaml \
  --url redis://localhost:6379

# 3. 执行批量迁移
rvl migrate batch-apply \
  --plan batch_plan.yaml \
  --backup-dir ./migration_backups \
  --accept-data-loss \
  --url redis://localhost:6379

# 4. 查看状态
rvl migrate batch-status --state batch_state.yaml

批量计划生成逻辑

批量计划器接受一个共享 patch,对每个目标索引测试其适用性:

  • 若 patch 可应用(如字段存在、变更支持),则为该索引生成独立的 MigrationPlan 并标记 applicable: true
  • 若 patch 不适用(如字段缺失),标记 applicable: false 并记录 skip_reason,跳过该索引
  • 前缀冲突检查:若两个索引的键前缀有重叠(一个为另一个的前缀),批量计划器将拒绝生成计划,以避免同一键被多次量化导致数据损坏。需将冲突索引分组分别处理。

批量执行与恢复

  • batch-apply 按顺序逐个执行索引迁移,进度保存在 batch_state.yaml
  • 若中途中断,使用 batch-resume 继续:rvl migrate batch-resume --state batch_state.yaml
  • 失败策略(fail_fastcontinue_on_error)在 batch-plan 时设置

Python API 示例

同步单索引迁移

python 复制代码
from redisvl.migration import MigrationPlanner, MigrationExecutor

planner = MigrationPlanner()
plan = planner.create_plan(
    "myindex",
    redis_url="redis://localhost:6379",
    schema_patch_path="schema_patch.yaml",
)

executor = MigrationExecutor()
report = executor.apply(
    plan,
    redis_url="redis://localhost:6379",
    backup_dir="/tmp/migration_backups",
    batch_size=500,
    num_workers=4,
)
print(f"迁移结果: {report.result}")

异步 API

python 复制代码
import asyncio
from redisvl.migration import AsyncMigrationPlanner, AsyncMigrationExecutor

async def migrate():
    planner = AsyncMigrationPlanner()
    plan = await planner.create_plan(...)
    executor = AsyncMigrationExecutor()
    report = await executor.apply(...)
    print(report.result)

asyncio.run(migrate())

批量迁移 Python API

python 复制代码
from redisvl.migration import BatchMigrationPlanner, BatchMigrationExecutor

planner = BatchMigrationPlanner()
batch_plan = planner.create_batch_plan(
    redis_url="redis://localhost:6379",
    pattern="*_idx",
    schema_patch_path="quantize_patch.yaml",
)

executor = BatchMigrationExecutor()
report = executor.apply(
    batch_plan,
    redis_url="redis://localhost:6379",
    backup_dir="/tmp/migration_backups",
    state_path="batch_state.yaml",
)
print(f"成功: {report.summary.successful}/{report.summary.total_indexes}")

性能调优建议

批次大小(--batch-size

  • 默认 500 是良好平衡点
  • 增大到 1000+ 可减少网络往返,但增加单次内存占用和延迟
  • 视网络和 Redis 性能可调整到 200~1000 之间

并行工作线程(--workers

  • 用于向量量化阶段,每个 worker 独立连接 Redis
  • 增加 worker 可加速量化,但会提高 Redis 连接数和 CPU 负载
  • 建议从 2~4 开始测试

备份磁盘空间估算

使用估计命令提前计算:

bash 复制代码
rvl migrate estimate --plan migration_plan.yaml

若开启 AOF,加上 --aof-enabled 以获得更精确的磁盘需求。


常见问题排查

问题 可能原因 解决方案
计划生成失败,提示"unsupported change" 变更需要数据转换(如改变维度) 按提示调整策略,或采用应用层迁移
Apply 失败:"source schema mismatch" 线上索引在计划生成后发生了改变 重新生成计划
Apply 超时:"timeout waiting for index ready" 数据量大,重建慢 增加超时时间或在低峰期执行
验证失败:"document count mismatch" 计划生成后有文档增删 应用写操作暂停期间重新生成计划并执行
量化后另一索引中的文档消失 共享键导致类型冲突(不支持) 回滚向量,使用应用层迁移创建新键或新字段,协调所有索引
批量计划生成报错"overlapping indexes" 两个索引前缀重叠 将冲突索引分开到不同批次,或手动处理
恢复中断时提示备份不匹配 备份目录与当前计划不一致 删除旧备份目录,重新开始

总结

RedisVL 的索引迁移器提供了一套安全、可续传、可回滚的机制来演进您的索引结构。核心要点:

  1. 三阶段流程:Patch → Plan → Apply
  2. 必须停机:迁移期间索引不可用,需暂停读写
  3. 备份必备--backup-dir 为所有迁移强制要求,确保可恢复
  4. 向量量化:自动备份原始向量,支持崩溃续传和手动回滚
  5. 批量处理:同一 patch 可应用于多个索引,但需注意前缀冲突

在生产环境操作前,建议先在测试索引上演练,确认计划无误后再应用。迁移计划中的 diff_classification.supportedwarnings 字段是决策的关键依据。

相关推荐
zandy10111 小时前
衡石 Agentic BI的ReAct 推理框架在 Agentic BI 中的工程化实践
前端·javascript·react.js
罗超驿2 小时前
JavaEE进阶之路:从Web架构原理到HTML标签全解析
前端·html·web·javaee
西安小哥2 小时前
从前端到AI工程师:一场跨越鸿沟的真实蜕变之旅
前端
Prince4182 小时前
侧边栏收起缩放适配方案
前端
用户7783366132112 小时前
serpbase + Cloudflare R2 边缘持久化实战
前端·人工智能
এ慕ོ冬℘゜2 小时前
jQuery attr() 方法超详细讲解:属性获取、赋值、实战踩坑全解
前端·javascript·jquery
东方小月2 小时前
从零开发一个 Coding Agent(三):EventStream 事件流通道设计与实现
前端·人工智能·后端
Oo大司命oO2 小时前
藏在正则表达式里的陷阱
数据库·mysql·正则表达式
_oP_i3 小时前
mysql统计数据库使用存储大小
数据库
程序员黑豆3 小时前
鸿蒙应用开发:Refresh + List 下拉刷新组件使用教程
前端·华为·harmonyos