【问题标题】:Update using join on big table - performance tips?在大表上使用连接更新 - 性能提示?
【发布时间】:2020-09-01 13:17:31
【问题描述】:

一直在为这个更新而苦苦挣扎,永远不会结束

update votings v
set voter_id = (select pv.number from voters pv WHERE pv.person_id = v.person_id);

当前表有 96M 记录

select count(0) from votings;
  count   
----------
 96575239
(1 registro)

更新显然是使用索引

explain update votings v                             
set voter_id = (select pv.number from voters pv WHERE pv.rl_person_id = v.person_id);
                                                    QUERY PLAN                                                     
-------------------------------------------------------------------------------------------------------------------
 Update on votings v  (cost=0.00..788637465.40 rows=91339856 width=1671)
   ->  Seq Scan on votings v  (cost=0.00..788637465.40 rows=91339856 width=1671)
         SubPlan 1
           ->  Index Scan using idx_voter_rl_person_id on voters pv  (cost=0.56..8.58 rows=1 width=9)
                 Index Cond: (rl_person_id = v.person_id)
(5 registros)

这是我的投票索引

Índices:
    "votings_pkey" PRIMARY KEY, btree (id)
    "votings_election_id_voter_id_key" UNIQUE CONSTRAINT, btree (election_id, person_id)
    "votings_external_id_external_source_key" UNIQUE CONSTRAINT, btree (external_id, external_source)
    "idx_votings_updated_at" btree (updated_at DESC)
    "idx_votings_vote_party" btree (vote_party)
    "idx_votings_vote_state_vote_party" btree (vote_state, vote_party)
    "idx_votings_voter_id" btree (person_id)
Restrições de chave estrangeira:
    "votings_election_id_fkey" FOREIGN KEY (election_id) REFERENCES elections(id)
    "votings_voter_id_fkey" FOREIGN KEY (person_id) REFERENCES people_all(id)

伙计们,有什么想法在更新运行缓慢方面发挥了最大的作用?正在使用的行数或连接数?

【问题讨论】:

  • 与您的问题无关,但是:count(0) 实际上比 Postgres 中的 count(*) 慢(并且没有 DBMS,count(0) 确实更快)
  • 很高兴知道!通常我使用 (0) 或 (1) 从来没有考虑过
  • 如果update votings v set voter_id = pf.number from from voters pv WHERE pv.person_id = v.person_id;更快,你可以试试
  • 从解释中,确实改进了,会尝试执行它,谢谢@a_horse_with_no_name

标签: sql postgresql database-performance database-administration


【解决方案1】:

更新表中的所有行会非常昂贵。我建议重新创建表格:

create temp_votings as
    select v.*, vv.vote_id
    from votings v join
         voters vv
         on vv.person_id = v.person_id;

对于此查询,您需要votes(person_id, vote_id) 上的索引。我猜person_id 可能已经是主键了;如果是这样,则不需要额外的索引。

然后,您可以替换现有的表——但先备份它:

truncate table votings;

insert into votings ( . . . )    -- list columns here
    select . . .                 -- and the same columns here
    from temp_votings;

【讨论】:

    【解决方案2】:

    如果您实际上在 voting 中有 91339856 行,则对 voters 的 91339856 索引扫描肯定是主要的成本因素。顺序扫描会更快。

    如果您不强制 PostgreSQL 进行嵌套循环连接,您可能会提高性能:

    UPDATE votings
    SET voter_id = voters.number
    FROM voters
    WHERE votings.person_id = voters.person_id;
    

    【讨论】:

    • 我能够像这样运行查询,虽然它仍然需要很长时间,但最好是这样。我想在这种情况下重新创建表格将是提高速度的理想方式。
    • 是的,很可能是这样。增加work_mem 也会有所帮助。
    【解决方案3】:

    我可以在这里提出的一个建议是使用覆盖索引进行子查询查找:

    CREATE INDEX idx_cover ON voters (person_id, number);
    

    虽然在 select 的上下文中,这可能不会比您当前在 person_id 上的索引更有优势,但在更新的上下文中它可能更重要。原因是对于更新,此索引可能会使 Postgres 不必在更新前创建和维护原始表的副本。

    【讨论】:

    • 改进了一半,在我创建索引后从解释中获取,虽然,不使用子查询,改进了更多,谢谢@Tim Biegeleisen
    猜你喜欢
    • 2015-09-27
    • 1970-01-01
    • 2013-11-06
    • 2015-07-29
    • 1970-01-01
    • 2014-01-27
    • 2016-02-04
    • 2023-03-05
    • 1970-01-01
    相关资源
    最近更新 更多