postgresql UPDATE 查询永远占用 300 万行

postgresql UPDATE query taking forever with 3m rows

这是 table 架构:

CREATE TABLE public.page_by_category
(
    id integer NOT NULL DEFAULT nextval('page_by_category_id_seq'::regclass),
    page_id bigint NOT NULL,
    category_id bigint NOT NULL,
    weight integer NOT NULL,
    CONSTRAINT id_pk PRIMARY KEY (id),
    CONSTRAINT category_id_fkey FOREIGN KEY (category_id)
    CONSTRAINT page_id_fkey FOREIGN KEY (page_id)
)

这是需要很长时间的查询:UPDATE page_by_category SET weight=0 on 3m rows.

查看 pg_stat_activity 这是结果:

30366   "2 days 18:32:12.141453"    "user"  "UPDATE page_by_category SET weight=0"

如何检查查询是否卡住了?由于没有 IO,CPU 大量使用...在我的 centos 上,我使用 top,iotop 来查看是否使用了 cpu 或磁盘,但仅使用了 5%...

PostgreSQL 版本:“”PostgreSQL 10.7 on x86_64-pc-linux-gnu”

这应该不会花费太多时间。杀掉进程,重新执行查询,看是否又卡了。

SELECT pg_cancel_backend(<pid of the process>)

如果您不知道 pid 那么您可以尝试以下查询:

SELECT * FROM pg_stat_activity WHERE state = 'active';

然后找到你要杀掉的进程。

如果该进程仍然存在,请尝试:

SELECT pg_terminate_backend(<pid of the process>)

(如果可能,请在终止查询后重新启动 DB,然后重试。)

由于您的挂起更新的进程 ID 是 30366,您应该查找持有阻止语句的锁的打开事务:

SELECT pg_blocking_pids(30366);

弄清楚这些连接有什么问题以及为什么它们持有锁这么长时间。

杀了他们,运行

SELECT pg_terminate_backend(?????);

其中 ????? 是通过上述查询找到的阻塞进程 ID 之一。

如果它不是阻止您查询的锁,则仍然存在以下可能性:

  • 您的存储速度非常慢

  • 你有昂贵的行级触发器

  • 你有很多索引