更新时间:2026-10-09 GMT+08:00
分享

Lance Late Read调优说明

Late Read(延迟读)是Lance外表针对向量top-k查询的性能优化特性。该特性通过将数据扫描拆分为“早读阶段”和“延迟读”两个阶段,显著减少宽列(如图片、长文本、原始向量)的无效读取量,从而提升查询性能。

在普通计划下,SELECT <宽列> FROM t ORDER BY vec <-> q LIMIT k需要把全表所有候选行的全部投影列读出来,再排序取top-k。宽列(图片、bytea、长文本、原始向量)在排序前被读取,绝大部分随后被丢弃。

Late Read把扫描分为两步:

  1. 早读阶段:只读_rowid、xc_node_id、_distance三个系统列,加上WHERE谓词列/JOIN key列,走IVF索引产出候选集,排序取top-k。
  2. 延迟读阶段:把top-k的_rowid按xc_node_id路由回数据所在DN(RESTORE流),只对这k行调用Lance take_rows取回宽列。宽列的读取量从O(候选行数 × 宽列宽度) 降到O(k × 宽列宽度)。

Late Read执行策略

Late Read支持两种执行策略,您可以通过参数指定或由优化器自动选择。

  • 一阶段(single_phase)

    一次Late Read把向量列和其他宽列一起取回。

    图1 一阶段Late Read计划示意图

    特点:计划层次浅、只有一次RESTORE+一次take_rows,但宽列是为粗排候选集取的,其中会有一部分行在后续精排中被淘汰,这部分宽列IO是浪费的。

  • 二阶段(two_phase)

    把延迟读拆成两次,先只取向量列做精排定稿,再只为最终k行取宽列。

    图2 二阶段Late Read计划示意图

    特点:宽列严格只为最终k行读取,宽列IO最省,代价是多一轮RESTORE流和一次Python侧take_rows调用。只有当非向量宽列足够宽、足够多时才划算。

GUC参数说明

late_read_strategy
  • 参数说明:设置Lance DFS表top-k查询的Late Read策略。取值为列表,其中none/single_phase/two_phase/auto四个策略选项互斥,nested_late_read和join_late_read分别为独立开关。
  • 取值范围:
    • none:关闭Late Read。走原始计划,所有投影列在扫描阶段一次读全。
    • auto:由优化器按代价与列宽自动在none、single_phase、two_phase之间选择。
    • single_phase:规则强制走一阶段,跳过收益检查。
    • two_phase:规则强制走二阶段,跳过收益检查。
    • nested_late_read:独立开关,允许Late Read在嵌套查询块(子查询、CTE)中生效。
      • 未设置时(默认):子查询和CTE内部的查询不走Late Read。
      • 设置后:每个查询块独立判定Late Read,子查询和CTE内部的top-k查询可以走Late Read。
    • join_late_read:独立开关,允许Late Read跨INNER JOIN生效(仅支持hashjoin)。设置后,Late Read仍需同时满足以下全部条件方可生效:
      • 所有连接均为INNER JOIN(存在outer/semi/anti join则回退为普通计划)。
      • 查询中有且仅有一个Lance top-k驱动表,且查询中Lance表数量与之相等。
      • 驱动表存在等值连接条件。
      • 无聚集、GROUP BY、DISTINCT、集合操作。
  • 默认值:none。
enable_topk_compensation
  • 参数说明:设置是否开启Lance ANN扫描的top-k补偿读。开启后,Lance ANN扫描使用基于filter_ratio的k值计算和while-loop补偿机制,确保WHERE过滤后仍能返回LIMIT n行。
  • 取值范围:
    • on:开启后,会启动while-loop补偿机制,确保最终输出的行数满足k值要求。
    • off:关闭时,当where过滤率比较大时,可能输出远小于k值的行数。
  • 默认值:off。

相关文档