You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PostgreSQL多Embedding向量文章推荐查询性能优化求助

优化基于多Embedding余弦相似度的TopN推荐查询性能

核心问题诊断

1000篇文章计算耗时超30秒,核心瓶颈集中在:

  • PL/pgSQL循环的串行计算开销极大,无法利用PostgreSQL的集合运算优化
  • 余弦相似度的重复计算(尤其是L2范数部分)浪费资源
  • 未读文章过滤条件未建立高效索引
  • 多Embedding的最大值平均逻辑未做计算顺序优化

可行优化方案

1. 用集合式SQL替代PL/pgSQL循环

PL/pgSQL的FOR循环在处理批量数据时性能极差,改用集合运算将多Embedding展开为行级计算,大幅提升效率。假设表结构如下:

  • users: user_id, interest_embeddings(double precision数组的数组)
  • articles: article_id, content_embeddings(double precision数组的数组)
  • user_unread: user_id, article_id, created_at

优化后的查询逻辑:

WITH user_emb_with_norm AS (
    SELECT 
        user_id,
        emb AS u_emb,
        SQRT(SUM(emb[i]^2 FOR i IN 1..array_length(emb,1))) AS u_norm
    FROM users, unnest(interest_embeddings) emb
    WHERE user_id = $1
),
article_candidates AS (
    SELECT 
        a.article_id,
        emb AS a_emb,
        SQRT(SUM(emb[i]^2 FOR i IN 1..array_length(emb,1))) AS a_norm
    FROM articles a
    LEFT JOIN user_unread ur 
        ON a.article_id = ur.article_id 
        AND ur.user_id = $1 
        AND ur.created_at >= NOW() - INTERVAL '5 days'
    WHERE ur.article_id IS NULL -- 过滤近5天已读
    , unnest(a.content_embeddings) emb
),
pair_similarities AS (
    SELECT 
        u.user_id,
        a.article_id,
        -- 余弦相似度计算:点积/(范数乘积)
        (SUM(u.u_emb[i] * a.a_emb[i] FOR i IN 1..array_length(u.u_emb,1))) 
        / (u.u_norm * a.a_norm) AS cos_sim
    FROM user_emb_with_norm u
    CROSS JOIN article_candidates a
    WHERE array_length(u.u_emb,1) = array_length(a.a_emb,1) -- 保证维度一致
    GROUP BY u.user_id, a.article_id
),
max_sim_per_article AS (
    SELECT user_id, article_id, MAX(cos_sim) AS max_cos_sim
    FROM pair_similarities
    GROUP BY user_id, article_id
),
final_scores AS (
    SELECT user_id, article_id, AVG(max_cos_sim) AS final_score
    FROM max_sim_per_article
    GROUP BY user_id, article_id
)
SELECT article_id, final_score
FROM final_scores
ORDER BY final_score DESC
LIMIT 20;

2. 预计算Embedding的L2范数

将余弦相似度中的范数计算提前存储,避免每次查询重复计算:

-- 给用户表添加范数列并初始化
ALTER TABLE users ADD COLUMN interest_norms double precision[];
UPDATE users 
SET interest_norms = ARRAY(
    SELECT SQRT(SUM(emb[i]^2 FOR i IN 1..array_length(emb,1)))
    FROM unnest(interest_embeddings) emb
);

-- 给文章表添加范数列并初始化
ALTER TABLE articles ADD COLUMN content_norms double precision[];
UPDATE articles 
SET content_norms = ARRAY(
    SELECT SQRT(SUM(emb[i]^2 FOR i IN 1..array_length(emb,1)))
    FROM unnest(content_embeddings) emb
);

后续查询时直接调用预存的范数,减少计算量。

3. 优化未读过滤的索引

针对user_unread表的过滤条件建立复合索引:

CREATE INDEX idx_user_unread_user_created ON user_unread(user_id, created_at DESC);

该索引可快速定位用户近5天的已读文章,减少候选文章数量。

4. 缩小候选集范围(业务允许时)

如果不需要全量1000篇文章计算,可先通过分类、热度等维度预筛选200-500篇候选,再计算相似度,直接降低计算规模。

5. 启用并行计算

PostgreSQL 10+支持并行查询,开启后可利用多核资源加速计算:

SET max_parallel_workers_per_gather = 4; -- 根据服务器核数调整

可行性结论

该场景完全可行,优化后1000篇文章的计算耗时可压缩至1秒以内。之前的30秒耗时主要源于PL/pgSQL循环的串行低效和重复计算,通过上述优化可彻底解决性能问题。

内容的提问来源于stack exchange,提问作者user2779450

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 07:07:39