Postgresql 将一个表分块以将timestamp批量更新为timestamp 短版 Postgres 11.4部署在RDS上 是否有一种内置的或直接的方法来拆分表中的行以进行批量更新 一旦有了一个bucket方案,如何在SQL中运行一个循环来处理每个bucket,并稍作停顿,让服务器喘口气 甚至有必要分批完成这项工作,还是我无缘无故地担心 详细版本:

Postgresql 将一个表分块以将timestamp批量更新为timestamp 短版 Postgres 11.4部署在RDS上 是否有一种内置的或直接的方法来拆分表中的行以进行批量更新 一旦有了一个bucket方案,如何在SQL中运行一个循环来处理每个bucket,并稍作停顿,让服务器喘口气 甚至有必要分批完成这项工作,还是我无缘无故地担心 详细版本:,postgresql,timestamp,alter-table,timestamp-with-timezone,Postgresql,Timestamp,Alter Table,Timestamp With Timezone,我们已经收集数据一段时间了,并且使用timestamtz字段。我犯了一个错误,我应该用时间戳。我们要做的是从不同的地点收集大量数据,然后自己计算UTC,然后将数据推送到Postgres。据我所知,timestamp和timestamptz数据都是相同的8字节,timestamptz给你的是神奇的(而且是不可见的)时区转换。也就是说,数据并没有什么不同,而是Postgres对待数据的方式不同。在我们的例子中,这意味着我们把数据作为UTC推到Postgres中,然后再把它拉到本地,这是在搞砸。我们的

我们已经收集数据一段时间了,并且使用timestamtz字段。我犯了一个错误,我应该用时间戳。我们要做的是从不同的地点收集大量数据,然后自己计算UTC,然后将数据推送到Postgres。据我所知,timestamp和timestamptz数据都是相同的8字节,timestamptz给你的是神奇的(而且是不可见的)
时区转换。也就是说,数据并没有什么不同,而是Postgres对待数据的方式不同。在我们的例子中,这意味着我们把数据作为UTC推到Postgres中,然后再把它拉到本地,这是在搞砸。我们的服务器数据没有一个时区,这就是为什么我们在内部将其设置为UTC,就像Postgres一样。为了使报告更简单,分析表通常有一个用于本地数据和utc数据的冗余列。通过这种方式,我们可以运行报告,比较不同时区设施的“8-11周一上午”。不同的设施有不同的时区,所以我们使用“local”值,这是这些查询的本地值。但是如果我们需要一个统一的时间线,那么我们就使用UTC。简而言之:同一个表中的行可能来自不同时区的源

好的,这就是背景,我现在有1000万行要更新。结构修改看起来很简单:

-- Change the data type, this is instantaneous.
ALTER TABLE assembly
   ALTER COLUMN created_dts 
   SET DATA TYPE timestamp;
   
-- Reset the default, it's probably not necessary, but the ::timestamptz is misleading/confusing here otherwise.
ALTER TABLE assembly
   ALTER COLUMN created_dts 
   SET DEFAULT '-infinity'::timestamp
我必须删除并重新创建一些视图,但这只是运行一些备份脚本的问题

我的问题是如何在不拖累服务器的情况下有效地进行更新?我在想象一次分批处理5K行的东西,或者类似的东西。为了简单起见,假设我们的所有服务器都设置为US/Central。当我们最初将数据推送到UTC时,它又被Postgres转换了,所以现在数据被服务器的时间和UTC之间的偏移量抵消了。(我想)如果是这样,最简单的更新可能如下所示:

SET TIME ZONE 'UTC'; -- Tell Postgres we're in UTC to line up the data with the UTC clock it's set to.
UPDATE analytic_scan 
  SET created_dts = created_dts at time zone 'US/Central' -- Tell Postgres to convert the value back to where we started.
这似乎是可行的(?),忽略了处理夏令时的明显遗漏。我可以添加一个
WHERE
子句来处理这个问题,但它不会改变我的问题。现在的问题是,我有这样的记录:

analytic_productivity           728,708
analytic_scan                 4,296,273
analytic_sterilizer_load        136,926
analytic_sterilizer_loadinv     327,700
record_changes_log           17,949,132
所以,不是很大,但不是什么都没有。有没有一种方法可以合理地在SQL中切片数据,以便

  • 每行更新一次
  • 没有行被更新过一次
  • 一次更新的行不太多
所有表都有一个UUID PK字段,一对表都有一个生成的标识列,就像从这个报告表中截取的:

CREATE TABLE IF NOT EXISTS "data"."analytic_productivity" (
    "id" uuid NOT NULL DEFAULT NULL,
    "pg_con_id" integer GENERATED BY DEFAULT AS IDENTITY UNIQUE,
    "data_file_id" uuid NOT NULL DEFAULT NULL,
    "start_utc" timestamptz NOT NULL DEFAULT '-infinity',
    "start_local" timestamptz NOT NULL DEFAULT '-infinity',
    "end_utc" timestamptz NOT NULL DEFAULT '-infinity',
    "end_local" timestamptz NOT NULL DEFAULT '-infinity')
我的一个想法是使用
UUID::text
的子字符串或散列来生成更小的批:

select * from analytic_sterilizer_loadinv 
  where left(id::text,1) = 'a'
这似乎缓慢而可怕。哈希似乎更好一些:

select abs(hashtext(id::text))  % 64,
       count(*)
       
  from analytic_sterilizer_loadinv 
桶的大小不是那么均匀,但可能已经足够好了,如果需要的话,我可以增加桶的数量。不幸的是,我不知道如何使用bucket在SQL中的循环中运行代码。如果有人告诉我怎么做,我会很感激的。如果有一个简单的内置分块功能,我很想知道


除了锁定整个表之外,我还没有考虑清楚如何处理将被修改所占用的传入数据。我也许能做到。

如果你负担得起的话,不要批量更新,而是一次更新。主要的缺点是,这样会使表膨胀,之后应在表上运行
VACUUM(FULL)
,这将导致停机时间

我将编写客户机代码以成批执行更新,例如在bash中:

排版-i部分=0
#PostgreSQL客户端时区
导出PGTZ=UTC
而[$part-lt 64]
做

谢谢你的回答。如果我一次完成更新,是否应该在开始时抛出表锁?或者通过某种配置临时调整以提高吞吐量?如果
max\u wal\u size
较高,批量修改会更快。不需要锁定表,除非您希望并发的数据修改会锁定表中的多行,并可能导致死锁。但是锁桌子也不会有什么坏处。如果您一次完成所有操作,请不要忘记之后的
真空(完全)
。我将在测试数据集上运行一些实验,看看我学到了什么,谢谢。