saf*_*fts 10 postgresql vacuum update aws-aurora
我想弄清楚为什么一个UPDATE
语句需要太长时间(> 30 秒)。
这是随机的,即在大多数情况下,它在 100 毫秒内完成。但是,有时(随机)需要> 30 秒才能完成。
一些细节:
REINDEX
ing、VACUUM
ing(和VACUUM ANALYZE
),但没有任何改进log_lock_waits
) 但我什么也没看到。查询:
UPDATE "my_table" SET "match_request_id" = 'c607789f-4816-4a38-844b-173fa7bf64ed'::uuid WHERE "my_table"."id" = 129624354;
Run Code Online (Sandbox Code Playgroud)
的输出 EXPLAIN (ANALYZE VERBOSE BUFFERS COSTS)
Update on public.my_table (cost=0.56..8.58 rows=1 width=832) (actual time=34106.965..34106.966 rows=0 loops=1)
Buffers: shared hit=431280 read=27724
I/O Timings: read=32469.021
-> Index Scan using my_table_pkey on public.my_table (cost=0.56..8.58 rows=1 width=832) (actual time=0.100..0.105 rows=1 loops=1)
Output: (...)
Index Cond: (my_table.id = 130561719)
Buffers: shared hit=7
Planning Time: 23.872 ms
Execution Time: 34107.047 ms
Run Code Online (Sandbox Code Playgroud)
请注意,这是EXPLAIN ANALYZE
. 我很困惑,因为虽然成本真的很低,但实际运行时间却是巨大的!
我试图了解这是否是预期的,以及我是否可以以某种方式改善这种情况。欢迎任何想法,我有点用完了!
编辑:添加一些评论要求的更多信息:
“正常”更新的查询计划
Update on public.my_table (cost=0.43..8.45 rows=1 width=837) (actual time=2.037..2.037 rows=0 loops=1)
Buffers: shared hit=152 read=1
I/O Timings: read=1.225
-> Index Scan using my_table_pkey on public.my_table (cost=0.43..8.45 rows=1 width=837) (actual time=0.024..0.026 rows=1 loops=1)
Output: (...)
Index Cond: (my_table.id = 129624354)
Buffers: shared hit=4
Planning Time: 1.170 ms
Execution Time: 2.133 ms
(9 rows)
Run Code Online (Sandbox Code Playgroud)
该表有 23 个索引,6 个外键约束。3 布林,1 杜松子酒。其余的 B 树。我不确定如何检查fastupdate
GIN 索引,输出\d+ index_name
是
Column | Type | Key? | Definition | Storage | Stats target
--------+------+------+------------+----------+--------------
search | text | yes | search | extended |
gin, for table "public.my_table"
Run Code Online (Sandbox Code Playgroud)
jja*_*nes 10
GIN 索引具有“快速更新”机制,其中新数据以线性方式写入特定部分。一旦超过某个设置的大小(gin_pending_list_limit,它是全局设置的,但可以按索引覆盖),下一个写入索引的过程就会被分配将这些条目合并到索引的主要部分的任务,这可能导致长时间冻结该过程。对于您看到的冻结时间,您必须为 gin_pending_list_limit 设置一个相当高的设置。
如果一致延迟对您来说比整体插入/更新性能更重要,您可以禁用快速更新。或者您可以降低 gin_pending_list_limit 的值以在降低冻结时间的同时保留一些好处。
如果您需要一致性和整体效率,您可以编写一个手动进程/线程,它会打开自己的连接并select gin_clean_pending_list(...)
在主更新操作进行时循环运行索引。这样你就可以从待处理列表中受益,而清理它的延迟全部加载到无关紧要的后台进程中。(Vacuum 和 autovacuum 也会清理它,但你不能真正让它们运行得足够频繁以完成足够的工作,因为它们还需要做所有其他工作。因此 gin_clean_pending_list)