zhiqiang-hhhh commented on PR #63666:
URL: https://github.com/apache/doris/pull/63666#issuecomment-4565309297

   可以,重新梳理一版,只讲 slot index 这个点。
   
   **1. 两套编号怎么来的**
   
   假设表:
   
   ```sql
   CREATE TABLE t (
       id int,
       pad_int int,
       pad_text string,
       embedding array<float>,
       value int,
       INDEX idx_embedding(`embedding`) USING ANN ...
   );
   ```
   
   存储层的 `storage cid` 基本来自 tablet schema 的列顺序,写 segment 时会写进 column meta:
   
   ```text
   storage cid:
   0 -> id
   1 -> pad_int
   2 -> pad_text
   3 -> embedding
   4 -> value
   ```
   
   而 `schema index` 是当前这次 scan 的 reader schema / block 
里的列位置。它不是固定表列编号,而是“当前读出来的第几列”。
   
   比如查询:
   
   ```sql
   SELECT id
   FROM t
   WHERE l2_distance_approximate(embedding, [0,0,0]) < 1.0;
   ```
   
   这次只需要 `id` 和 `embedding`:
   
   ```text
   reader schema index: 0   1
   reader column:       id  embedding
   storage cid:         0   3
   ```
   
   所以当前 `_schema->column_ids()` 是:
   
   ```cpp
   idx_to_cid = [0, 3]
   ```
   
   表达式 rebind 之后,`embedding` 的 `SlotRef::column_id()` 会被改成当前 reader schema 位置:
   
   ```text
   embedding slot_ref->column_id() = 1
   ```
   
   ANN prepare 时保存的就是这个值:
   
   ```cpp
   range_search_runtime.src_col_idx = slot_ref->column_id(); // 1
   ```
   
   所以这里:
   
   ```text
   src_col_idx = 1              // schema index / block index
   idx_to_cid[src_col_idx] = 3  // storage cid
   ```
   
   **2. 旧代码为什么 index-only scan 没 work**
   
   ANN range search 本身查 index 时是对的:
   
   ```cpp
   src_col_cid = idx_to_cid[src_col_idx]; // idx_to_cid[1] = 3
   ```
   
   也就是说它能正确用 `embedding` 的 ANN index 过滤 row bitmap。
   
   问题发生在过滤完以后,Doris 想标记:
   
   ```text
   embedding 这个 common expr 已经被 index 处理过了。
   如果 embedding 不输出,只用于这个 ANN predicate,就不用再读 embedding 数据列。
   ```
   
   状态 map 大概长这样:
   
   ```cpp
   _common_expr_index_exec_status[3][expr] = false;
   _common_expr_to_slotref_map[ctx][1] = expr;
   ```
   
   注意:
   
   ```text
   _common_expr_index_exec_status 用 storage cid: 3
   _common_expr_to_slotref_map 用 schema index: 1
   ```
   
   旧代码却这样查:
   
   ```cpp
   auto src_col_idx = 1;
   ColumnId cid = idx_to_cid[src_col_idx]; // 3
   
   if (slot_ref_map.find(cid) == slot_ref_map.end()) {
       return Status::OK();
   }
   ```
   
   它拿 `3` 去查 `slot_ref_map`,但 `slot_ref_map` 的 key 是 `1`,所以查不到,直接 
return。后面的状态标记没有执行。
   
   于是状态仍然是:
   
   ```cpp
   _common_expr_index_exec_status[3][expr] = false;
   ```
   
   后续 `_check_all_conditions_passed_inverted_index_for_column(3, ...)` 会认为 
`embedding` 上的 expr 没有完全被 index 覆盖,因此不能跳过读取 `embedding` 数据列。
   
   结果就是:
   
   ```text
   ANN index 已经过滤了 row bitmap,
   但 embedding 还是会被读出来再计算表达式。
   ```
   
   所以之前不是结果错,而是 **index-only scan 优化没有生效**。
   
   正确修法是保持坐标系一致:
   
   ```cpp
   src_col_idx = 1;
   
   slot_ref_map.find(src_col_idx);              // 用 schema index 找 expr
   set_true_for_index_status(expr, src_col_idx); // 也传 schema index
   ```
   
   `set_true_for_index_status` 内部再做:
   
   ```cpp
   column_id = _col_ids[src_col_idx]; // _col_ids[1] = 3
   _expr_index_status[3][expr] = true;
   ```
   
   最终状态变成:
   
   ```cpp
   _common_expr_index_exec_status[3][expr] = true;
   ```
   
   这样当查询只是:
   
   ```sql
   SELECT id
   FROM t
   WHERE l2_distance_approximate(embedding, [0,0,0]) < 1.0;
   ```
   
   且 `embedding` 不需要输出时,ANN index 已经满足 predicate,BE 就可以不再读取 `embedding` 
数据列,index-only scan 才真正 work。


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to