【问题标题】:Postgres query performance using row_number()使用 row_number() 的 Postgres 查询性能
【发布时间】:2018-08-26 13:57:29
【问题描述】:

假设有一个表customers,它有以下列:

  • customer_id 作为主键

  • 创作日期

  • 其他几列

我想使用 creation_date >= to_date('2000/01/01', 'YYYY/MM/DD') 查询客户表中的所有条目并插入到另一个表客户端中。 客户表中的条目数约为 1000 万,并且两个表都在同一个数据库中。 我想尽量减少复制这些数据的时间。

有两种方法:

  1. 对每个 n 并行运行以下查询,其中 n = 1、10001、20001... 以此类推

    insert into clients 
    values ( 
      select * 
      from ( 
        select *, row_number() over (ORDER BY customer_id) as rn 
        from ( 
          select * 
          from customers 
          where creation_date >= to_date('2000/01/01', 'YYYY/MM/DD')
        ) as sub1
      ) as sub2  
      where rn>=n limit 10000
    );
    
  2. 运行单个查询

    insert into clients 
    values ( 
      select * 
      from customers 
      where creation_date >= to_date('2000/01/01', 'YYYY/MM/DD')
    );
    

对于1,以下是执行计划

在客户端上插入(成本=0.42..113.55 行=10000 宽度=4506) -> 对“*SELECT*”进行子查询扫描(成本=0.42..113.55 行=10000 宽度=4506) -> 限制(成本=0.42..112.45 行=10000 宽度=616) -> sub2 上的子查询扫描(成本=0.42..57773.19 行=1000000 宽度=616) 过滤器:(sub2.rn >= 0) -> WindowAgg(成本=0.42..57579.79 行=1000000 宽度=624) -> 对客户使用“customer_id_pkey”进行索引扫描(成本=0.42..57347.71 行=1000000 宽度=616) 过滤器: ((creation_date >= to_date('2000/01/01'::text, 'YYYY/MM/DD'::text))

对于所有 n 并行执行,sub2 上的子查询扫描 将在整个数据上运行。因此,并行执行它已经破坏了目的。

我的理解是否正确,或者接近 1 所花费的时间比接近 2 的时间要短。

另外请建议是否有提高查询性能的方法。

[计划节点的值已被修改,因此请忽略行、成本和宽度值]

【问题讨论】:

  • 数据库大小是相对的。如果您的服务器相当强大,或者负载较低,或者记录大小较小,则 1000 万不一定很大。如果 其中任何一个 是真的,那么你可能真的想多了。如果 没有 为真,您可能需要更多资源用于您的数据库服务器。
  • 如果查询返回多于一行,插入将不起作用。如果您想基于 select 使用 insert 删除 values 子句 - 只允许单行。

标签: sql postgresql


【解决方案1】:

这更像是一个扩展评论。

您真的应该尝试不同的方法,看看哪种方法效果最好。就个人而言,我几乎会采用第二种方法,尽管我会将其写为:

insert into clients ( . . .)
    select c.*
    from customers c
    where creation_date >= date '2000-01-01';

为什么?对表执行多个inserts 会产生表争用、日志争用和可能的索引争用。这可能会减慢速度。我不是说它会 - 只是不去想它,我只是开始查询并等待它需要多长时间(一杯咖啡?午餐?过夜?)

如果您的数据真的很大,那么您可能需要考虑对数据进行分区。如果是这种情况,那么多个同时插入是个好主意——只要它们都进入一个分区。

顺便说一句,在您的第一个版本中,您不需要使用row_number()。您可以使用offset/fetch 语法来获取不同的数据。

【讨论】:

  • 方法2中的查询与您提到的有何不同?
  • @Geeta:没有什么不同,只是另一种指定日期的方式。当然首选版本应该是标准 SQL 日期文字:DATE '2000-01-01' :-)
  • 谢谢,我这里还有一个问题,所以分区数据的一种方法是使用偏移量和限制并并行运行插入,但要做到这一点,我必须获得条目的总数,即将扫描整个表。另一种方法是使用索引列,比如创建日期,并基于它,跨线程拆分数据以进行并行插入。这个解决方案会表现更好吗?
猜你喜欢
  • 2012-11-06
  • 1970-01-01
  • 1970-01-01
  • 2021-05-04
  • 2023-03-30
  • 1970-01-01
  • 2018-01-09
  • 1970-01-01
  • 2013-05-02
相关资源
最近更新 更多