在 postgres 中执行大规模 JSONB 替换的最有效方法
Most efficient way to perform large scale JSONB replace in postgres
我有将近 200 万条记录需要对标识符数组中的值进行简单的字符串替换,例如这个部分记录
{
"id": "a851375d-f6a0-4fba-ba69-c584901de73c",
"source": "MARC",
"title": "Voice Content and Usability [electronic resource] / So, Preston.",
"identifiers": [
{
"value": "9781952616020",
"identifierTypeId": "fcca2643-406a-482a-b760-7a7f8aec640e"
},
{
"value": "(CaSebORM)9781098128852",
"identifierTypeId": "7e591197-f335-4afb-bc6d-a6d76ca3bace"
},
{
"value": "5429852",
"identifierTypeId": "d5989db8-ddc3-4f06-a465-73e99ec262d3"
},
{
"value": "ocm00000001saf9781098128852",
"identifierTypeId": "7e591197-f335-4afb-bc6d-a6d76ca3bace"
}
]
}
我需要从最终值中检测并删除“ocm0000000”,我正在寻找最有效的方法。当我试图简单地将 jsonb 转换为字符串时,进行简单的字符串替换,例如
update my.table
set jsonb = REPLACE(jsonb::text, 'ocm0000000', '')::jsonb
它 CPU 通过了屋顶并占用了我们 RDS 集群的所有连接,干扰了其他操作,因此有必要终止进程。
当这么多行受到影响时,进行此类替换的最佳方法是什么?
您当前的 UPDATE 将重写每一行,即使是那些不需要更改的行。您应该添加一个 WHERE 子句来防止这种情况发生。如果您想分批更新,这一点尤其重要。
set jsonb = REPLACE(jsonb::text, 'ocm0000000', '')::jsonb
where jsonb::text like '%ocm0000000%'
完成此操作的最简单方法是声明维护 window 并直接执行,或者在下班时间执行。对我来说,在一台配备 SSD 驱动器的小型笔记本电脑上更新 200 万份示例记录只用了不到 2 分钟。你说只是部分,但我不知道你的真实记录会大多少。也许您可以克隆我们的服务器并使用它来测试需要多长时间。您还可以临时配置更多 IOPS 以使其运行速度更快。
如果你想批量运行它,你可以添加一个where子句来限制要更新的ctid的范围:
set jsonb = REPLACE(jsonb::text, 'ocm0000000', '')::jsonb
where jsonb::text like '%ocm0000000%'
and ctid between '(0,0)' and '(9999,99)'; --first ~80MB.
然后您将等待系统喘口气,然后将 CTID window 向下滑动以重复此操作。重复直到整个 table 被覆盖。您只需要继续,直到 ctid 中的第一个数字与 table 中的当前页面一样大。然后 运行 再一次没有 ctid 条件,以确保没有任何遗漏。
我有将近 200 万条记录需要对标识符数组中的值进行简单的字符串替换,例如这个部分记录
{
"id": "a851375d-f6a0-4fba-ba69-c584901de73c",
"source": "MARC",
"title": "Voice Content and Usability [electronic resource] / So, Preston.",
"identifiers": [
{
"value": "9781952616020",
"identifierTypeId": "fcca2643-406a-482a-b760-7a7f8aec640e"
},
{
"value": "(CaSebORM)9781098128852",
"identifierTypeId": "7e591197-f335-4afb-bc6d-a6d76ca3bace"
},
{
"value": "5429852",
"identifierTypeId": "d5989db8-ddc3-4f06-a465-73e99ec262d3"
},
{
"value": "ocm00000001saf9781098128852",
"identifierTypeId": "7e591197-f335-4afb-bc6d-a6d76ca3bace"
}
]
}
我需要从最终值中检测并删除“ocm0000000”,我正在寻找最有效的方法。当我试图简单地将 jsonb 转换为字符串时,进行简单的字符串替换,例如
update my.table
set jsonb = REPLACE(jsonb::text, 'ocm0000000', '')::jsonb
它 CPU 通过了屋顶并占用了我们 RDS 集群的所有连接,干扰了其他操作,因此有必要终止进程。
当这么多行受到影响时,进行此类替换的最佳方法是什么?
您当前的 UPDATE 将重写每一行,即使是那些不需要更改的行。您应该添加一个 WHERE 子句来防止这种情况发生。如果您想分批更新,这一点尤其重要。
set jsonb = REPLACE(jsonb::text, 'ocm0000000', '')::jsonb
where jsonb::text like '%ocm0000000%'
完成此操作的最简单方法是声明维护 window 并直接执行,或者在下班时间执行。对我来说,在一台配备 SSD 驱动器的小型笔记本电脑上更新 200 万份示例记录只用了不到 2 分钟。你说只是部分,但我不知道你的真实记录会大多少。也许您可以克隆我们的服务器并使用它来测试需要多长时间。您还可以临时配置更多 IOPS 以使其运行速度更快。
如果你想批量运行它,你可以添加一个where子句来限制要更新的ctid的范围:
set jsonb = REPLACE(jsonb::text, 'ocm0000000', '')::jsonb
where jsonb::text like '%ocm0000000%'
and ctid between '(0,0)' and '(9999,99)'; --first ~80MB.
然后您将等待系统喘口气,然后将 CTID window 向下滑动以重复此操作。重复直到整个 table 被覆盖。您只需要继续,直到 ctid 中的第一个数字与 table 中的当前页面一样大。然后 运行 再一次没有 ctid 条件,以确保没有任何遗漏。