优化查询,将表A中的随机行插入表B中,不重复

问题描述 投票:0回答:1

我还是 SQL 新手,使用 PostgreSQL 16.2。

create table users(
    user_id serial primary key
);
create table task_pool(
    task_id serial primary key,   
    order_id integer,
    clicker_id integer,
    INDEX idx_order_id (order_id)
);
create table task_dispersal(
    id serial primary key,
    order_id integer,
    to_disp integer
);

users 有 100,000 行(因此有 100,000 个 user_ids);
task_分散中有 1,000 行,每行 to_disp 为 10。
从任务池中的 0 行开始。

我想迭代

task_dispersal
中的行。对于每一行...

  • users
    表中获取 to_disp 数量的随机 user_id,这些随机 user_id 尚未包含在
    task_pool
    中以及迭代行的相关 order_id 中。

  • 例如,如果

    task_dispersal
    中的某行 order_id 1 和 to_disp 10,它将检查
    task_pool
    中 order_id 1 的所有行,从这些行中获取 user_id 并从用户表中过滤它们,然后选择10 个随机行(user_id)插入到
    task_pool

我正在使用这个查询:

INSERT into task_pool(clicker_id, order_id)
SELECT U.user_id, TD.id
FROM task_dispersal TD
CROSS JOIN LATERAL (
    SELECT Us.user_id
    FROM users Us
    WHERE user_id NOT IN (
        SELECT clicker_id
        FROM task_pool
        WHERE order_id = TD.id
    )
    ORDER BY RANDOM()
    LIMIT TD.to_disp
) U

它可以工作,而且相对较快,但我仍然希望有一种方法可以优化它,因为在我的(非常弱的)托管数据库上需要 26 秒,而我正在尝试大约几秒钟。

这是查询计划:

Insert on public.task_pool  (cost=6586.23..674875.51 rows=0 width=0) (actual time=26540.859..26540.860 rows=0 loops=1)
  Buffers: shared hit=32262 dirtied=17 written=18
  ->  Nested Loop  (cost=6586.23..674875.51 rows=500000 width=24) (actual time=1929.910..26299.971 rows=1000 loops=1)
        Output: nextval('task_pool_task_id_seq'::regclass), td.order_id, us.user_id, CURRENT_TIMESTAMP, 0
        Buffers: shared hit=28619
        ->  Seq Scan on public.task_dispersal td  (cost=0.00..3.00 rows=100 width=8) (actual time=0.007..0.140 rows=100 loops=1)
              Output: td.id, td.order_id, td.reset_time, td.disp_remaining, td.daily_disp, td.disp_interval, td.next_disp_time, td.expired_tasks, td.to_disp
              Buffers: shared hit=2
        ->  Limit  (cost=6586.23..6598.73 rows=5000 width=12) (actual time=249.542..249.543 rows=10 loops=100)
              Output: us.user_id, (random())
              Buffers: shared hit=27607
              ->  Sort  (cost=6586.23..6711.23 rows=50000 width=12) (actual time=249.538..249.539 rows=10 loops=100)
                    Output: us.user_id, (random())
                    Sort Key: (random())
                    Sort Method: top-N heapsort  Memory: 25kB
                    Buffers: shared hit=27607
                    ->  Index Only Scan using users_pkey on public.users us  (cost=2.96..2181.57 rows=50000 width=12) (actual time=3.846..138.345 rows=100000 loops=100)
                          Output: us.user_id, random()
                          Filter: (NOT (hashed SubPlan 1))
                          Heap Fetches: 0
                          Buffers: shared hit=27607
                          SubPlan 1
                            ->  Index Scan using idx_order_id on public.task_pool task_pool_1  (cost=0.15..2.63 rows=16 width=4) (actual time=0.003..0.003 rows=0 loops=100)
                                  Output: task_pool_1.clicker_id
                                  Index Cond: (task_pool_1.order_id = td.order_id)
                                  Buffers: shared hit=106
Settings: effective_io_concurrency = '200', random_page_cost = '1.1', effective_cache_size = '192MB', max_parallel_workers = '1', max_parallel_workers_per_gather = '1', work_mem = '1703kB'
Query Identifier: 3069500943293269296
Planning:
  Buffers: shared hit=18 read=2
Planning Time: 0.275 ms
JIT:
  Functions: 22
  Options: Inlining true, Optimization true, Expressions true, Deforming true
  Timing: Generation 1.054 ms, Inlining 135.482 ms, Optimization 1098.077 ms, Emission 399.930 ms, Total 1634.542 ms
Execution Time: 26541.848 ms

我真的不知道如何阅读查询计划,但我很确定对不在带有 order_id 的 task_pool 中的

ORDER BY RANDOM()
结果使用
user_ids
成本最高?


我不认为我可以使用

tablesample bernoulli
users
表中获取随机用户;因为它选择的一些/所有样本行可能已经在task_pool中,所以它将插入少于所需数量(to_disp)的user_ids(这种情况在执行查询几次后经常发生)

如果我可以在

的结果上使用表样本伯努利
WHERE user_id NOT IN (
        SELECT clicker_id
        FROM task_pool
        WHERE order_id = TD.order_id
    )

那就太棒了;不幸的是它只能用在表、临时表或物化视图上,并且不能在

WHERE
之后使用。

我尝试制作每行未使用的 user_ids 的临时表,然后

tablesample bernoulli
(当每行有多达 100,000 个未使用的 user_ids 时非常慢)

目前,我认为除了升级服务器之外,没有什么可以做的优化。还有更好的想法吗?

sql postgresql random postgresql-performance
1个回答
0
投票

随机重新排列以避免横向,可能会有所帮助......

WITH
   randomised AS
(
   SELECT
      U.user_id, TD.id, TD.to_disp
      ROW_NUMBER() OVER (
         PARTITION BY TD.id
             ORDER BY RANDOM()
      )
         AS selection_id
   FROM
      task_dispersal TD
   CROSS JOIN
      users          U
   WHERE
      NOT EXISTS (
         SELECT *
           FROM task_pool
          WHERE order_id = TD.id
           AND clicker_id = U.user_id
      ) 
)
SELECT
  user_id, id
FROM
  randomised
WHERE
  selection_id <= to_disp
© www.soinside.com 2019 - 2024. All rights reserved.