橡皮、老虎皮、狮子皮哪一个最不好?

锚点对齐:解决多模态推荐系统中的位置坍缩问题

多模态推荐系统正在面临一个隐藏的危机。

当系统试图将图像、文本、用户行为等不同模态的数据对齐到同一个向量空间时,一个微妙但致命的问题出现了:位置坍缩(Positional Collapse)。模态特有的结构信息被抹平,推荐质量悄然下降。

一篇最新的 arXiv 论文提出了一个优雅的解决方案:锚点对齐(Anchored Alignment)。

多模态推荐的困境

现代推荐系统早已不满足于单一的交互数据。商品图片、标题描述、用户评论——这些多模态信息理应让推荐更精准。

但传统的对齐方法有一个副作用:

强制对齐 = 信息损失

当你把图像特征和文本特征强行投影到同一个空间时:

  • 图像的空间结构信息被稀释
  • 文本的语义层次被压缩
  • 最糟糕的是,ID 嵌入(用户/商品标识)开始主导一切

结果就是:模型记住了"用户 A 喜欢商品 B",却忘记了"为什么喜欢"。

什么是位置坍缩?

想象一个三维空间:

  • X 轴代表图像特征(颜色、形状、纹理)
  • Y 轴代表文本特征(主题、情感、关键词)
  • Z 轴代表 ID 特征(用户偏好、商品属性)

强制对齐的过程,就像把这个三维空间压扁成二维平面。不同模态的信息被迫"挤"在一起,失去了原有的结构关系。

论文作者称之为"位置坍缩"——模态在嵌入空间中的相对位置失去了意义。

AnchorRec:解耦对齐与表示学习

AnchorRec 的核心洞察是:对齐和表示学习不应该在同一个空间进行

传统方法:

图像特征 → 统一空间 ← 文本特征
    ↓              ↓
   对齐          对齐
    ↓              ↓
  混合表示  →  推荐预测

AnchorRec 的方法:

图像特征 → 投影空间 ← 文本特征
    ↓              ↓
   锚点对齐(轻量级)
    ↓              ↓
  保持原生结构 → 多模态融合 → 推荐预测

关键区别在于:

  1. 原生结构保留:每个模态在自己的空间中学习表示
  2. 间接对齐:通过轻量级投影空间进行锚点对齐
  3. 解耦设计:对齐不干扰表示学习

锚点机制的工作原理

锚点对齐的核心是引入一组"锚点"(Anchors)作为中介:

  1. 锚点定义:在投影空间中定义一组可学习的锚点向量
  2. 模态映射:每个模态学习如何将自身特征映射到锚点
  3. 对齐约束:不同模态对同一锚点的映射应该一致

这种设计的巧妙之处在于:

  • 锚点充当了"翻译官",让不同模态能够"对话"
  • 但对话发生在投影空间,不影响各自的原生表示
  • 对齐是间接的、轻量级的,不会压倒模态特有的信息

实验结果解读

论文在四个 Amazon 数据集上进行了实验,结果值得关注:

推荐准确性

  • AnchorRec 达到了与 SOTA 方法相当的 top-N 推荐准确率
  • 证明了解耦对齐不会牺牲性能

多模态表达能力

  • 定性分析显示更好的多模态一致性
  • 模态间的语义关系更加清晰

关键优势

  • 避免了 ID 主导的问题
  • 保留了模态特有的结构信息
  • 计算开销更小(轻量级投影)

对搜索与推荐的启示

AnchorRec 的设计哲学对搜索和推荐系统有广泛借鉴意义:

1. 对齐不是目的,是手段

很多系统为了追求"统一嵌入空间",牺牲了对齐前的信息丰富度。AnchorRec 提醒我们:对齐是为了让模态能够协作,而不是让它们变得一样。

2. 解耦是复杂系统的生存之道

将表示学习和对齐解耦,让每个模块专注于自己的任务。这种设计在复杂系统中往往比端到端训练更稳健。

3. 轻量级投影的价值

不需要复杂的转换网络,简单的投影层就能实现有效的跨模态对齐。这降低了计算成本,也减少了过拟合风险。

局限与思考

AnchorRec 并非万能药:

  • 锚点数量的选择:需要针对具体任务调优
  • 投影空间的设计:如何定义最优的锚点分布仍是一个开放问题
  • 动态适应性:对于模态分布随时间变化的场景,锚点可能需要动态更新

但对于电商推荐、内容发现等经典场景,AnchorRec 提供了一个值得尝试的新范式。

结语

多模态推荐系统的未来,可能不在于如何把不同模态"揉"在一起,而在于如何让它们保持独立的同时有效协作。

AnchorRec 的锚点对齐,正是这种思路的一个优雅实现。


在信息融合的世界里,最大的挑战不是连接,而是如何在连接中保持各自的独特性。

当我们学会让图像保持视觉的结构、让文本保持语义的层次,同时又能让它们相互对话,推荐系统才能真正理解"为什么推荐"。


论文: arXiv:2603.12726
标题: Anchored Alignment: Preventing Positional Collapse in Multimodal Recommender Systems
代码: GitHub
关键词: 多模态推荐、锚点对齐、位置坍缩、表示学习

继续阅读 »

多模态推荐系统正在面临一个隐藏的危机。

当系统试图将图像、文本、用户行为等不同模态的数据对齐到同一个向量空间时,一个微妙但致命的问题出现了:位置坍缩(Positional Collapse)。模态特有的结构信息被抹平,推荐质量悄然下降。

一篇最新的 arXiv 论文提出了一个优雅的解决方案:锚点对齐(Anchored Alignment)。

多模态推荐的困境

现代推荐系统早已不满足于单一的交互数据。商品图片、标题描述、用户评论——这些多模态信息理应让推荐更精准。

但传统的对齐方法有一个副作用:

强制对齐 = 信息损失

当你把图像特征和文本特征强行投影到同一个空间时:

  • 图像的空间结构信息被稀释
  • 文本的语义层次被压缩
  • 最糟糕的是,ID 嵌入(用户/商品标识)开始主导一切

结果就是:模型记住了"用户 A 喜欢商品 B",却忘记了"为什么喜欢"。

什么是位置坍缩?

想象一个三维空间:

  • X 轴代表图像特征(颜色、形状、纹理)
  • Y 轴代表文本特征(主题、情感、关键词)
  • Z 轴代表 ID 特征(用户偏好、商品属性)

强制对齐的过程,就像把这个三维空间压扁成二维平面。不同模态的信息被迫"挤"在一起,失去了原有的结构关系。

论文作者称之为"位置坍缩"——模态在嵌入空间中的相对位置失去了意义。

AnchorRec:解耦对齐与表示学习

AnchorRec 的核心洞察是:对齐和表示学习不应该在同一个空间进行

传统方法:

图像特征 → 统一空间 ← 文本特征
    ↓              ↓
   对齐          对齐
    ↓              ↓
  混合表示  →  推荐预测

AnchorRec 的方法:

图像特征 → 投影空间 ← 文本特征
    ↓              ↓
   锚点对齐(轻量级)
    ↓              ↓
  保持原生结构 → 多模态融合 → 推荐预测

关键区别在于:

  1. 原生结构保留:每个模态在自己的空间中学习表示
  2. 间接对齐:通过轻量级投影空间进行锚点对齐
  3. 解耦设计:对齐不干扰表示学习

锚点机制的工作原理

锚点对齐的核心是引入一组"锚点"(Anchors)作为中介:

  1. 锚点定义:在投影空间中定义一组可学习的锚点向量
  2. 模态映射:每个模态学习如何将自身特征映射到锚点
  3. 对齐约束:不同模态对同一锚点的映射应该一致

这种设计的巧妙之处在于:

  • 锚点充当了"翻译官",让不同模态能够"对话"
  • 但对话发生在投影空间,不影响各自的原生表示
  • 对齐是间接的、轻量级的,不会压倒模态特有的信息

实验结果解读

论文在四个 Amazon 数据集上进行了实验,结果值得关注:

推荐准确性

  • AnchorRec 达到了与 SOTA 方法相当的 top-N 推荐准确率
  • 证明了解耦对齐不会牺牲性能

多模态表达能力

  • 定性分析显示更好的多模态一致性
  • 模态间的语义关系更加清晰

关键优势

  • 避免了 ID 主导的问题
  • 保留了模态特有的结构信息
  • 计算开销更小(轻量级投影)

对搜索与推荐的启示

AnchorRec 的设计哲学对搜索和推荐系统有广泛借鉴意义:

1. 对齐不是目的,是手段

很多系统为了追求"统一嵌入空间",牺牲了对齐前的信息丰富度。AnchorRec 提醒我们:对齐是为了让模态能够协作,而不是让它们变得一样。

2. 解耦是复杂系统的生存之道

将表示学习和对齐解耦,让每个模块专注于自己的任务。这种设计在复杂系统中往往比端到端训练更稳健。

3. 轻量级投影的价值

不需要复杂的转换网络,简单的投影层就能实现有效的跨模态对齐。这降低了计算成本,也减少了过拟合风险。

局限与思考

AnchorRec 并非万能药:

  • 锚点数量的选择:需要针对具体任务调优
  • 投影空间的设计:如何定义最优的锚点分布仍是一个开放问题
  • 动态适应性:对于模态分布随时间变化的场景,锚点可能需要动态更新

但对于电商推荐、内容发现等经典场景,AnchorRec 提供了一个值得尝试的新范式。

结语

多模态推荐系统的未来,可能不在于如何把不同模态"揉"在一起,而在于如何让它们保持独立的同时有效协作。

AnchorRec 的锚点对齐,正是这种思路的一个优雅实现。


在信息融合的世界里,最大的挑战不是连接,而是如何在连接中保持各自的独特性。

当我们学会让图像保持视觉的结构、让文本保持语义的层次,同时又能让它们相互对话,推荐系统才能真正理解"为什么推荐"。


论文: arXiv:2603.12726
标题: Anchored Alignment: Preventing Positional Collapse in Multimodal Recommender Systems
代码: GitHub
关键词: 多模态推荐、锚点对齐、位置坍缩、表示学习

收起阅读 »

Pinecone 的删除工程:如何安全清理数十亿向量对象

在分布式系统中,删除数据比写入数据更难。

这听起来反直觉,但 Pinecone 的工程团队用一篇技术博客揭示了一个残酷现实:当你每天处理数十亿个向量对象时,"垃圾回收"会成为基础设施账单上最大的开销之一。

他们把这个系统命名为 Janitor(清洁工)。

不可变存储的双刃剑

Pinecone 的数据平面基于不可变对象存储(immutable blob storage)。这个设计选择带来了显著的好处:

  • 写入路径简单:每次写入都生成新文件,而非修改现有文件
  • 天然支持版本控制:历史数据自动保留
  • 崩溃恢复容易:没有部分写入的状态需要处理

但代价同样明显:旧版本数据永远不会自动消失

每次向量更新都会产生一个新文件,旧文件变成"垃圾"。更糟糕的是,如果写入节点在提交元数据前崩溃,文件会变成"孤儿"——存在于存储中,但对系统完全不可见。

这就是 Pinecone 团队所说的 "删除税"(deletion tax):你不再需要的数据,变成了无法停止支付的成本。

为什么删除这么难?

在单节点数据库中,删除很简单:检查引用计数,归零就删。但在分布式系统中,删除是一条漫长的链条:

写入节点 → 元数据服务 → 缓存层 → 读取节点 → 对象存储

每个环节都有自己的状态,每个环节都可能失败。最大的挑战是传播延迟:当你删除一个对象时,怎么确保所有可能引用它的地方都已经更新?

删得太早,读取节点可能还在服务来自该对象的查询;删得太晚,存储成本持续累积。

Janitor 的三模式设计

Pinecone 没有试图用单一方案解决所有删除问题,而是将删除分为三种模式,每种有独立的策略和风险 profile:

1. 正常模式(Normal Mode)

处理日常积累的过时文件——已被新版本取代、不再被元数据引用的对象。

  • 频率:每 4 小时执行分片级清理,每周执行全量扫描
  • 风险:缓存中可能仍保留对旧文件的引用
  • 策略:保守的延迟删除,确保引用过期后才执行

2. 孤儿模式(Orphan Mode)

处理更棘手的问题:系统根本不知道存在的文件。

场景:写入节点成功写入 blob,但在提交元数据前崩溃。文件存在于存储中,但没有任何元数据指向它。

  • 频率:每月全量扫描
  • 方法:从对象存储反向扫描,验证每个对象是否可达
  • 挑战:孤儿文件对系统完全不可见,必须通过反向扫描发现

3. 客户删除模式(Customer Deletion Mode)

最敏感的操作:客户主动删除索引或命名空间。

客户的期望是"数据立即且不可逆地消失",但"立即"和"不可逆"本身就是矛盾的。Pinecone 的做法是:

  • 立即从元数据中移除引用(客户视角:已删除)
  • 异步执行物理删除(系统视角:待清理)
  • 保留审计日志(合规视角:可追溯)

核心协议:识别 → 验证 → 执行

Janitor 的核心是一个三步协议:

识别(Identify):找出候选删除对象

  • 扫描元数据,找出不再被引用的文件
  • 记录对象 ID、最后访问时间、引用路径

验证(Verify):确保安全删除

  • 交叉检查缓存状态
  • 确认读取节点不再使用该对象
  • 验证对象确实不可达

执行(Execute):物理删除并审计

  • 执行删除操作
  • 记录删除日志
  • 监控存储成本变化

这个协议的关键是验证阶段。Pinecone 发现,大多数删除事故都发生在"识别"和"执行"之间缺少充分的验证。

工程权衡的艺术

Janitor 的设计体现了分布式系统工程的几个核心原则:

1. 没有完美的删除策略

只有适合特定场景的策略。正常模式、孤儿模式、客户删除模式,每种都有其适用边界。

2. 延迟是友非敌

在删除场景中,适当的延迟比过早删除更安全。Pinecone 的 4 小时清理周期,是对"存储成本"和"数据安全"的权衡。

3. 可观测性优先

Janitor 的每个操作都有详细的审计日志。不是出于合规要求,而是因为"你无法优化无法观测的东西"。

4. 成本驱动设计

Janitor 的诞生不是因为技术债务,而是因为存储成本。工程决策最终要回归商业现实。

对向量数据库的启示

Pinecone 的删除工程实践,对构建大规模向量检索系统有重要借鉴:

向量数据的特殊性

  • 高维向量占用存储大(768 维 × 4 字节 = 3KB/向量)
  • 更新频繁(RAG 场景下文档持续更新)
  • 索引结构复杂(HNSW、IVF 等需要重建)

这些因素使得向量数据的"删除税"比普通数据库更高。

工程建议

  1. 设计时考虑删除:不要事后补救,删除策略应该在架构设计阶段就确定
  2. 分离逻辑删除和物理删除:给客户"立即删除"的体验,给系统"延迟清理"的空间
  3. 投资可观测性:删除操作的审计日志,是排查数据丢失问题的关键
  4. 定期评估存储成本:垃圾数据是沉默的成本杀手

结语

Pinecone 的 Janitor 系统告诉我们:在分布式系统中,最简单的操作往往最复杂。

写入数据是原子操作,删除数据是协调问题。当我们设计系统时,很容易关注"如何存储数据",而忽视"如何安全地删除数据"。

但正如 Pinecone 团队所发现的:你不再需要的那些数据,最终会成为你无法忽视的成本。


在数据基础设施中,删除不是功能的缺失,而是设计的体现。

当系统能够优雅地处理数据的消亡,它才真正具备了承载数据生命周期的能力。


来源: Pinecone Engineering Blog (March 5, 2026)
标题: Garbage Day: How Pinecone Safely Deletes Billions of Objects at Scale
技术要点: 不可变存储、垃圾回收、分布式删除、成本优化

继续阅读 »

在分布式系统中,删除数据比写入数据更难。

这听起来反直觉,但 Pinecone 的工程团队用一篇技术博客揭示了一个残酷现实:当你每天处理数十亿个向量对象时,"垃圾回收"会成为基础设施账单上最大的开销之一。

他们把这个系统命名为 Janitor(清洁工)。

不可变存储的双刃剑

Pinecone 的数据平面基于不可变对象存储(immutable blob storage)。这个设计选择带来了显著的好处:

  • 写入路径简单:每次写入都生成新文件,而非修改现有文件
  • 天然支持版本控制:历史数据自动保留
  • 崩溃恢复容易:没有部分写入的状态需要处理

但代价同样明显:旧版本数据永远不会自动消失

每次向量更新都会产生一个新文件,旧文件变成"垃圾"。更糟糕的是,如果写入节点在提交元数据前崩溃,文件会变成"孤儿"——存在于存储中,但对系统完全不可见。

这就是 Pinecone 团队所说的 "删除税"(deletion tax):你不再需要的数据,变成了无法停止支付的成本。

为什么删除这么难?

在单节点数据库中,删除很简单:检查引用计数,归零就删。但在分布式系统中,删除是一条漫长的链条:

写入节点 → 元数据服务 → 缓存层 → 读取节点 → 对象存储

每个环节都有自己的状态,每个环节都可能失败。最大的挑战是传播延迟:当你删除一个对象时,怎么确保所有可能引用它的地方都已经更新?

删得太早,读取节点可能还在服务来自该对象的查询;删得太晚,存储成本持续累积。

Janitor 的三模式设计

Pinecone 没有试图用单一方案解决所有删除问题,而是将删除分为三种模式,每种有独立的策略和风险 profile:

1. 正常模式(Normal Mode)

处理日常积累的过时文件——已被新版本取代、不再被元数据引用的对象。

  • 频率:每 4 小时执行分片级清理,每周执行全量扫描
  • 风险:缓存中可能仍保留对旧文件的引用
  • 策略:保守的延迟删除,确保引用过期后才执行

2. 孤儿模式(Orphan Mode)

处理更棘手的问题:系统根本不知道存在的文件。

场景:写入节点成功写入 blob,但在提交元数据前崩溃。文件存在于存储中,但没有任何元数据指向它。

  • 频率:每月全量扫描
  • 方法:从对象存储反向扫描,验证每个对象是否可达
  • 挑战:孤儿文件对系统完全不可见,必须通过反向扫描发现

3. 客户删除模式(Customer Deletion Mode)

最敏感的操作:客户主动删除索引或命名空间。

客户的期望是"数据立即且不可逆地消失",但"立即"和"不可逆"本身就是矛盾的。Pinecone 的做法是:

  • 立即从元数据中移除引用(客户视角:已删除)
  • 异步执行物理删除(系统视角:待清理)
  • 保留审计日志(合规视角:可追溯)

核心协议:识别 → 验证 → 执行

Janitor 的核心是一个三步协议:

识别(Identify):找出候选删除对象

  • 扫描元数据,找出不再被引用的文件
  • 记录对象 ID、最后访问时间、引用路径

验证(Verify):确保安全删除

  • 交叉检查缓存状态
  • 确认读取节点不再使用该对象
  • 验证对象确实不可达

执行(Execute):物理删除并审计

  • 执行删除操作
  • 记录删除日志
  • 监控存储成本变化

这个协议的关键是验证阶段。Pinecone 发现,大多数删除事故都发生在"识别"和"执行"之间缺少充分的验证。

工程权衡的艺术

Janitor 的设计体现了分布式系统工程的几个核心原则:

1. 没有完美的删除策略

只有适合特定场景的策略。正常模式、孤儿模式、客户删除模式,每种都有其适用边界。

2. 延迟是友非敌

在删除场景中,适当的延迟比过早删除更安全。Pinecone 的 4 小时清理周期,是对"存储成本"和"数据安全"的权衡。

3. 可观测性优先

Janitor 的每个操作都有详细的审计日志。不是出于合规要求,而是因为"你无法优化无法观测的东西"。

4. 成本驱动设计

Janitor 的诞生不是因为技术债务,而是因为存储成本。工程决策最终要回归商业现实。

对向量数据库的启示

Pinecone 的删除工程实践,对构建大规模向量检索系统有重要借鉴:

向量数据的特殊性

  • 高维向量占用存储大(768 维 × 4 字节 = 3KB/向量)
  • 更新频繁(RAG 场景下文档持续更新)
  • 索引结构复杂(HNSW、IVF 等需要重建)

这些因素使得向量数据的"删除税"比普通数据库更高。

工程建议

  1. 设计时考虑删除:不要事后补救,删除策略应该在架构设计阶段就确定
  2. 分离逻辑删除和物理删除:给客户"立即删除"的体验,给系统"延迟清理"的空间
  3. 投资可观测性:删除操作的审计日志,是排查数据丢失问题的关键
  4. 定期评估存储成本:垃圾数据是沉默的成本杀手

结语

Pinecone 的 Janitor 系统告诉我们:在分布式系统中,最简单的操作往往最复杂。

写入数据是原子操作,删除数据是协调问题。当我们设计系统时,很容易关注"如何存储数据",而忽视"如何安全地删除数据"。

但正如 Pinecone 团队所发现的:你不再需要的那些数据,最终会成为你无法忽视的成本。


在数据基础设施中,删除不是功能的缺失,而是设计的体现。

当系统能够优雅地处理数据的消亡,它才真正具备了承载数据生命周期的能力。


来源: Pinecone Engineering Blog (March 5, 2026)
标题: Garbage Day: How Pinecone Safely Deletes Billions of Objects at Scale
技术要点: 不可变存储、垃圾回收、分布式删除、成本优化

收起阅读 »

OpenSearch 3.5 FP16 向量搜索优化:从 280ms 到 91ms 的技术突破

向量搜索的性能优化正在进入一个新的阶段。

OpenSearch 3.5 发布了一项令人瞩目的性能提升:通过 Bulk SIMD 技术,FP16 向量搜索的吞吐量提升了 310%,p99 延迟降至 91ms。这不仅仅是数字游戏,背后反映的是向量数据库在工程实现上的深层思考。

FP16 的困境:精度与性能的权衡

在向量检索场景中,FP16(半精度浮点数)是一个极具吸引力的选择:

  • 内存占用减半:相比 FP32,FP16 向量只需要一半的存储空间
  • 带宽需求降低:同样的内存带宽可以传输更多向量
  • 精度损失可控:对于 Embedding 向量,FP16 的精度通常足够

但理想很丰满,现实很骨感。OpenSearch 3.1 引入内存优化搜索时,FP16 却成了性能瓶颈——搜索速度比 FP32 慢了近一倍。

问题出在哪里?

JVM 的先天不足

OpenSearch 基于 Java 生态,而 JVM 对 FP16 的支持存在一个根本性问题:没有原生 FP16 类型

这意味着:

  1. FP16 向量必须先转换为 FP32 才能进行计算
  2. 转换过程是纯软件实现,无法利用 CPU 的硬件加速
  3. 每次距离计算都要重复这个转换,成为性能瓶颈

对于高维向量(如 768 维或 1536 维),这个开销被放大到极致。

SIMD:向量化计算的救星

OpenSearch 3.4 开始引入 SIMD(Single Instruction Multiple Data)优化,将距离计算委托给 C++ 层的 SIMD 实现。

SIMD 的核心思想很简单:

  • 一次性加载多个数据到寄存器
  • 一条指令同时处理多个数据
  • 充分利用现代 CPU 的并行计算能力

以 Faiss 库的 SIMD 实现为例,它可以同时处理 4 个维度的向量计算,通过循环展开技术大幅提升效率。

但这还不是最优解。

Bulk SIMD:从单次到批量

OpenSearch 3.5 的 Bulk SIMD 优化,解决了一个被忽视的问题:查询向量的重复加载

在传统的 SIMD 实现中:

对于每个文档向量:
    加载查询向量的前 8 个维度到寄存器
    加载文档向量的前 8 个维度到寄存器
    执行 SIMD 计算
    加载查询向量的下 8 个维度...
    ...

问题很明显:查询向量的每个维度被重复加载了 N 次(N = 文档数量)。

Bulk SIMD 的改进思路是:一次性加载查询向量,批量处理多个文档向量

加载查询向量的前 8 个维度到寄存器(一次)
对于每 4 个文档向量:
    批量加载 4 个文档向量的前 8 个维度
    执行 SIMD 计算(一次处理 4 个)
重复直到处理完所有维度

这种"查询向量复用"的策略,将内存访问模式从随机访问变为顺序访问,大幅提升了缓存命中率。

性能数据解读

OpenSearch 团队公布的性能数据值得关注:

版本 优化技术 吞吐量提升 p99 延迟
3.1 内存优化搜索 基线 ~280ms
3.4 SIMD FP16 ~150% ~150ms
3.5 Bulk SIMD 310% 91ms

从 280ms 到 91ms,延迟降低了 67%,这意味着:

  • 同样的硬件可以支撑 3 倍的并发查询
  • 或者将成本降低至原来的 1/3
  • 用户体验从"可感知延迟"变为"瞬时响应"

对搜索工程的启示

OpenSearch 的优化路径给我们几个重要启示:

1. 性能优化是渐进式的

从 3.1 到 3.5,经历了三个版本的迭代优化。每个版本解决一个具体瓶颈,最终累积成质变。

2. 跨语言优化的必要性

Java 生态的便利性不应成为性能的天花板。通过 JNI 调用 C++ SIMD 代码,是 JVM 应用突破性能瓶颈的常见模式。

3. 内存访问模式比算法更重要

Bulk SIMD 的核心改进不是算法创新,而是优化了内存访问模式。在现代 CPU 架构下,缓存友好性往往比算法复杂度更影响性能。

4. 向量化是趋势

无论是 SIMD、GPU 还是专用向量处理器,向量化计算都是向量检索的必经之路。OpenSearch 的优化只是这个趋势的一个缩影。

局限与思考

Bulk SIMD 并非万能药:

  • CPU 依赖:需要支持 AVX2/AVX-512 的现代 CPU
  • 向量维度限制:某些优化对维度有特定要求(如 768 维)
  • 实现复杂度:跨语言调用增加了维护成本

但对于大规模向量检索场景,这些代价是值得的。

未来展望

随着向量搜索成为 AI 应用的基础设施,性能优化将进入更深层次的竞争:

  • 专用硬件:GPU、TPU、向量处理器的应用
  • 量化技术:INT8、Binary 向量的工程化
  • 索引算法:HNSW、IVF 的持续优化
  • 内存架构:CXL、持久内存的利用

OpenSearch 的 FP16 优化只是这场竞赛的一个节点。对于搜索工程师而言,理解这些底层优化原理,将有助于在架构设计中做出更明智的权衡。


在 AI 时代,向量搜索的性能不再是"锦上添花",而是决定产品体验的核心竞争力。

当延迟从 280ms 降到 91ms,用户感受到的不是数字变化,而是"流畅"与"卡顿"的本质区别。


来源: OpenSearch Blog (March 3, 2026)
标题: Accelerating FP16 vector search performance using bulk SIMD in OpenSearch 3.5
技术要点: SIMD, Bulk SIMD, FP16, 向量搜索性能优化

继续阅读 »

向量搜索的性能优化正在进入一个新的阶段。

OpenSearch 3.5 发布了一项令人瞩目的性能提升:通过 Bulk SIMD 技术,FP16 向量搜索的吞吐量提升了 310%,p99 延迟降至 91ms。这不仅仅是数字游戏,背后反映的是向量数据库在工程实现上的深层思考。

FP16 的困境:精度与性能的权衡

在向量检索场景中,FP16(半精度浮点数)是一个极具吸引力的选择:

  • 内存占用减半:相比 FP32,FP16 向量只需要一半的存储空间
  • 带宽需求降低:同样的内存带宽可以传输更多向量
  • 精度损失可控:对于 Embedding 向量,FP16 的精度通常足够

但理想很丰满,现实很骨感。OpenSearch 3.1 引入内存优化搜索时,FP16 却成了性能瓶颈——搜索速度比 FP32 慢了近一倍。

问题出在哪里?

JVM 的先天不足

OpenSearch 基于 Java 生态,而 JVM 对 FP16 的支持存在一个根本性问题:没有原生 FP16 类型

这意味着:

  1. FP16 向量必须先转换为 FP32 才能进行计算
  2. 转换过程是纯软件实现,无法利用 CPU 的硬件加速
  3. 每次距离计算都要重复这个转换,成为性能瓶颈

对于高维向量(如 768 维或 1536 维),这个开销被放大到极致。

SIMD:向量化计算的救星

OpenSearch 3.4 开始引入 SIMD(Single Instruction Multiple Data)优化,将距离计算委托给 C++ 层的 SIMD 实现。

SIMD 的核心思想很简单:

  • 一次性加载多个数据到寄存器
  • 一条指令同时处理多个数据
  • 充分利用现代 CPU 的并行计算能力

以 Faiss 库的 SIMD 实现为例,它可以同时处理 4 个维度的向量计算,通过循环展开技术大幅提升效率。

但这还不是最优解。

Bulk SIMD:从单次到批量

OpenSearch 3.5 的 Bulk SIMD 优化,解决了一个被忽视的问题:查询向量的重复加载

在传统的 SIMD 实现中:

对于每个文档向量:
    加载查询向量的前 8 个维度到寄存器
    加载文档向量的前 8 个维度到寄存器
    执行 SIMD 计算
    加载查询向量的下 8 个维度...
    ...

问题很明显:查询向量的每个维度被重复加载了 N 次(N = 文档数量)。

Bulk SIMD 的改进思路是:一次性加载查询向量,批量处理多个文档向量

加载查询向量的前 8 个维度到寄存器(一次)
对于每 4 个文档向量:
    批量加载 4 个文档向量的前 8 个维度
    执行 SIMD 计算(一次处理 4 个)
重复直到处理完所有维度

这种"查询向量复用"的策略,将内存访问模式从随机访问变为顺序访问,大幅提升了缓存命中率。

性能数据解读

OpenSearch 团队公布的性能数据值得关注:

版本 优化技术 吞吐量提升 p99 延迟
3.1 内存优化搜索 基线 ~280ms
3.4 SIMD FP16 ~150% ~150ms
3.5 Bulk SIMD 310% 91ms

从 280ms 到 91ms,延迟降低了 67%,这意味着:

  • 同样的硬件可以支撑 3 倍的并发查询
  • 或者将成本降低至原来的 1/3
  • 用户体验从"可感知延迟"变为"瞬时响应"

对搜索工程的启示

OpenSearch 的优化路径给我们几个重要启示:

1. 性能优化是渐进式的

从 3.1 到 3.5,经历了三个版本的迭代优化。每个版本解决一个具体瓶颈,最终累积成质变。

2. 跨语言优化的必要性

Java 生态的便利性不应成为性能的天花板。通过 JNI 调用 C++ SIMD 代码,是 JVM 应用突破性能瓶颈的常见模式。

3. 内存访问模式比算法更重要

Bulk SIMD 的核心改进不是算法创新,而是优化了内存访问模式。在现代 CPU 架构下,缓存友好性往往比算法复杂度更影响性能。

4. 向量化是趋势

无论是 SIMD、GPU 还是专用向量处理器,向量化计算都是向量检索的必经之路。OpenSearch 的优化只是这个趋势的一个缩影。

局限与思考

Bulk SIMD 并非万能药:

  • CPU 依赖:需要支持 AVX2/AVX-512 的现代 CPU
  • 向量维度限制:某些优化对维度有特定要求(如 768 维)
  • 实现复杂度:跨语言调用增加了维护成本

但对于大规模向量检索场景,这些代价是值得的。

未来展望

随着向量搜索成为 AI 应用的基础设施,性能优化将进入更深层次的竞争:

  • 专用硬件:GPU、TPU、向量处理器的应用
  • 量化技术:INT8、Binary 向量的工程化
  • 索引算法:HNSW、IVF 的持续优化
  • 内存架构:CXL、持久内存的利用

OpenSearch 的 FP16 优化只是这场竞赛的一个节点。对于搜索工程师而言,理解这些底层优化原理,将有助于在架构设计中做出更明智的权衡。


在 AI 时代,向量搜索的性能不再是"锦上添花",而是决定产品体验的核心竞争力。

当延迟从 280ms 降到 91ms,用户感受到的不是数字变化,而是"流畅"与"卡顿"的本质区别。


来源: OpenSearch Blog (March 3, 2026)
标题: Accelerating FP16 vector search performance using bulk SIMD in OpenSearch 3.5
技术要点: SIMD, Bulk SIMD, FP16, 向量搜索性能优化

收起阅读 »

NanoVDR:将 20 亿参数视觉检索模型蒸馏为 7000 万文本编码器

视觉文档检索(Visual Document Retrieval, VDR)正在经历一场效率革命。

传统方案依赖数十亿参数的视觉-语言模型(VLM)同时处理文档索引和查询编码。这种对称设计带来了一个尴尬的现实:即使是纯文本查询,也需要加载庞大的多模态模型,推理延迟高、GPU 依赖强。

一项最新研究提出了一个反直觉的洞察:查询和文档的复杂度是不对称的

不对称的检索场景

想象这样一个场景:

  • 文档端:需要处理扫描发票、PDF 报告、手写笔记——视觉复杂度高,需要强大的视觉理解能力
  • 查询端:用户输入的只是一句简短的文字描述——纯文本,没有视觉信息

既然查询没有视觉内容,为什么查询编码器也需要是视觉-语言模型?

这就是 NanoVDR 的核心假设:文档编码和查询编码可以解耦,用不同的模型处理。

NanoVDR 的解耦架构

NanoVDR 采用了一种"教师-学生"的蒸馏架构:

离线阶段(教师)

  • 使用冻结的 20 亿参数 VLM(如 Qwen2-VL)索引文档
  • 生成高质量的文档向量表示
  • 这是一次性的离线计算,可以承受高成本

在线阶段(学生)

  • 使用蒸馏后的纯文本编码器处理查询
  • 最小仅需 6900 万参数(DistilBERT 级别)
  • 在 CPU 上也能快速推理

这种架构的巧妙之处在于:它利用了查询-文档的不对称性,把计算成本从在线查询转移到了离线索引。

关键设计:蒸馏目标的选择

解耦架构的核心挑战是:如何让纯文本学生模型学会理解视觉文档的语义?

研究人员系统比较了六种蒸馏目标:

蒸馏目标 原理 效果
点级余弦对齐 直接对齐查询文本的向量表示 最佳
排序蒸馏 学习相对排序关系 次优
对比学习 拉近正样本、推开负样本 需要更多数据

研究发现,点级余弦对齐(Pointwise Cosine Alignment) consistently 优于其他方案。它的优势在于:

  1. 只需要预缓存的教师查询嵌入
  2. 训练时不需要处理文档
  3. 实现简单,训练成本低于 13 GPU 小时

跨语言:隐藏的性能瓶颈

研究还揭示了一个容易被忽视的问题:跨语言迁移是主要性能瓶颈

当模型在英语数据上训练,却要在法语、德语等场景下使用时,性能会显著下降。解决方案出乎意料地简单:用机器翻译扩充训练数据

通过添加机器翻译的查询,NanoVDR-S-Multi 在多语言场景下依然保持了 95.1% 的教师模型质量。

性能对比:小模型的大胜利

在 ViDoRe 基准测试(22 个数据集)上的结果令人印象深刻:

模型 参数量 相对教师质量 CPU 查询延迟
教师 VLM 2B 100%
DSE-Qwen2 2B 基线
NanoVDR-S-Multi 69M 95.1% 50× 更低

32 倍参数减少,50 倍延迟降低,却保持了 95% 以上的质量——这对于生产环境的视觉文档检索系统来说,是一个极具吸引力的 trade-off。

对搜索工程的启示

NanoVDR 的设计哲学对搜索系统架构有普遍借鉴意义:

1. 识别不对称性

不是所有检索场景都需要对称的编码器。分析查询和文档的特点,识别可以解耦的环节。

2. 离线重、在线轻

把计算密集型工作移到离线阶段,在线阶段只保留轻量推理。这是降低服务成本的经典策略。

3. 蒸馏目标的选择至关重要

同样的教师模型,不同的蒸馏目标,效果可能天差地别。系统性的对比实验是必要的。

4. 跨语言不是后期补丁

多语言能力应该在设计初期就考虑,通过数据增强来解决,而不是依赖模型的"零样本"能力。

局限与思考

NanoVDR 的方案也有其适用边界:

  • 查询必须是纯文本:如果查询包含图像(如"找和这个类似的文档"),纯文本编码器就无能为力了
  • 依赖教师模型质量:蒸馏的效果上限是教师模型的能力
  • 领域适应性:在特定垂直领域(如医疗、法律文档),可能需要额外的领域适应

但对于企业文档检索、发票处理、表单搜索等以文本查询为主的场景,NanoVDR 提供了一个极具性价比的解决方案。


在 AI 检索系统的工程实践中,最大的优化机会往往藏在"理所当然"的架构假设里。

当我们质疑"查询和文档必须用同样的编码器"这一默认选择时,效率提升的空间就显现了。


论文: arXiv:2603.12824
标题: Distilling a 2B Vision-Language Retriever into a 70M Text-Only Encoder for Visual Document Retrieval
作者: Zhuchenyang Liu 等

继续阅读 »

视觉文档检索(Visual Document Retrieval, VDR)正在经历一场效率革命。

传统方案依赖数十亿参数的视觉-语言模型(VLM)同时处理文档索引和查询编码。这种对称设计带来了一个尴尬的现实:即使是纯文本查询,也需要加载庞大的多模态模型,推理延迟高、GPU 依赖强。

一项最新研究提出了一个反直觉的洞察:查询和文档的复杂度是不对称的

不对称的检索场景

想象这样一个场景:

  • 文档端:需要处理扫描发票、PDF 报告、手写笔记——视觉复杂度高,需要强大的视觉理解能力
  • 查询端:用户输入的只是一句简短的文字描述——纯文本,没有视觉信息

既然查询没有视觉内容,为什么查询编码器也需要是视觉-语言模型?

这就是 NanoVDR 的核心假设:文档编码和查询编码可以解耦,用不同的模型处理。

NanoVDR 的解耦架构

NanoVDR 采用了一种"教师-学生"的蒸馏架构:

离线阶段(教师)

  • 使用冻结的 20 亿参数 VLM(如 Qwen2-VL)索引文档
  • 生成高质量的文档向量表示
  • 这是一次性的离线计算,可以承受高成本

在线阶段(学生)

  • 使用蒸馏后的纯文本编码器处理查询
  • 最小仅需 6900 万参数(DistilBERT 级别)
  • 在 CPU 上也能快速推理

这种架构的巧妙之处在于:它利用了查询-文档的不对称性,把计算成本从在线查询转移到了离线索引。

关键设计:蒸馏目标的选择

解耦架构的核心挑战是:如何让纯文本学生模型学会理解视觉文档的语义?

研究人员系统比较了六种蒸馏目标:

蒸馏目标 原理 效果
点级余弦对齐 直接对齐查询文本的向量表示 最佳
排序蒸馏 学习相对排序关系 次优
对比学习 拉近正样本、推开负样本 需要更多数据

研究发现,点级余弦对齐(Pointwise Cosine Alignment) consistently 优于其他方案。它的优势在于:

  1. 只需要预缓存的教师查询嵌入
  2. 训练时不需要处理文档
  3. 实现简单,训练成本低于 13 GPU 小时

跨语言:隐藏的性能瓶颈

研究还揭示了一个容易被忽视的问题:跨语言迁移是主要性能瓶颈

当模型在英语数据上训练,却要在法语、德语等场景下使用时,性能会显著下降。解决方案出乎意料地简单:用机器翻译扩充训练数据

通过添加机器翻译的查询,NanoVDR-S-Multi 在多语言场景下依然保持了 95.1% 的教师模型质量。

性能对比:小模型的大胜利

在 ViDoRe 基准测试(22 个数据集)上的结果令人印象深刻:

模型 参数量 相对教师质量 CPU 查询延迟
教师 VLM 2B 100%
DSE-Qwen2 2B 基线
NanoVDR-S-Multi 69M 95.1% 50× 更低

32 倍参数减少,50 倍延迟降低,却保持了 95% 以上的质量——这对于生产环境的视觉文档检索系统来说,是一个极具吸引力的 trade-off。

对搜索工程的启示

NanoVDR 的设计哲学对搜索系统架构有普遍借鉴意义:

1. 识别不对称性

不是所有检索场景都需要对称的编码器。分析查询和文档的特点,识别可以解耦的环节。

2. 离线重、在线轻

把计算密集型工作移到离线阶段,在线阶段只保留轻量推理。这是降低服务成本的经典策略。

3. 蒸馏目标的选择至关重要

同样的教师模型,不同的蒸馏目标,效果可能天差地别。系统性的对比实验是必要的。

4. 跨语言不是后期补丁

多语言能力应该在设计初期就考虑,通过数据增强来解决,而不是依赖模型的"零样本"能力。

局限与思考

NanoVDR 的方案也有其适用边界:

  • 查询必须是纯文本:如果查询包含图像(如"找和这个类似的文档"),纯文本编码器就无能为力了
  • 依赖教师模型质量:蒸馏的效果上限是教师模型的能力
  • 领域适应性:在特定垂直领域(如医疗、法律文档),可能需要额外的领域适应

但对于企业文档检索、发票处理、表单搜索等以文本查询为主的场景,NanoVDR 提供了一个极具性价比的解决方案。


在 AI 检索系统的工程实践中,最大的优化机会往往藏在"理所当然"的架构假设里。

当我们质疑"查询和文档必须用同样的编码器"这一默认选择时,效率提升的空间就显现了。


论文: arXiv:2603.12824
标题: Distilling a 2B Vision-Language Retriever into a 70M Text-Only Encoder for Visual Document Retrieval
作者: Zhuchenyang Liu 等

收起阅读 »

Wayland 的架构之困:当合成器与窗口管理器解耦之后

Linux 桌面生态正在经历一场静默的架构革命。

过去十年,Wayland 作为 X11 的继任者被寄予厚望,却迟迟未能完全接管桌面市场。其中一个被忽视的原因,或许藏在它的架构设计里——传统 Wayland 合成器(Compositor)采用了与 X11 时代截然不同的单体架构,将显示服务器、合成器和窗口管理器三个角色强行捆绑在一起。

这种设计解决了 X11 的延迟问题,却制造了新的麻烦:如果你想换一个窗口管理器,就必须重写整个 Wayland 合成器。

被忽视的架构债务

让我们先看看 X11 时代的分工:

  • X Server 负责与内核交互,处理输入事件和缓冲区
  • Compositor 负责将多个窗口的缓冲区合成为一帧画面
  • Window Manager 负责窗口布局、快捷键、用户交互策略

这种分离架构的问题很明显:当用户点击一个按钮时,输入事件要在 X Server、Compositor、Window Manager 之间来回传递,每一次跨进程通信都在增加延迟。

Wayland 的解决方案是"一刀切"——把三个角色合并到一个进程里。这确实消除了延迟,但也带来了意想不到的副作用。

单体架构的隐性成本

想象一下这个场景:你喜欢 i3wm 的平铺布局,却看中了 Hyprland 的动画效果。在 X11 时代,你可以自由组合:用 i3wm 管理窗口,用 picom 做合成。但在 Wayland 世界里,这是不可能的。

每一个 Wayland 合成器都在重复造轮子:

  • Sway 实现了 i3 的平铺逻辑,但必须从头写一套 Wayland 合成代码
  • Hyprland 做出了漂亮的动画,却无法被其他窗口管理器复用
  • 开发者想要自定义窗口管理策略,必须 fork 整个合成器

这种架构的隐性成本是:创新被锁定在单体边界内,无法像 X11 时代那样自由组合最佳组件。

River 的解耦实验

River 项目正在尝试一条不同的路。

它的核心洞察是:Wayland 解决的是 X11 的延迟问题,但并不意味着窗口管理器必须与合成器绑定。真正需要合并的只有显示服务器和合成器——这两个角色决定了帧延迟。窗口管理器完全可以作为独立进程存在。

River 0.4.0 版本引入的 river-window-management-v1 协议,正是这种思路的体现:

  • River 专注于帧完美渲染、性能优化、底层基础设施
  • Window Manager 作为独立程序,通过协议控制窗口位置、快捷键、布局策略

这种分离不是简单的进程拆分,而是重新定义了协议边界。窗口管理器拥有完整的控制权,却不必关心缓冲区合成、DRM/KMS 交互这些底层细节。

对搜索技术的启示

这个架构实验对搜索领域有有趣的映射。

现代搜索引擎的架构演进,其实也经历了类似的"合久必分,分久必合":

早期分离架构:爬虫、索引、查询处理、排序各自独立,通过消息队列通信。灵活但延迟高。

单体优化阶段:为了降低延迟,很多系统开始将组件合并到同一进程,甚至同一台机器。性能提升,但灵活性下降。

现在的微服务趋势:随着硬件性能提升和网络优化,又开始在灵活性和性能之间寻找新平衡。

River 的探索提醒我们:架构选择的权衡不是一成不变的。当底层基础设施(Wayland 协议、硬件性能)发生变化时,上层的组件边界也值得重新审视。

未来展望

River 的窗口管理协议已经吸引了多个独立窗口管理器的实现。这种生态的多样性,正是 X11 时代的魅力所在。

如果这种模式被更广泛地接受,我们可能会看到:

  • 专注于特定交互范式(平铺、浮动、标签页)的窗口管理器百花齐放
  • 合成器专注于渲染性能和视觉效果,不必重复实现窗口管理逻辑
  • 用户可以自由组合最佳组件,而不是被迫接受单体合成器的全部设计

当然,这种架构也有代价:进程间通信的延迟、协议标准化的复杂性、生态碎片化的风险。River 能否证明这些代价是值得的,还有待时间检验。

但无论如何,这种敢于质疑"既定架构"的探索精神,本身就值得尊重。


技术架构的演进,从来不是寻找唯一正确的答案,而是在不同约束条件下寻找最优的权衡。

或许 Wayland 的下一个十年,会从 River 这样的实验中找到新的方向。


来源: HackerNews (232 points, 107 comments)
原文: Separating the Wayland Compositor and Window Manager
相关: FOSDEM 2026 Talk

继续阅读 »

Linux 桌面生态正在经历一场静默的架构革命。

过去十年,Wayland 作为 X11 的继任者被寄予厚望,却迟迟未能完全接管桌面市场。其中一个被忽视的原因,或许藏在它的架构设计里——传统 Wayland 合成器(Compositor)采用了与 X11 时代截然不同的单体架构,将显示服务器、合成器和窗口管理器三个角色强行捆绑在一起。

这种设计解决了 X11 的延迟问题,却制造了新的麻烦:如果你想换一个窗口管理器,就必须重写整个 Wayland 合成器。

被忽视的架构债务

让我们先看看 X11 时代的分工:

  • X Server 负责与内核交互,处理输入事件和缓冲区
  • Compositor 负责将多个窗口的缓冲区合成为一帧画面
  • Window Manager 负责窗口布局、快捷键、用户交互策略

这种分离架构的问题很明显:当用户点击一个按钮时,输入事件要在 X Server、Compositor、Window Manager 之间来回传递,每一次跨进程通信都在增加延迟。

Wayland 的解决方案是"一刀切"——把三个角色合并到一个进程里。这确实消除了延迟,但也带来了意想不到的副作用。

单体架构的隐性成本

想象一下这个场景:你喜欢 i3wm 的平铺布局,却看中了 Hyprland 的动画效果。在 X11 时代,你可以自由组合:用 i3wm 管理窗口,用 picom 做合成。但在 Wayland 世界里,这是不可能的。

每一个 Wayland 合成器都在重复造轮子:

  • Sway 实现了 i3 的平铺逻辑,但必须从头写一套 Wayland 合成代码
  • Hyprland 做出了漂亮的动画,却无法被其他窗口管理器复用
  • 开发者想要自定义窗口管理策略,必须 fork 整个合成器

这种架构的隐性成本是:创新被锁定在单体边界内,无法像 X11 时代那样自由组合最佳组件。

River 的解耦实验

River 项目正在尝试一条不同的路。

它的核心洞察是:Wayland 解决的是 X11 的延迟问题,但并不意味着窗口管理器必须与合成器绑定。真正需要合并的只有显示服务器和合成器——这两个角色决定了帧延迟。窗口管理器完全可以作为独立进程存在。

River 0.4.0 版本引入的 river-window-management-v1 协议,正是这种思路的体现:

  • River 专注于帧完美渲染、性能优化、底层基础设施
  • Window Manager 作为独立程序,通过协议控制窗口位置、快捷键、布局策略

这种分离不是简单的进程拆分,而是重新定义了协议边界。窗口管理器拥有完整的控制权,却不必关心缓冲区合成、DRM/KMS 交互这些底层细节。

对搜索技术的启示

这个架构实验对搜索领域有有趣的映射。

现代搜索引擎的架构演进,其实也经历了类似的"合久必分,分久必合":

早期分离架构:爬虫、索引、查询处理、排序各自独立,通过消息队列通信。灵活但延迟高。

单体优化阶段:为了降低延迟,很多系统开始将组件合并到同一进程,甚至同一台机器。性能提升,但灵活性下降。

现在的微服务趋势:随着硬件性能提升和网络优化,又开始在灵活性和性能之间寻找新平衡。

River 的探索提醒我们:架构选择的权衡不是一成不变的。当底层基础设施(Wayland 协议、硬件性能)发生变化时,上层的组件边界也值得重新审视。

未来展望

River 的窗口管理协议已经吸引了多个独立窗口管理器的实现。这种生态的多样性,正是 X11 时代的魅力所在。

如果这种模式被更广泛地接受,我们可能会看到:

  • 专注于特定交互范式(平铺、浮动、标签页)的窗口管理器百花齐放
  • 合成器专注于渲染性能和视觉效果,不必重复实现窗口管理逻辑
  • 用户可以自由组合最佳组件,而不是被迫接受单体合成器的全部设计

当然,这种架构也有代价:进程间通信的延迟、协议标准化的复杂性、生态碎片化的风险。River 能否证明这些代价是值得的,还有待时间检验。

但无论如何,这种敢于质疑"既定架构"的探索精神,本身就值得尊重。


技术架构的演进,从来不是寻找唯一正确的答案,而是在不同约束条件下寻找最优的权衡。

或许 Wayland 的下一个十年,会从 River 这样的实验中找到新的方向。


来源: HackerNews (232 points, 107 comments)
原文: Separating the Wayland Compositor and Window Manager
相关: FOSDEM 2026 Talk

收起阅读 »

讨论:ReAct 模式在搜索场景的实际应用

最近在学习 AI 搜索相关的技术,发现 Agentic Engineering 这个概念很有意思。

想请教各位:

ReAct 模式在实际项目中真的好用吗?

看了一些论文和示例,感觉理论上很完美:

  • Thought → Action → Observation → 循环

但实际操作中遇到的问题:

  1. LLM 规划的步骤经常不合理
  2. 工具调用失败后的重试机制怎么设计?
  3. 多轮交互后的上下文管理

有没有在生产环境用过的同学分享一下经验?

另外,对于搜索场景,你们觉得 Agent 更适合做:

  • A. 查询理解/扩展
  • B. 结果后处理/总结
  • C. 全流程自动化

期待讨论!

继续阅读 »

最近在学习 AI 搜索相关的技术,发现 Agentic Engineering 这个概念很有意思。

想请教各位:

ReAct 模式在实际项目中真的好用吗?

看了一些论文和示例,感觉理论上很完美:

  • Thought → Action → Observation → 循环

但实际操作中遇到的问题:

  1. LLM 规划的步骤经常不合理
  2. 工具调用失败后的重试机制怎么设计?
  3. 多轮交互后的上下文管理

有没有在生产环境用过的同学分享一下经验?

另外,对于搜索场景,你们觉得 Agent 更适合做:

  • A. 查询理解/扩展
  • B. 结果后处理/总结
  • C. 全流程自动化

期待讨论!

收起阅读 »

请教:生产环境的 Web 性能监控方案?

看到今天发布的几篇关于 Web 性能的文章,想请教一下大家:

在实际项目中,你们是怎么做性能监控的?

我目前只知道 Chrome DevTools 的 Lighthouse,但感觉更适合开发阶段。上线后有什么好的监控方案吗?

特别是:

  • 真实用户性能数据怎么收集?
  • 有没有开源的监控工具推荐?
  • 性能预算(Performance Budget)怎么设定比较合理?

求大佬们指点!

继续阅读 »

看到今天发布的几篇关于 Web 性能的文章,想请教一下大家:

在实际项目中,你们是怎么做性能监控的?

我目前只知道 Chrome DevTools 的 Lighthouse,但感觉更适合开发阶段。上线后有什么好的监控方案吗?

特别是:

  • 真实用户性能数据怎么收集?
  • 有没有开源的监控工具推荐?
  • 性能预算(Performance Budget)怎么设定比较合理?

求大佬们指点!

收起阅读 »

TLPI 成为大学教材:Linux 系统编程的教育价值

Linux 系统编程一直是后端开发者的核心技能。最近,《The Linux Programming Interface》(TLPI) 作者 Michael Kerrisk 宣布该书正式被多所大学采纳为课程教材。

TLPI 简介

《The Linux Programming Interface》是 Linux/Unix 系统编程领域的权威著作,涵盖了:

  • 文件 I/O 与文件系统
  • 进程管理与信号
  • 线程与同步
  • 内存管理
  • 网络编程
  • 高级 IPC 机制

为什么适合作为教材?

1. 理论与实践结合

书中每个概念都配有完整的代码示例,学生可以直接编译运行。

2. 覆盖全面

从基础的文件操作到复杂的 epoll、inotify 都有详细讲解。

3. 与工业界接轨

内容紧跟 Linux 内核发展,学习的知识在实际工作中直接可用。

对搜索工程师的价值

对于从事搜索引擎、分布式系统开发的工程师,TLPI 中的以下章节尤为重要:

章节 主题 应用场景
Ch 5 文件 I/O 索引文件读写
Ch 44 Pipes & FIFO 进程间通信
Ch 63 epoll 高性能网络服务
Ch 64 inotify 文件变更监控

学习建议

  1. 动手实践 - 每章的示例代码都要自己敲一遍
  2. 阅读 man 手册 - 培养查阅官方文档的习惯
  3. 结合内核源码 - 深入理解系统调用实现

来源: HackerNews (28 points)
原文: The Linux Programming Interface as a university course text

继续阅读 »

Linux 系统编程一直是后端开发者的核心技能。最近,《The Linux Programming Interface》(TLPI) 作者 Michael Kerrisk 宣布该书正式被多所大学采纳为课程教材。

TLPI 简介

《The Linux Programming Interface》是 Linux/Unix 系统编程领域的权威著作,涵盖了:

  • 文件 I/O 与文件系统
  • 进程管理与信号
  • 线程与同步
  • 内存管理
  • 网络编程
  • 高级 IPC 机制

为什么适合作为教材?

1. 理论与实践结合

书中每个概念都配有完整的代码示例,学生可以直接编译运行。

2. 覆盖全面

从基础的文件操作到复杂的 epoll、inotify 都有详细讲解。

3. 与工业界接轨

内容紧跟 Linux 内核发展,学习的知识在实际工作中直接可用。

对搜索工程师的价值

对于从事搜索引擎、分布式系统开发的工程师,TLPI 中的以下章节尤为重要:

章节 主题 应用场景
Ch 5 文件 I/O 索引文件读写
Ch 44 Pipes & FIFO 进程间通信
Ch 63 epoll 高性能网络服务
Ch 64 inotify 文件变更监控

学习建议

  1. 动手实践 - 每章的示例代码都要自己敲一遍
  2. 阅读 man 手册 - 培养查阅官方文档的习惯
  3. 结合内核源码 - 深入理解系统调用实现

来源: HackerNews (28 points)
原文: The Linux Programming Interface as a university course text

收起阅读 »

LLM 架构全景图:从 Transformer 到 MoE

大语言模型(LLM)的架构演进是 AI 领域最活跃的研究方向之一。Sebastian Raschka 整理的 LLM Architecture Gallery 为我们提供了清晰的视觉参考。

主流架构概览

Transformer 基础架构

  • Encoder-Only (BERT 系列)

    • 双向注意力机制
    • 适合理解任务
    • 代表模型: BERT, RoBERTa, DeBERTa
  • Decoder-Only (GPT 系列)

    • 自回归生成
    • 适合文本生成
    • 代表模型: GPT-3/4, LLaMA, Claude
  • Encoder-Decoder (T5 系列)
    • 编码器-解码器分离
    • 适合翻译、摘要
    • 代表模型: T5, BART, UL2

关键技术创新

注意力机制演进

机制 特点 应用
Full Attention 全局注意力 原始 Transformer
Sparse Attention 稀疏模式 Longformer, BigBird
Flash Attention 内存优化 现代 LLM 标配
Multi-Query Attention 推理加速 LLaMA-2, Falcon
Grouped-Query Attention 平衡效果与速度 LLaMA-3, Mistral

位置编码方案

  • 绝对位置编码 (原始 Transformer)
  • 相对位置编码 (T5, DeBERTa)
  • 旋转位置编码 RoPE (LLaMA, Mistral)
  • ALiBi (BLOOM, MPT)

搜索领域的架构选择

对于搜索和 RAG 应用:

  1. Embedding 模型 - 通常选择 Encoder-Only (BERT 类)
  2. 生成模型 - Decoder-Only 更适合生成回答
  3. 重排序模型 - 轻量级 Cross-Encoder

最新趋势

  • Mixture of Experts (MoE) - 稀疏激活,如 Mixtral
  • State Space Models - 长序列建模,如 Mamba
  • 多模态融合 - 统一处理文本、图像、音频

来源: HackerNews (257 points, 20 comments)
原文: LLM Architecture Gallery

继续阅读 »

大语言模型(LLM)的架构演进是 AI 领域最活跃的研究方向之一。Sebastian Raschka 整理的 LLM Architecture Gallery 为我们提供了清晰的视觉参考。

主流架构概览

Transformer 基础架构

  • Encoder-Only (BERT 系列)

    • 双向注意力机制
    • 适合理解任务
    • 代表模型: BERT, RoBERTa, DeBERTa
  • Decoder-Only (GPT 系列)

    • 自回归生成
    • 适合文本生成
    • 代表模型: GPT-3/4, LLaMA, Claude
  • Encoder-Decoder (T5 系列)
    • 编码器-解码器分离
    • 适合翻译、摘要
    • 代表模型: T5, BART, UL2

关键技术创新

注意力机制演进

机制 特点 应用
Full Attention 全局注意力 原始 Transformer
Sparse Attention 稀疏模式 Longformer, BigBird
Flash Attention 内存优化 现代 LLM 标配
Multi-Query Attention 推理加速 LLaMA-2, Falcon
Grouped-Query Attention 平衡效果与速度 LLaMA-3, Mistral

位置编码方案

  • 绝对位置编码 (原始 Transformer)
  • 相对位置编码 (T5, DeBERTa)
  • 旋转位置编码 RoPE (LLaMA, Mistral)
  • ALiBi (BLOOM, MPT)

搜索领域的架构选择

对于搜索和 RAG 应用:

  1. Embedding 模型 - 通常选择 Encoder-Only (BERT 类)
  2. 生成模型 - Decoder-Only 更适合生成回答
  3. 重排序模型 - 轻量级 Cross-Encoder

最新趋势

  • Mixture of Experts (MoE) - 稀疏激活,如 Mixtral
  • State Space Models - 长序列建模,如 Mamba
  • 多模态融合 - 统一处理文本、图像、音频

来源: HackerNews (257 points, 20 comments)
原文: LLM Architecture Gallery

收起阅读 »

Agentic Engineering:AI 智能体的工程化实践

AI 智能体(AI Agent)正在改变软件工程的工作方式。Simon Willison 在其最新指南中系统性地总结了 Agentic Engineering 的核心模式。

什么是 Agentic Engineering?

Agentic Engineering 是指构建能够自主规划、执行和迭代的 AI 系统,而非简单的单次调用模型。

核心模式

1. ReAct 模式(Reasoning + Acting)

AI 交替进行推理和行动:

  • Thought: 分析当前状态和目标
  • Action: 执行具体操作
  • Observation: 观察执行结果
  • 循环直到任务完成

2. 工具使用(Tool Use)

让 AI 能够调用外部工具:

  • 代码执行环境
  • 搜索引擎
  • 数据库查询
  • API 调用

3. 规划与执行分离

将复杂任务分解为可管理的子任务:

  1. 生成执行计划
  2. 按步骤执行
  3. 验证中间结果
  4. 必要时重新规划

4. 多智能体协作

多个专业 Agent 协同工作:

  • 研究员 Agent 收集信息
  • 编码 Agent 实现功能
  • 审查 Agent 检查质量

在搜索领域的应用

Agentic Engineering 为搜索技术带来新可能:

  • 智能查询扩展 - Agent 自动分析用户意图,生成多维度查询
  • 结果深度分析 - 自动对比、总结多个搜索结果
  • 知识图谱构建 - 从搜索结果中提取实体关系
  • 个性化推荐 - 基于用户历史行为主动推荐相关内容

实施建议

  1. 从简单的 ReAct 模式开始
  2. 为 Agent 设计清晰的工具接口
  3. 建立有效的错误恢复机制
  4. 记录和评估 Agent 的决策过程

来源: HackerNews (21 points, 10 comments)
原文: What Is Agentic Engineering?

继续阅读 »

AI 智能体(AI Agent)正在改变软件工程的工作方式。Simon Willison 在其最新指南中系统性地总结了 Agentic Engineering 的核心模式。

什么是 Agentic Engineering?

Agentic Engineering 是指构建能够自主规划、执行和迭代的 AI 系统,而非简单的单次调用模型。

核心模式

1. ReAct 模式(Reasoning + Acting)

AI 交替进行推理和行动:

  • Thought: 分析当前状态和目标
  • Action: 执行具体操作
  • Observation: 观察执行结果
  • 循环直到任务完成

2. 工具使用(Tool Use)

让 AI 能够调用外部工具:

  • 代码执行环境
  • 搜索引擎
  • 数据库查询
  • API 调用

3. 规划与执行分离

将复杂任务分解为可管理的子任务:

  1. 生成执行计划
  2. 按步骤执行
  3. 验证中间结果
  4. 必要时重新规划

4. 多智能体协作

多个专业 Agent 协同工作:

  • 研究员 Agent 收集信息
  • 编码 Agent 实现功能
  • 审查 Agent 检查质量

在搜索领域的应用

Agentic Engineering 为搜索技术带来新可能:

  • 智能查询扩展 - Agent 自动分析用户意图,生成多维度查询
  • 结果深度分析 - 自动对比、总结多个搜索结果
  • 知识图谱构建 - 从搜索结果中提取实体关系
  • 个性化推荐 - 基于用户历史行为主动推荐相关内容

实施建议

  1. 从简单的 ReAct 模式开始
  2. 为 Agent 设计清晰的工具接口
  3. 建立有效的错误恢复机制
  4. 记录和评估 Agent 的决策过程

来源: HackerNews (21 points, 10 comments)
原文: What Is Agentic Engineering?

收起阅读 »

Web 性能审计:一个 49MB 新闻页面的启示

Web 性能优化一直是开发者关注的重点。最近一篇关于新闻网站性能审计的文章在 HackerNews 上引发了热议 —— 一个新闻页面竟然达到了 49MB 的体积。

49MB 网页的构成分析

作者 Shubham Jain 对主流新闻网站进行了深度性能审计,发现:

广告与追踪脚本

  • 页面加载了数十个第三方追踪器
  • 广告脚本占用了大量带宽和 CPU
  • 部分广告脚本存在内存泄漏问题

图片与媒体资源

  • 未优化的原始图片(单张可达 2-3MB)
  • 自动播放的视频预加载
  • 响应式图片实现不当

JavaScript 膨胀

  • 过时的 jQuery 及其插件
  • 重复加载的库文件
  • 未压缩的源码

性能影响

指标 优化前 优化后
页面大小 49MB 1.2MB
加载时间 45s 2.5s
内存占用 800MB 120MB

对搜索技术的启示

对于搜索引擎和开发者而言,这提醒我们:

  1. Core Web Vitals 的重要性 - 页面性能直接影响搜索排名
  2. 移动优先索引 - 大页面在移动设备上体验极差
  3. 爬虫效率 - 过大的页面会增加搜索引擎抓取成本

优化建议

  • 实施严格的资源预算(Performance Budget)
  • 使用现代图片格式(WebP、AVIF)
  • 延迟加载非关键资源
  • 定期审计第三方脚本

来源: HackerNews (321 points, 170 comments)
原文: The 49MB Web Page

继续阅读 »

Web 性能优化一直是开发者关注的重点。最近一篇关于新闻网站性能审计的文章在 HackerNews 上引发了热议 —— 一个新闻页面竟然达到了 49MB 的体积。

49MB 网页的构成分析

作者 Shubham Jain 对主流新闻网站进行了深度性能审计,发现:

广告与追踪脚本

  • 页面加载了数十个第三方追踪器
  • 广告脚本占用了大量带宽和 CPU
  • 部分广告脚本存在内存泄漏问题

图片与媒体资源

  • 未优化的原始图片(单张可达 2-3MB)
  • 自动播放的视频预加载
  • 响应式图片实现不当

JavaScript 膨胀

  • 过时的 jQuery 及其插件
  • 重复加载的库文件
  • 未压缩的源码

性能影响

指标 优化前 优化后
页面大小 49MB 1.2MB
加载时间 45s 2.5s
内存占用 800MB 120MB

对搜索技术的启示

对于搜索引擎和开发者而言,这提醒我们:

  1. Core Web Vitals 的重要性 - 页面性能直接影响搜索排名
  2. 移动优先索引 - 大页面在移动设备上体验极差
  3. 爬虫效率 - 过大的页面会增加搜索引擎抓取成本

优化建议

  • 实施严格的资源预算(Performance Budget)
  • 使用现代图片格式(WebP、AVIF)
  • 延迟加载非关键资源
  • 定期审计第三方脚本

来源: HackerNews (321 points, 170 comments)
原文: The 49MB Web Page

收起阅读 »

Chrome DevTools MCP 支持:AI 助手可直接调试浏览器会话

Google Chrome 团队近日宣布为 Chrome DevTools 引入 MCP(Model Context Protocol)支持,这一更新让 AI 助手能够直接访问和控制浏览器调试会话。

什么是 MCP?

MCP(Model Context Protocol)是 Anthropic 推出的开放协议,旨在标准化 AI 助手与外部工具、数据源之间的交互。通过 MCP,AI 可以安全地访问本地文件、数据库、API 等资源。

Chrome DevTools MCP 的核心能力

此次更新为开发者带来了以下新特性:

  1. 直接调试浏览器会话 - AI 助手可以通过 MCP 连接 Chrome DevTools,实时查看控制台日志、网络请求、DOM 结构
  2. 自动化调试流程 - 让 AI 协助分析性能瓶颈、定位 JavaScript 错误
  3. 与现有工具链集成 - 支持 Cursor、Claude Desktop 等 AI 编码工具

技术意义

这一更新标志着浏览器调试工具与 AI 助手的深度整合。开发者现在可以:

  • 用自然语言描述问题,让 AI 自动检查页面元素
  • 让 AI 分析网络请求,找出加载性能问题
  • 通过 AI 辅助进行跨浏览器兼容性测试

使用场景示例

开发者: "这个页面加载很慢,帮我分析一下"

AI: 通过 MCP 连接 DevTools,分析资源加载瀑布图,发现第三方脚本阻塞了首屏渲染,建议延迟加载

行业影响

Chrome DevTools MCP 的发布可能会推动更多开发工具采用类似方案。对于搜索技术社区而言,这也为 AI 驱动的网页分析、SEO 诊断等场景提供了新的可能性。


来源: HackerNews (342 points, 147 comments)
原文: Chrome DevTools MCP

继续阅读 »

Google Chrome 团队近日宣布为 Chrome DevTools 引入 MCP(Model Context Protocol)支持,这一更新让 AI 助手能够直接访问和控制浏览器调试会话。

什么是 MCP?

MCP(Model Context Protocol)是 Anthropic 推出的开放协议,旨在标准化 AI 助手与外部工具、数据源之间的交互。通过 MCP,AI 可以安全地访问本地文件、数据库、API 等资源。

Chrome DevTools MCP 的核心能力

此次更新为开发者带来了以下新特性:

  1. 直接调试浏览器会话 - AI 助手可以通过 MCP 连接 Chrome DevTools,实时查看控制台日志、网络请求、DOM 结构
  2. 自动化调试流程 - 让 AI 协助分析性能瓶颈、定位 JavaScript 错误
  3. 与现有工具链集成 - 支持 Cursor、Claude Desktop 等 AI 编码工具

技术意义

这一更新标志着浏览器调试工具与 AI 助手的深度整合。开发者现在可以:

  • 用自然语言描述问题,让 AI 自动检查页面元素
  • 让 AI 分析网络请求,找出加载性能问题
  • 通过 AI 辅助进行跨浏览器兼容性测试

使用场景示例

开发者: "这个页面加载很慢,帮我分析一下"

AI: 通过 MCP 连接 DevTools,分析资源加载瀑布图,发现第三方脚本阻塞了首屏渲染,建议延迟加载

行业影响

Chrome DevTools MCP 的发布可能会推动更多开发工具采用类似方案。对于搜索技术社区而言,这也为 AI 驱动的网页分析、SEO 诊断等场景提供了新的可能性。


来源: HackerNews (342 points, 147 comments)
原文: Chrome DevTools MCP

收起阅读 »

【搜索客社区日报】第2196期 (2026-03-13)

【搜索客社区日报】第2196期 (2026-03-13)

1、超越 Filter Rewrite:OpenSearch Doc Value Skip Index 如何让聚合查询快 28 倍
https://searchkit.cn/article/15707

OpenSearch 团队最新技术博客,介绍 Lucene 10 的 Doc Value Skip Index 如何克服 Filter Rewrite 的局限性,即使过滤字段和聚合字段不相关,也能实现最高 28 倍的聚合性能提升,存储开销仅增加 0.1%-1%。

2、可微分几何索引:生成式检索的新思路
https://searchkit.cn/article/15700

探索生成式检索中的新型索引结构,为向量搜索开辟新方向。

3、用 LLM 做伪相关反馈:搜索技术的新突破?
https://searchkit.cn/article/15699

研究如何利用大语言模型改进传统搜索中的伪相关反馈机制。

4、RAGPerf:首个端到端 RAG 系统基准测试框架
https://searchkit.cn/article/15698

RAG 系统的全面基准测试框架,为 RAG 应用提供标准化评估工具。

编辑:search_engineer
更多资讯:http://news.searchkit.cn

[尊重社区原创,转载请保留或注明出处]

继续阅读 »

【搜索客社区日报】第2196期 (2026-03-13)

1、超越 Filter Rewrite:OpenSearch Doc Value Skip Index 如何让聚合查询快 28 倍
https://searchkit.cn/article/15707

OpenSearch 团队最新技术博客,介绍 Lucene 10 的 Doc Value Skip Index 如何克服 Filter Rewrite 的局限性,即使过滤字段和聚合字段不相关,也能实现最高 28 倍的聚合性能提升,存储开销仅增加 0.1%-1%。

2、可微分几何索引:生成式检索的新思路
https://searchkit.cn/article/15700

探索生成式检索中的新型索引结构,为向量搜索开辟新方向。

3、用 LLM 做伪相关反馈:搜索技术的新突破?
https://searchkit.cn/article/15699

研究如何利用大语言模型改进传统搜索中的伪相关反馈机制。

4、RAGPerf:首个端到端 RAG 系统基准测试框架
https://searchkit.cn/article/15698

RAG 系统的全面基准测试框架,为 RAG 应用提供标准化评估工具。

编辑:search_engineer
更多资讯:http://news.searchkit.cn

[尊重社区原创,转载请保留或注明出处]

收起阅读 »

超越 Filter Rewrite:OpenSearch Doc Value Skip Index 如何让聚合查询快 28 倍

超越 Filter Rewrite:OpenSearch Doc Value Skip Index 如何让聚合查询快 28 倍

原文:https://opensearch.org/blog/beyond-filter-rewrite-how-doc-value-skip-indexes-accelerate-aggregations-in-opensearch/ 作者:Ankit Jain, Asim Mahmood (AWS/OpenSearch 团队)

前言

在之前的博客中,我们介绍了如何通过 Filter Rewrite 和多范围遍历技术,利用 Lucene 的 BKD 树实现高达 100 倍的日期直方图聚合性能提升。这些技术效果显著,但有一个根本限制:要求查询过滤字段和聚合字段必须是同一个,且无法支持复杂的子聚合逻辑。当过滤字段和聚合字段不同时,或者子聚合需要逐文档计算时,查询引擎只能退回到逐个扫描文档的传统方式。

本文将介绍如何通过 Lucene 10 的 Doc Value Skip Index 克服这些限制,即使过滤字段和聚合字段不相关,也能实现最高 28 倍 的聚合性能提升。


Filter Rewrite 的局限性

Filter Rewrite 将日期直方图聚合转换为一系列范围过滤器,然后使用 Lucene 的 BKD 树(Points 索引)来统计每个桶的文档数量,无需访问单个文档值。多范围遍历进一步优化了这一点,通过单次有序遍历处理所有桶。

这两种技术都依赖一个关键假设:查询中用于过滤的字段就是被聚合的字段

问题场景

考虑以下查询,它在 trip_distance 上过滤,但在 dropoff_datetime 上聚合:

{
  "size": 0,
  "query": {
    "range": {
      "trip_distance": {
        "gte": 0,
        "lte": 20
      }
    }
  },
  "aggs": {
    "dropoffs_over_time": {
      "date_histogram": {
        "field": "dropoff_datetime",
        "calendar_interval": "month"
      }
    }
  }
}

由于 trip_distancedropoff_datetime 是不同的字段,trip_distance 的 BKD 树无法提供 dropoff_datetime 值在各桶中分布的信息。引擎无法将聚合重写为对聚合字段的范围过滤器,只能退回到传统方法:遍历每个匹配的文档,获取其 dropoff_datetime 文档值,然后放入正确的桶中

同样,当子聚合需要逐文档计算时(例如计算每个时间桶内的平均值),Filter Rewrite 也无能为力,因为它只提供文档计数,无法访问单个字段值。

这些并非边缘情况。在实际分析查询中,很多场景是在一个维度上过滤,在另一个维度上聚合,子聚合在仪表板和报表中也很常见。我们需要一种更通用的方法。


Doc Value Skip Index:聚合引擎的新工具

从 Lucene 10.0 开始,一种新的索引结构可用:数值文档值上的可选 Skip Index,在 PR #13449 中引入,并在 PR #13563 中增加了多级支持。该结构通过 DocValuesSkipper 抽象暴露,为本文描述的优化提供了基础。

什么是 Skip Index?

在计算机科学中,跳表(Skip List)是一种概率数据结构,通过在有序数据上分层多个级别的链表,使用随机化决定哪些元素出现在更高层,从而提供期望 O(log n) 的搜索时间。

Lucene 的 Doc Value Skip Index 借用了多级概念,但完全是确定性的。它不使用随机化,而是在段创建时将文档划分为固定大小的区间,并在编译时构建摘要级别的层次结构。没有随机性:该结构完全由文档计数和配置的区间大小决定。

类比理解:就像书的目录。不需要逐页扫描来找到所需内容,先查看章标题,然后是节标题,最后缩小到具体页面。Skip Index 的工作原理相同:它让聚合引擎在决定是否检查单个值之前,先检查大块文档的摘要元数据。

Lucene 如何实现 Skip Index

Lucene 的实现使用最多 4 层(level 0-3)的层次结构。基础层(level 0)以 4,096 个文档为区间进行摘要。每个后续层聚合下一层的 8 个区间(2^3 的偏移),如下表所示:

Level 每个区间的文档数 说明
0 4,096 基础区间
1 32,768 (4,096 × 8) 每 8 个 level-0 区间
2 262,144 (4,096 × 64) 每 8 个 level-1 区间
3 2,097,152 (4,096 × 512) 每 8 个 level-2 区间

对于最坏情况下有 2^31 - 1(约 21 亿)个文档的索引,Skip Index 层次结构在 level 0 约有 524,288 个条目,level 1 有 65,536 个,level 2 有 8,192 个,level 3 有 1,024 个。因此,搜索空间从数十亿个文档减少到仅需几千次元数据检查。

每个区间条目非常紧凑(仅 29 字节),编码以下元数据:

  • 级别数(1 字节):该条目包含在多少个级别中
  • 最小值和最大值(16 字节):该区间内字段值的范围
  • 最小和最大文档 ID(8 字节):该区间的文档 ID 边界
  • 文档计数(4 字节):该区间内的文档数量

这些元数据使聚合引擎能够对每个区间做出三种决策:

  1. 如果区间的最小/最大值完全在当前聚合桶之外,跳过整个区间
  2. 如果区间的最小/最大值完全在单个桶内,批量计数所有文档,无需单独检查
  3. 如果区间跨越多个桶,退回到逐个处理文档

遍历从最高可用级别开始,仅在需要时下降,类似于之前优化中 BKD 树遍历跳过整个子树的方式。关键区别在于该结构操作的是文档值而非 Points 索引,因此无论查询过滤哪个字段都有效。

Skip Index 如何解决 Filter Rewrite 的局限性

回顾我们确定的两个主要限制:

  1. 不相关字段:Filter Rewrite 要求过滤字段和聚合字段相同
  2. 复杂子聚合:Filter Rewrite 只提供计数;无法支持桶内的逐文档计算

Skip Index 解决了这两个限制,因为它直接操作聚合字段的文档值,独立于匹配文档集的产生方式。查询引擎首先评估查询(使用适合过滤字段的索引)以产生一组匹配的文档 ID。然后,在聚合期间,它查询聚合字段的 Skip Index,以确定这些匹配文档的块是否可以跳过或批量计数。

这种解耦是关键洞察。Skip Index 不关心文档集是否被过滤过,它只需要知道聚合字段的值在每个区间内的分布情况。

示例

例如,考虑一个在 process.name 上过滤(词项过滤器)并在 @timestamp 上聚合(按天分桶的日期直方图)的查询:

GET /logs-*/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "range": {
            "@timestamp": {
              "gte": "2024-01-01",
              "lte": "2024-01-31"
            }
          }
        },
        {
          "term": {
            "process.name": "systemd"
          }
        }
      ]
    }
  },
  "aggs": {
    "daily_counts": {
      "date_histogram": {
        "field": "@timestamp",
        "calendar_interval": "day"
      }
    }
  }
}

没有 Skip Index 时,@timestamp 上的范围查询可能受益于 Filter Rewrite,但 process.name 上的词项过滤器阻止了查询被重写。引擎必须遍历匹配两个条件的每个文档,并单独检索每个 @timestamp 值。

启用 @timestamp 的 Skip Index 后,聚合引擎可以在收集期间查询 Skip Index。当它遇到一个区间,其中最小和最大 @timestamp 值都落在同一个日桶内时,它会批量计数该区间中所有匹配的文档,无需查找单个值。这可以将总查找次数减少约 85%

使用 Popcount 高效计数

批量计数步骤有一个值得探讨的细节。当 Skip Index 表明区间内的所有值都落在单个桶内时,我们知道批量计数是可能的;然而,我们不能直接使用区间的文档计数。Skip Index 覆盖段中的所有文档,但查询产生的 DocIdSetIterator 只代表匹配查询过滤器的文档子集。我们需要计算该过滤子集中有多少文档落在 Skip Index 区间内。

一种方法是逐个遍历迭代器,边遍历边计数。但这违背了跳过的大部分目的。相反,我们使用硬件级优化:popcount(人口计数)CPU 指令,它可以在单次操作中计算机器字中设置位的数量。

当查询引擎产生 DocIdSetIterator 时,它通常在内部将匹配文档集表示为位集,其中每个位对应一个文档 ID,如果文档匹配查询则设置为 1。要计算 Skip Index 区间内有多少匹配文档,我们提取该位集的相关部分(对应区间内最小/最大文档 ID 范围内的位),并对这些字应用 popcount。这给出了区间内的精确匹配文档数,无需逐个遍历。

DocIdSetIterator 还支持批量收集接口:收集器可以接收文档 ID 流,而不是重复调用 nextDoc()。当流中的所有文档 ID 都落在同一个桶内时(由 Skip Index 确定),整个流可以在一次操作中收集。初步基准测试显示,这种方法在 Skip Index 收益的基础上可额外带来最高 3 倍 的提升。


有序数据:Skip Index 的最佳场景

当文档按聚合字段排序时,Skip Index 发挥最佳性能。数据有序时,连续文档具有相似或单调递增的值,意味着每个 4,096 文档区间内的最小/最大范围较窄。窄范围更有可能完全落在单个聚合桶内,最大化批量计数的机会。

对于时序工作负载(日志分析、指标和可观测性),时间戳字段是自然的选择。大多数时序数据大致按时间顺序到达,OpenSearch 中的数据流保持这种顺序。

确保数据保持有序

仅仅有时间戳字段并不能保证数据在段合并和文档添加时保持有序。有两种方法可以保持排序:

1. Index Sort(推荐):配置显式的索引排序设置,确保无论段如何合并,数据都保持排序:

{
  "settings": {
    "index.sort.field": "@timestamp",
    "index.sort.order": "desc"
  }
}

这保证所有段保持时间戳顺序,提供一致的 Skip Index 性能。

2. Log Merge Policy:或者,使用保留传入文档顺序的合并策略。log_byte_size 合并策略倾向于保持时序数据典型的仅追加工作负载的时间顺序。

当数据无序时,Skip Index 仍然可以正常工作,但提供较少的跳过和批量计数机会,因为每个区间的最小/最大范围可能跨越多个桶。


启用 Skip Index

Skip Index 可以根据 OpenSearch 版本自动启用或通过手动配置启用。

按版本的默认行为

版本 行为
OpenSearch 3.2 为数值字段引入 skip_list 映射参数(默认:false
OpenSearch 3.3 对名为 @timestamp 的日期字段的日期直方图聚合自动启用 Skip Index
OpenSearch 3.4 将自动 Skip Index 优化扩展到 @timestamp 上的自动日期直方图聚合

手动配置

要在其他数值字段上启用 Skip Index,请在字段映射中添加 skip_list 参数:

PUT /my-index
{
  "mappings": {
    "properties": {
      "custom_timestamp": {
        "type": "date",
        "skip_list": true
      },
      "price": {
        "type": "long",
        "skip_list": true
      }
    }
  }
}

性能测试结果

我们使用 OpenSearch Benchmark 和 http_logsbig5 数据集测量了性能。结果显示了显著改进,特别是对于 Filter Rewrite 之前无法帮助的查询。

日期直方图性能(http_logs 工作负载)

基于 PR #19130 使用 http_logs 数据集的测试:

操作 基线 (p90) 启用 Skip Index (p90) 提升
hourly_agg_with_filter 998 ms 36 ms 28 倍更快
hourly_agg_with_filter_and_metrics 1,618 ms 970 ms 40% 更快

第一个操作展示了 Skip Index 在纯计数聚合与不相关过滤器上的全部优势。第二个操作包含子聚合(指标),改进较小,因为逐文档指标计算仍然需要指标字段的单个值查找。

日期直方图查询的吞吐量提高了 21%,对索引性能没有可测量的影响。

自动日期直方图性能(big5 工作负载)

OpenSearch 3.4 将 Skip Index 优化扩展到自动日期直方图聚合。基于 PR #20057 使用 big5 数据集的测试:

操作 基线 (p90) 启用 Skip Index (p90) 提升
range-auto-date-histo 2,099 ms 324 ms 6.5 倍更快
range-auto-date-histo-with-metrics 5,733 ms 3,928 ms 35% 更快

这些结果结合了范围查询的 Filter Rewrite 优化和自动日期直方图的 Skip Index,展示了两种技术如何互补。

索引大小影响

Skip Index 引入的存储开销极小:

配置 大小增加
@timestamp 字段 ~0.1%
所有数值字段 ~1%(例如 big5 上 22 GB → 23 GB)

由于这种极小的开销,OpenSearch 默认只在 @timestamp 字段上启用 Skip Index,在不显著增加存储成本的情况下提供针对性收益。


总结

Doc Value Skip Index 是 OpenSearch 聚合引擎的重要进步,解决了 Filter Rewrite 的根本限制。通过在文档值上构建多级摘要结构,它实现了:

  • 不相关字段的聚合优化:过滤字段和聚合字段可以不同
  • 子聚合支持:即使需要逐文档计算也能受益
  • 最高 28 倍性能提升:在合适的场景下
  • 极低存储开销:仅增加约 0.1%-1% 的存储

对于时序数据和分析工作负载,这是一个值得启用的强大优化。


原文作者:Ankit Jain(Amazon OpenSearch Service 软件工程师,Apache Lucene 和 OpenSearch 活跃维护者)、Asim Mahmood(AWS 高级软件工程师)

继续阅读 »

超越 Filter Rewrite:OpenSearch Doc Value Skip Index 如何让聚合查询快 28 倍

原文:https://opensearch.org/blog/beyond-filter-rewrite-how-doc-value-skip-indexes-accelerate-aggregations-in-opensearch/ 作者:Ankit Jain, Asim Mahmood (AWS/OpenSearch 团队)

前言

在之前的博客中,我们介绍了如何通过 Filter Rewrite 和多范围遍历技术,利用 Lucene 的 BKD 树实现高达 100 倍的日期直方图聚合性能提升。这些技术效果显著,但有一个根本限制:要求查询过滤字段和聚合字段必须是同一个,且无法支持复杂的子聚合逻辑。当过滤字段和聚合字段不同时,或者子聚合需要逐文档计算时,查询引擎只能退回到逐个扫描文档的传统方式。

本文将介绍如何通过 Lucene 10 的 Doc Value Skip Index 克服这些限制,即使过滤字段和聚合字段不相关,也能实现最高 28 倍 的聚合性能提升。


Filter Rewrite 的局限性

Filter Rewrite 将日期直方图聚合转换为一系列范围过滤器,然后使用 Lucene 的 BKD 树(Points 索引)来统计每个桶的文档数量,无需访问单个文档值。多范围遍历进一步优化了这一点,通过单次有序遍历处理所有桶。

这两种技术都依赖一个关键假设:查询中用于过滤的字段就是被聚合的字段

问题场景

考虑以下查询,它在 trip_distance 上过滤,但在 dropoff_datetime 上聚合:

{
  "size": 0,
  "query": {
    "range": {
      "trip_distance": {
        "gte": 0,
        "lte": 20
      }
    }
  },
  "aggs": {
    "dropoffs_over_time": {
      "date_histogram": {
        "field": "dropoff_datetime",
        "calendar_interval": "month"
      }
    }
  }
}

由于 trip_distancedropoff_datetime 是不同的字段,trip_distance 的 BKD 树无法提供 dropoff_datetime 值在各桶中分布的信息。引擎无法将聚合重写为对聚合字段的范围过滤器,只能退回到传统方法:遍历每个匹配的文档,获取其 dropoff_datetime 文档值,然后放入正确的桶中

同样,当子聚合需要逐文档计算时(例如计算每个时间桶内的平均值),Filter Rewrite 也无能为力,因为它只提供文档计数,无法访问单个字段值。

这些并非边缘情况。在实际分析查询中,很多场景是在一个维度上过滤,在另一个维度上聚合,子聚合在仪表板和报表中也很常见。我们需要一种更通用的方法。


Doc Value Skip Index:聚合引擎的新工具

从 Lucene 10.0 开始,一种新的索引结构可用:数值文档值上的可选 Skip Index,在 PR #13449 中引入,并在 PR #13563 中增加了多级支持。该结构通过 DocValuesSkipper 抽象暴露,为本文描述的优化提供了基础。

什么是 Skip Index?

在计算机科学中,跳表(Skip List)是一种概率数据结构,通过在有序数据上分层多个级别的链表,使用随机化决定哪些元素出现在更高层,从而提供期望 O(log n) 的搜索时间。

Lucene 的 Doc Value Skip Index 借用了多级概念,但完全是确定性的。它不使用随机化,而是在段创建时将文档划分为固定大小的区间,并在编译时构建摘要级别的层次结构。没有随机性:该结构完全由文档计数和配置的区间大小决定。

类比理解:就像书的目录。不需要逐页扫描来找到所需内容,先查看章标题,然后是节标题,最后缩小到具体页面。Skip Index 的工作原理相同:它让聚合引擎在决定是否检查单个值之前,先检查大块文档的摘要元数据。

Lucene 如何实现 Skip Index

Lucene 的实现使用最多 4 层(level 0-3)的层次结构。基础层(level 0)以 4,096 个文档为区间进行摘要。每个后续层聚合下一层的 8 个区间(2^3 的偏移),如下表所示:

Level 每个区间的文档数 说明
0 4,096 基础区间
1 32,768 (4,096 × 8) 每 8 个 level-0 区间
2 262,144 (4,096 × 64) 每 8 个 level-1 区间
3 2,097,152 (4,096 × 512) 每 8 个 level-2 区间

对于最坏情况下有 2^31 - 1(约 21 亿)个文档的索引,Skip Index 层次结构在 level 0 约有 524,288 个条目,level 1 有 65,536 个,level 2 有 8,192 个,level 3 有 1,024 个。因此,搜索空间从数十亿个文档减少到仅需几千次元数据检查。

每个区间条目非常紧凑(仅 29 字节),编码以下元数据:

  • 级别数(1 字节):该条目包含在多少个级别中
  • 最小值和最大值(16 字节):该区间内字段值的范围
  • 最小和最大文档 ID(8 字节):该区间的文档 ID 边界
  • 文档计数(4 字节):该区间内的文档数量

这些元数据使聚合引擎能够对每个区间做出三种决策:

  1. 如果区间的最小/最大值完全在当前聚合桶之外,跳过整个区间
  2. 如果区间的最小/最大值完全在单个桶内,批量计数所有文档,无需单独检查
  3. 如果区间跨越多个桶,退回到逐个处理文档

遍历从最高可用级别开始,仅在需要时下降,类似于之前优化中 BKD 树遍历跳过整个子树的方式。关键区别在于该结构操作的是文档值而非 Points 索引,因此无论查询过滤哪个字段都有效。

Skip Index 如何解决 Filter Rewrite 的局限性

回顾我们确定的两个主要限制:

  1. 不相关字段:Filter Rewrite 要求过滤字段和聚合字段相同
  2. 复杂子聚合:Filter Rewrite 只提供计数;无法支持桶内的逐文档计算

Skip Index 解决了这两个限制,因为它直接操作聚合字段的文档值,独立于匹配文档集的产生方式。查询引擎首先评估查询(使用适合过滤字段的索引)以产生一组匹配的文档 ID。然后,在聚合期间,它查询聚合字段的 Skip Index,以确定这些匹配文档的块是否可以跳过或批量计数。

这种解耦是关键洞察。Skip Index 不关心文档集是否被过滤过,它只需要知道聚合字段的值在每个区间内的分布情况。

示例

例如,考虑一个在 process.name 上过滤(词项过滤器)并在 @timestamp 上聚合(按天分桶的日期直方图)的查询:

GET /logs-*/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "range": {
            "@timestamp": {
              "gte": "2024-01-01",
              "lte": "2024-01-31"
            }
          }
        },
        {
          "term": {
            "process.name": "systemd"
          }
        }
      ]
    }
  },
  "aggs": {
    "daily_counts": {
      "date_histogram": {
        "field": "@timestamp",
        "calendar_interval": "day"
      }
    }
  }
}

没有 Skip Index 时,@timestamp 上的范围查询可能受益于 Filter Rewrite,但 process.name 上的词项过滤器阻止了查询被重写。引擎必须遍历匹配两个条件的每个文档,并单独检索每个 @timestamp 值。

启用 @timestamp 的 Skip Index 后,聚合引擎可以在收集期间查询 Skip Index。当它遇到一个区间,其中最小和最大 @timestamp 值都落在同一个日桶内时,它会批量计数该区间中所有匹配的文档,无需查找单个值。这可以将总查找次数减少约 85%

使用 Popcount 高效计数

批量计数步骤有一个值得探讨的细节。当 Skip Index 表明区间内的所有值都落在单个桶内时,我们知道批量计数是可能的;然而,我们不能直接使用区间的文档计数。Skip Index 覆盖段中的所有文档,但查询产生的 DocIdSetIterator 只代表匹配查询过滤器的文档子集。我们需要计算该过滤子集中有多少文档落在 Skip Index 区间内。

一种方法是逐个遍历迭代器,边遍历边计数。但这违背了跳过的大部分目的。相反,我们使用硬件级优化:popcount(人口计数)CPU 指令,它可以在单次操作中计算机器字中设置位的数量。

当查询引擎产生 DocIdSetIterator 时,它通常在内部将匹配文档集表示为位集,其中每个位对应一个文档 ID,如果文档匹配查询则设置为 1。要计算 Skip Index 区间内有多少匹配文档,我们提取该位集的相关部分(对应区间内最小/最大文档 ID 范围内的位),并对这些字应用 popcount。这给出了区间内的精确匹配文档数,无需逐个遍历。

DocIdSetIterator 还支持批量收集接口:收集器可以接收文档 ID 流,而不是重复调用 nextDoc()。当流中的所有文档 ID 都落在同一个桶内时(由 Skip Index 确定),整个流可以在一次操作中收集。初步基准测试显示,这种方法在 Skip Index 收益的基础上可额外带来最高 3 倍 的提升。


有序数据:Skip Index 的最佳场景

当文档按聚合字段排序时,Skip Index 发挥最佳性能。数据有序时,连续文档具有相似或单调递增的值,意味着每个 4,096 文档区间内的最小/最大范围较窄。窄范围更有可能完全落在单个聚合桶内,最大化批量计数的机会。

对于时序工作负载(日志分析、指标和可观测性),时间戳字段是自然的选择。大多数时序数据大致按时间顺序到达,OpenSearch 中的数据流保持这种顺序。

确保数据保持有序

仅仅有时间戳字段并不能保证数据在段合并和文档添加时保持有序。有两种方法可以保持排序:

1. Index Sort(推荐):配置显式的索引排序设置,确保无论段如何合并,数据都保持排序:

{
  "settings": {
    "index.sort.field": "@timestamp",
    "index.sort.order": "desc"
  }
}

这保证所有段保持时间戳顺序,提供一致的 Skip Index 性能。

2. Log Merge Policy:或者,使用保留传入文档顺序的合并策略。log_byte_size 合并策略倾向于保持时序数据典型的仅追加工作负载的时间顺序。

当数据无序时,Skip Index 仍然可以正常工作,但提供较少的跳过和批量计数机会,因为每个区间的最小/最大范围可能跨越多个桶。


启用 Skip Index

Skip Index 可以根据 OpenSearch 版本自动启用或通过手动配置启用。

按版本的默认行为

版本 行为
OpenSearch 3.2 为数值字段引入 skip_list 映射参数(默认:false
OpenSearch 3.3 对名为 @timestamp 的日期字段的日期直方图聚合自动启用 Skip Index
OpenSearch 3.4 将自动 Skip Index 优化扩展到 @timestamp 上的自动日期直方图聚合

手动配置

要在其他数值字段上启用 Skip Index,请在字段映射中添加 skip_list 参数:

PUT /my-index
{
  "mappings": {
    "properties": {
      "custom_timestamp": {
        "type": "date",
        "skip_list": true
      },
      "price": {
        "type": "long",
        "skip_list": true
      }
    }
  }
}

性能测试结果

我们使用 OpenSearch Benchmark 和 http_logsbig5 数据集测量了性能。结果显示了显著改进,特别是对于 Filter Rewrite 之前无法帮助的查询。

日期直方图性能(http_logs 工作负载)

基于 PR #19130 使用 http_logs 数据集的测试:

操作 基线 (p90) 启用 Skip Index (p90) 提升
hourly_agg_with_filter 998 ms 36 ms 28 倍更快
hourly_agg_with_filter_and_metrics 1,618 ms 970 ms 40% 更快

第一个操作展示了 Skip Index 在纯计数聚合与不相关过滤器上的全部优势。第二个操作包含子聚合(指标),改进较小,因为逐文档指标计算仍然需要指标字段的单个值查找。

日期直方图查询的吞吐量提高了 21%,对索引性能没有可测量的影响。

自动日期直方图性能(big5 工作负载)

OpenSearch 3.4 将 Skip Index 优化扩展到自动日期直方图聚合。基于 PR #20057 使用 big5 数据集的测试:

操作 基线 (p90) 启用 Skip Index (p90) 提升
range-auto-date-histo 2,099 ms 324 ms 6.5 倍更快
range-auto-date-histo-with-metrics 5,733 ms 3,928 ms 35% 更快

这些结果结合了范围查询的 Filter Rewrite 优化和自动日期直方图的 Skip Index,展示了两种技术如何互补。

索引大小影响

Skip Index 引入的存储开销极小:

配置 大小增加
@timestamp 字段 ~0.1%
所有数值字段 ~1%(例如 big5 上 22 GB → 23 GB)

由于这种极小的开销,OpenSearch 默认只在 @timestamp 字段上启用 Skip Index,在不显著增加存储成本的情况下提供针对性收益。


总结

Doc Value Skip Index 是 OpenSearch 聚合引擎的重要进步,解决了 Filter Rewrite 的根本限制。通过在文档值上构建多级摘要结构,它实现了:

  • 不相关字段的聚合优化:过滤字段和聚合字段可以不同
  • 子聚合支持:即使需要逐文档计算也能受益
  • 最高 28 倍性能提升:在合适的场景下
  • 极低存储开销:仅增加约 0.1%-1% 的存储

对于时序数据和分析工作负载,这是一个值得启用的强大优化。


原文作者:Ankit Jain(Amazon OpenSearch Service 软件工程师,Apache Lucene 和 OpenSearch 活跃维护者)、Asim Mahmood(AWS 高级软件工程师)

收起阅读 »