【问题标题】:Postgres function optimisationPostgres 功能优化
【发布时间】:2015-03-28 21:46:46
【问题描述】:

我正在运行以下功能。使用小得多的表对其进行测试可以按预期工作(18 行 - 约 400 毫秒)。但是,当指向我的真实数据(315000 行)时,它运行了 48 小时并且仍在运行。这比我在线性外推下的预期要长得多。

  1. 有没有更好的方法来做到这一点?
  2. 有没有办法测试它在运行时是否在做它应该做的事情?

有没有办法优化下面的功能?

DO 
$do$ 
DECLARE r public.tablex%rowtype; 
BEGIN 
FOR r IN SELECT id FROM public.tablex 
LOOP 
IF (select cast((select trunc(random() * 6 + 1)) as integer) = 5) THEN 
UPDATE public.tablex SET test='variable1' WHERE id = r.id; 
ELSIF (select cast((select trunc(random() * 6 + 1)) as integer) = 6) THEN 
UPDATE public.tablex SET test='variable2' WHERE id = r.id; 
END IF; 
END LOOP; 
RETURN; 
END 
$do$;

【问题讨论】:

    标签: performance postgresql random sql-update plpgsql


    【解决方案1】:

    @kordirko 明确表示您的DO 声明是不必要的昂贵。但还有更多。

    概率

    你更新的概率不是均匀分布的:

    • 1/6 或所有行都更新为'variable1'
    • 但只有 5/36 ((1/6) * (5/6)) 更新为 'variable2'

    从您的其余代码来看,我将假设这是一个意外错误,您希望每个人获得相等的 1/6 份额

    消毒

    您可以简化为:

    UPDATE tablex
    SET    test = CASE trunc(random() * 6)
                     WHEN  float '4' THEN 'variable1'
                     WHEN  float '5' THEN 'variable2'
                     ELSE  test
                  END;
    
    • 结果加 1 是没有意义的。而是比较 45 而不是 56(或 31 - 这里没有区别)。

    • double precision (= float) 常量相比要便宜一些,而不是将表达式trunc(random() * 6)double precision 结果转换为每一行的integer,就像它会发生在您的原始代码中一样.

    更好,但 仍然非常低效

    高级

    UPDATE tablex
    SET    test = CASE WHEN random() >= float '0.5' THEN 'variable1'
                                                    ELSE 'variable2' END
    WHERE  random() >= float '0.6666667';
    

    100 % 等效(除非您有触发器 ON UPDATE 或一些奇特的设置),但 要快得多,因为只有实际接收更新的行才会被触及。三分之二的行根本没有被触及。

    • 请注意,两个random() 调用是完全独立的。

    • 如果您想尽可能精确地了解三分之二的概率,请使用 2 / float '3.0' 而不是 float '0.6666667'。但这确实是学术上的差异。

    • 您可能希望在运行此之前(在同一事务中)LOCK tablex IN ROW EXCLUSIVE MODE 以排除并发写入的竞争条件。 Details in the manual.

    另类

    如果test 已经可以保存目标值“variable1”或“variable2”之一(在很多情况下),这会更便宜,但是:

    UPDATE tablex t
    SET    test = upd.val
    FROM  (
       SELECT id, CASE WHEN random() >= float '0.5' THEN 'variable1'
                                                    ELSE 'variable2' END AS val
       FROM   tablex
       WHERE  random() >= float '0.6666667'
       -- ORDER  BY id   -- the last two lines only to defend against ...
       -- FOR    UPDATE  -- ... concurrent writes and possible deadlocks
       ) upd
    WHERE  t.id = upd.id
    AND    t.test IS DISTINCT FROM upd.val;
    

    避免更多的空更新。
    如果test 定义为NOT NULL,您可以简化为:

     AND    t.test <> upd.val;
    

    比较这个相关答案的最后一章:

    【讨论】:

    • 谢谢。非常有用。确实是意外错误
    • 我只是想成功运行查询 - 最后花了我大约 86000 毫秒。
    • @user2263513:86 秒是超过 48 小时的显着改进。但通常我希望这在一个体面的服务器上会更快,对于 300k 行最多几秒钟 - 除非其他事情会减慢你的速度,比如非常宽的行或昂贵的索引或触发器或并发负载......你做了哪个变种申请?你锁桌子了吗?
    • UPDATE tablex SET test = CASE WHEN random() >= float '0.5' THEN 'variable1' ELSE 'variable2' END WHERE random() >= float '0.6666667';你选择了它,我也有一些活动触发器会减慢速度
    【解决方案2】:

    尝试简单的更新:

    UPDATE tablex
    SET  test =  CASE trunc(random() * 6) + 1
                     WHEN  5 THEN 'variable1'
                     WHEN  6 THEN 'variable2'
                     ELSE test
           END
    ;
    

    我猜它至少会快 50~200 倍。

    【讨论】:

      猜你喜欢
      • 2013-07-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-02
      • 2018-12-25
      • 2021-07-29
      相关资源
      最近更新 更多