本文介绍如何进行向量查询和融合查询。

暴力查询

假设有一个带条件的拍图查找商品需求:查找与输入图片相似度最高,价格在100到200元之间,上架时间在最近一个月以内的前100件商品。

设计查询SQL如下:

SELECT id, price FROM products WHERE 
    price > 100 AND price <= 200 
    AND InTime > '2019-03-01' AND InTime <= '2019-03-31' 
ORDER BY 
    l2_distance(array[10,2.0,…, 512.0], feature) 
LIMIT 100;
说明 示例中是完全标准的SQL。执行过程为:先过滤出符合价格和时间条件的商品,再对其依次与输入点做L2计算,最后将计算结果排序,取前100条输出。这种SQL适合数据量小,对召回要求特别高的场景。这种格式的SQL不会生成使用向量索引的执行计划,因此称为暴力查询。

使用向量索引加速向量查询

如果要使用向量索引加速查询,则Order by部分必须使用 “向量列 <-> 向量” 语法代替距离UDF的调用。例如,对同样的查询需求,有如下SQL:

SELECT id, price FROM products WHERE
    price > 100 AND price <= 200 
    AND InTime > '2019-03-01' AND InTime <= '2019-03-31' 
ORDER BY 
    feature <-> array[10,2.0,…, 512.0] 
LIMIT 100;
使用向量索引,对Order by子句的写法有一定要求:
  • 如果要使用向量索引,配合向量查询的order by排序方向必须为ASC或不填。如果需要根据距离降序排序,或在使用向量索引后,再根据其他列排序输出,请将向量查询作为子查询,在父查询中添加想要的order by语句。
  • 如果要使用向量索引,order by语句只能包含 “向量列 <-> 查询向量”这样一个条件。否则不能利用向量索引的加速能力。

向量结构化融合查询

AnalyticDB PostgreSQL版向量分析在基本的向量查询基础上,还支持向量和结构化条件的融合查询。仍以如下SQL为例:

SELECT id, price FROM products WHERE 
    price > 100 AND price <=200 
    AND InTime > '2019-03-01 00:00:00' AND InTime <= '2019-03-31 00:00:00' 
ORDER BY 
    feature <-> array[10,2.0,…, 512.0] 
LIMIT 100;

为了执行这条SQL,依据结构化条件列(price以及intime)上是否有索引,以及结构化条件的选择率大小不同,AnalyticDB PostgreSQL版向量分析可能会生成3类共5种不同的执行计划,并在其中选择最优的一个执行计划,以满足用户对性能和召回的要求:

第一类:暴力查询

根据结构化条件获取所有符合条件的行,再在其中根据向量距离进行排序,选择距离最小的100条输出。这种执行计划召回为100%,但是最慢,在底库过大或符合结构化条件行数过多时查询性能低下。执行计划如下(简化部分输出信息):

                                       QUERY PLAN
-----------------------------------------------------------------------------------
 Limit
     ->  Gather Motion 3:1  (slice1; segments: 3)   
         Merge Key: (l2_distance($0, feature))     
             ->  Limit
                 ->  Sort
                     Sort Key: (l2_distance($0, feature))
                     ->  Index Scan using products_price_idx on products
                         Index Cond: 价格过滤条件
                         Filter: 时间过滤条件

 Optimizer: Postgres query optimizer
第二类:纯向量查询 + 结构化过滤

为了加速查询,先使用向量索引查询出与输入图片最接近的N行数据,再在其中根据结构化条件进行过滤。这种执行计划的优点是速度最快。缺点是如果结构化查询的筛选率太小(即一行数据通过过滤的概率太小),则查询最终输出的数据行数可能比用户要求的limit小。执行计划如下(简化部分输出信息):

                                       QUERY PLAN
-----------------------------------------------------------------------------------
 Limit 
    ->  Gather Motion 3:1  (slice1; segments: 3) 
        Merge Key: ((feature <-> $0))     
            ->  Limit
                ->  Ann Index Scan using products_feature_idx on products
                    Order By: (feature <-> $0)
                    Filter: 价格和时间列过滤条件

 Optimizer: Postgres query optimizer
第三类:向量结构化融合查询

向量结构化融合查询结合了第一类和第二类执行计划,既能使用索引,又能解决第二类执行计划的“返回数据变少”的问题。同时,如果结构化条件列上有索引,且索引类型支持Bitmap生成,则融合查询还可以利用其他索引来生成Bitmap,从而进一步加速融合查询。执行计划如下(简化部分输出信息):

                                       QUERY PLAN
-----------------------------------------------------------------------------------
 Limit
     ->  Gather Motion 3:1  (slice1; segments: 3)   
         Merge Key: ((feature <-> $0))   
         ->  Limit
             ->  Fusion Ann Scan
                 ->  Bitmap Index Scan on products_price_idx
                     Index Cond: 价格过滤条件
                     ->  Ann Index Scan with filter using products_feature_idx on products
                         Order By: (feature <-> $0)
                         Filter: 时间过滤条件

 Optimizer: Postgres query optimizer
凡是explain输出的执行计划包含“Fusion Ann Scan”节点或“Ann Index Scan with filter”节点,都算是启用了融合查询能力。简要说明如下:
  • Ann Index Scan with filter:此节点的作用是将过滤条件下压到向量索引内部,在索引的执行过程中同时考虑过滤条件。
  • Fusion Ann Scan:此节点再某些结构化条件列上有索引时可能出现,其作用是根据结构化条件生成Bitmap(左子树),将Bitmap下压到向量索引(右子树)中,加速结构化条件的计算。出现Fusion Ann Scan节点时,其右子树可能是Ann Index Scan也可能是Ann Index Scan withfilter。如果右子树是Ann Index Scan,说明除了Bitmap外没有其他结构化条件下压。反之会出现Ann Index Scan withfilter。

AnalyticDB PostgreSQL版会在保证召回的情况下,选取代价最小的执行计划。