news 2026/9/12 3:28:30

如何配置 Dragonfly 把 HNSW 向量索引复制到副本(序列化与重建两条路径)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
如何配置 Dragonfly 把 HNSW 向量索引复制到副本(序列化与重建两条路径)

如何配置 Dragonfly 把 HNSW 向量索引复制到副本(序列化与重建两条路径)

【免费下载链接】dragonflyA modern replacement for Redis and Memcached项目地址: https://gitcode.com/GitHub_Trending/dr/dragonfly

在 Dragonfly 里用FT.CREATE ... VECTOR HNSW建了向量索引之后,如果给主节点挂副本,副本上的 HNSW 图不会“自动变出来”:HNSW 图是每个(index, field)一张、跨所有 shard 共享的全局结构,而全同步 RDB 流是逐 shard 发送的,副本侧必须把“图”和“各 shard 的键空间”两块状态重新拼起来。Dragonfly 为此提供了两条互斥的落地路径:

  • 序列化路径:主节点把图结构序列化成 RDB opcode 随全同步流出,副本直接恢复(shard 数不同时还会重映射GlobalDocId);
  • 重建路径:副本跳过图数据,仅凭键流把索引从头重建一遍。

下面按“建索引 → 配副本 → 验证”的顺序给出两条路径的完整配置和核对方法。机制细节以 docs/hnsw-index-replication.md 为准,复制协议以 docs/replication.md 为准。

先弄清两条路径由什么条件决定

复制线路上涉及三类数据,理解路径选择前先看它们各自的门槛:

数据载体门槛
索引定义(FT.CREATE参数)AUX 字段search-index无条件写入 summary flow;副本幂等地执行FT.CREATE
图结构(节点、层级、邻居链接表)RDB_OPCODE_VECTOR_INDEX(opcode 222),仅 shard 0 发送、仅非空图发送主节点--serialize_hnsw_index+ 副本能力VER6
各 shard 的 key→DocId 映射RDB_OPCODE_SHARD_DOC_INDEX(opcode 223),每个 shard 发送同上

路径判定(依据 docs/hnsw-index-replication.md §4、§6、§7):

条件副本实际走的路径
主节点--serialize_hnsw_index关闭(默认即关闭)重建:跳过步骤 2、3,副本从键流重建所有索引
主节点开启,但副本--deserialize_hnsw_index关闭重建:副本到达时直接跳过两个 opcode
双方都开启,且副本握手时上报的能力达到VER6序列化恢复
副本能力低于VER6重建:副本只通过 summary flow 收到FT.CREATE定义,索引完全靠键流重建
序列化开启但主/副本 shard 数不一致,且重映射失败或该索引没有收到映射该索引回退到重建

两个 flag 都默认false(见 snapshot.cc 与 rdb_load.cc 中的 flag 定义),所以不显式配置时走的就是重建路径。序列化路径必须主、副本两侧同时打开。

第一步:启动主节点并创建 HNSW 索引

按 docs/quick-start/README.md 的 Docker 方式起一个实例,并加上序列化 flag(--serialize_hnsw_index=true)。Linux 与 macOS 的网络参数不同,下面用带端口映射的写法,两种系统都适用;两个实例各占一个宿主机端口:

# 主节点(宿主机端口 6379) docker run -p 6379:6379 --ulimit memlock=-1 \ docker.dragonflydb.io/dragonflydb/dragonfly --serialize_hnsw_index=true

连上主节点,建一个带 HNSW 向量字段的索引,再写入文档。以下命令取自仓库测试 search_test.py 中test_replicate_all_index_types的真实用例:

FT.CREATE all_types_idx ON HASH PREFIX 1 item: \ SCHEMA name TEXT price NUMERIC SORTABLE category TAG location GEO \ embedding VECTOR HNSW 6 TYPE FLOAT32 DIM 2 DISTANCE_METRIC L2
# embedding 字段的值是 DIM 个 float32 的原始字节(本例 DIM=2,即 8 字节)。 # 仓库测试用 numpy 生成:np.array([x, y], dtype=np.float32).tobytes() HSET item:0 name "Product 0" price 0 category electronics location "-122.0,37.0" embedding <2 个 float32 的原始字节>

两个占位符需要你自己替换:<2 个 float32 的原始字节>是向量原始字节,由客户端侧用 numpy 之类的库生成后写入。注意一个明确限制:单个索引目前不支持多个 HNSW 向量字段(见 hnsw-index-replication.md 的 Open Issues)。

第二步:启动副本并挂到主节点

路径 A:序列化恢复(主路径)

副本启动参数加--deserialize_hnsw_index=true,它镜像主节点 flag 的作用:关闭时副本对两个 opcode 一律跳过:

# 副本(宿主机端口 6380,容器内仍是 6379) docker run -p 6380:6379 --ulimit memlock=-1 \ docker.dragonflydb.io/dragonflydb/dragonfly --deserialize_hnsw_index=true

然后在副本上挂主。仓库测试用的是运行期命令,等价于通过 redis-cli 执行:

redis-cli -h <副本地址> -p 6380 REPLICAOF <主节点地址> 6379

测试中也有用启动参数replicaof=localhost:<主节点端口>在副本启动时就挂主节点的写法,两种方式等价,任选其一即可。

路径 B:从键流重建(替代路径)

如果不想传图(比如索引很小、或想规避 shard 数不一致带来的重映射),就不开 flag:副本只带--replicaof或执行REPLICAOF,主节点也可以不开--serialize_hnsw_index。副本收完键流后会自己把整个 HNSW 索引建出来。

关于 shard 数:副本与主节点 shard 数相同时,映射按 shard id 原位恢复;不一致时副本会先建重映射表、改写图里所有global_id再恢复(因为GlobalDocId高 32 位编码的是主节点的 shard id)。某索引无法完整重映射时,该索引的图被丢弃、回退重建——这是文档明确给出的行为,不是需要规避的错误。

第三步:验证副本上的索引与 KNN 结果

按下面的顺序核对,每一步的判断标准都来自仓库文档与测试:

  1. 确认复制进入稳定状态。主节点INFO REPLICATION会把会话状态报成preparationfull_syncstable_sync之一;副本侧INFO的相位会经历FULL_SYNC_IN_PROGRESS/INITIAL_SYNC再到STABLE_SYNC(见 replication.md 的 Observability 一节)。等到主节点显示stable_sync再继续。

  2. 确认索引定义已落到副本。在副本上执行:

    FT._LIST

    返回里应包含all_types_idx(测试即以此判断索引存在)。

  3. 用同一条 KNN 查询对比主、副本结果。测试用的查询形式($vec传入 2 个 float32 的原始字节,如[50.0, 50.0]):

    FT.SEARCH all_types_idx "*=>[KNN 10 @embedding $vec]" PARAMS 2 vec <查询向量原始字节>

    STABLE_SYNC后对主、副本各跑一次。判断标准参照仓库测试:副本应返回同样数量的结果(KNN 10 对应计数 10),且两侧结果键集合一致。测试特意把两侧键排序后再比较,因为浮点距离并列时返回顺序可能有细微差异——所以核对时应比较键集合,而不是逐位比返回顺序。

  4. (可选)确认副本实际走了哪条路径。副本日志里有明确的路径标记,仓库测试正是靠这几个字符串断言的(示例字符串,非固定预期日志):

    • Restored HNSW index—— 同 shard 数下从序列化图恢复;
    • global_ids remapped—— 不同 shard 数下经重映射恢复;
    • Will rebuild from scratch(伴随 HNSW 字样)—— 走了重建路径。

    测试里用logbuflevel=-1强制 glog 每行立即刷盘,这样在进程日志文件里才能读到 INFO 行;如果你也要从日志文件核对,副本加这个启动参数。

边界与限制

  • 空图不传输:主节点不发送空索引的图块,副本从(空的)键流直接重建,属正常行为。
  • 磁盘 RDB 不含索引数据search-index数据只出现在复制用的 RDB 流里,RDB 保存到磁盘时会整体省略——向量索引是“复制态”的概念,不要把“RDB 文件里没有索引”当成数据丢失。
  • 重建路径的语义差异:全重建时,加载期间缓冲的操作会被丢弃而不是回放(重建本来就会从键流重索引所有文档);而序列化恢复路径下这些操作会在全部分片完成水合后统一 drain 回放。这解释了为什么两条路径的加载耗时特征不同:重建时间与文档量成正比。
  • 版本门槛:低于VER6能力的副本只收到FT.CREATE定义,无论 flag 如何配置都走重建。
  • 更底层的线格式(HnswNodeData结构、MRMW 锁、借用向量不变量、状态机kProhibit/kRestoring/kBuilding/kSerializing)都在 hnsw-index-replication.md 中,日常配置不需要接触;复制握手、全同步/部分同步的完整机制见 replication.md。

【免费下载链接】dragonflyA modern replacement for Redis and Memcached项目地址: https://gitcode.com/GitHub_Trending/dr/dragonfly

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 3:27:56

TI EV2300 USB驱动在Windows XP下的安装与通信原理

简介&#xff1a;本资源是专为Windows XP/2000系统设计的TI EV2300 USB通信驱动安装包&#xff0c;面向嵌入式开发工程师、工业控制调试人员及高校电子类课程实践者&#xff0c;解决EV2300微控制器在老旧Windows平台下无法识别、无法烧录与调试的核心兼容性问题。压缩包共33个文…

作者头像 李华
网站建设 2026/9/12 3:27:31

免费升级老Mac装最新macOS:OCLP完整操作指南

免费升级老Mac装最新macOS&#xff1a;OCLP完整操作指南 【免费下载链接】OpenCore-Legacy-Patcher Experience macOS just like before 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher OpenCore Legacy Patcher&#xff08;简称 OCLP&…

作者头像 李华
网站建设 2026/9/12 3:27:21

Pytest Fixtures:自动化测试的依赖注入与资源管理利器

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 3:27:18

Rust Axum中间件实战:JWT身份验证与类型安全提取器详解

Rust的异步Web生态里&#xff0c;Axum已经成了我新项目的默认选择。它由tokio团队维护&#xff0c;底层基于hyper和tower&#xff0c;整个链路非常干净&#xff0c;最大的优势就是类型系统表达力强——很多别的语言要靠运行时判断的事&#xff0c;在Rust这里编译期就能堵住。今…

作者头像 李华
网站建设 2026/9/12 3:26:49

基于OpenCV的普通摄像头瞳孔跟踪:从Haar级联到椭圆拟合的实现指南

简介&#xff1a;一套基于OpenCV与网络摄像头的瞳孔跟踪算法项目&#xff0c;面向计算机视觉入门与进阶学习者&#xff0c;解决实时捕捉、分析人眼瞳孔运动以获取注意力或生理反馈的需求。项目包内含14个文件&#xff0c;包括4个C源文件与4个头文件构成完整算法实现&#xff0c…

作者头像 李华