Alembic迁移与PostgreSQL卡住了?

22 投票
5 回答
16360 浏览
提问于 2025-04-18 01:36

我写了一个迁移脚本,在sqlite上运行得很好,但如果我尝试在postgres上使用同样的脚本,它就会一直卡住。通过简单的命令查看一下,我发现postgres卡在“创建表”的状态上。有没有什么好的做法可以避免这个问题?

5 个回答

0

在Postgres上运行以下查询,看看这个查询是否卡住了:

SELECT * FROM pg_stat_activity where state = 'active'
2

你的数据库很可能被其他查询锁住了。

特别是如果你在使用图形界面工具pgAdmin时,这种情况会经常发生。我发现,清空表格特别麻烦,有时候pgAdmin会崩溃,导致数据库卡住。

你需要做的是重启整个PostgreSQL服务,然后再试一次。

确保你:

  1. 尽量少用图形界面工具pgAdmin
  2. 如果不需要的话,记得关闭你用psycopg2打开的游标或数据库连接
4

对于遇到这个问题的朋友,这里有我解决它的方法:

标准的 alembic 的 env.py 文件里有一段代码:

with context.begin_transaction():
    logger.info("Running Transaction.")
    context.run_migrations()

但是,如果你在迁移脚本中引入引擎时使用了:

config = op.get_context().config
engine = engine_from_config(config.get_section(
                            config.config_ini_section), prefix='sqlalchemy.')

那么事务就会卡在“事务空闲”状态。我不太确定为什么会这样,但看起来是因为在事务中又嵌套了一个事务,而外层的事务从来没有被提交。

要解决这个问题,只需去掉外层的 begin_transaction():

# with context.begin_transaction():
logger.info("Running Transaction.")
context.run_migrations()

这样就可以正常工作了。

4

你可以随时重启一下postgresql。

35

如果真的卡在了一个锁上,你需要看看它在等什么。虽然CREATE TABLE卡在锁上听起来有点奇怪,但也不是不可能。

获取被卡住的进程ID

首先,找出那个在等待的进程ID。你可以通过ps命令找到它,或者通过从pg_stat_activitySELECT查询,找出那些waiting为真的进程,这样就能找到你感兴趣的命令:

SELECT * FROM pg_stat_activity WHERE waiting;

找出它在等哪个锁

通过查询pg_locks,看看被卡住的进程在等哪个锁:

SELECT * FROM pg_locks WHERE pid = <the-waiting-pid> AND NOT granted;

你可以把这两个步骤结合起来:

\x

SELECT * 
FROM pg_locks l
INNER JOIN pg_stat_activity s ON (l.pid = s.pid)
WHERE waiting
AND NOT granted;

然后查看结果,或者在s.query字段上使用LIKE过滤器,找到你想要识别的锁定问题的查询。

找出谁持有那个锁

现在你可以查询pg_locks,找出哪些进程持有那个锁,以及它们在做什么。

假设我们发现create在等待一个类型为relation的锁,模式为AccessExclusiveLock,关系为14421。我们想找出其他会话在那个关系上持有的锁:

SELECT * 
FROM pg_locks l 
INNER JOIN pg_stat_activity s ON (l.pid = s.pid)
WHERE locktype = 'relation'
AND   relation = 14421;

这样应该能告诉你是什么阻碍了创建操作。

一些细节

在PostgreSQL的维基上有一个很方便的锁监控查询,但它只会找到行级锁。所以对于DDL(数据定义语言)来说,一般没什么帮助。

另外,我故意没有把所有内容合并成一个查询。对于AccessExclusiveLock来说,找出阻塞某个特定进程的锁持有者其实很简单,但对于弱锁请求来说就没那么简单了——我得把哪些锁互相冲突的规则写成SQL,这样比较复杂。还是直接看比较好。

撰写回答