Rails应用异常:SQL请求/Heroku迁移均遇数据库锁等待问题求助
解决PostgreSQL锁等待导致的Rails应用及迁移卡滞问题
问题原因分析
不管是本地Rails应用的AccessShareLock等待,还是Heroku迁移时的AccessExclusiveLock等待,本质都是PostgreSQL的锁冲突,咱们分别拆解:
1. 本地应用卡滞(AccessShareLock等待)
AccessShareLock是PostgreSQL给所有读操作(比如SELECT)加的最低级锁,它会被更高优先级的锁(比如AccessExclusiveLock——表结构变更、删除表等操作会触发这个锁)阻塞。你的应用所有SQL请求都卡着,说明有某个事务长期持有了目标表的AccessExclusiveLock,导致所有读请求都排队等待。常见诱因:
- 之前的表结构变更(比如迁移)没正常完成,进程挂了但事务没释放锁;
- 代码里有长时间运行的事务(比如事务里嵌套了耗时操作,或者开启事务后没及时提交/回滚,处于
idle in transaction状态); - 其他工具/进程正在对目标表执行DDL操作(比如
ALTER TABLE)。
2. Heroku迁移卡滞(AccessExclusiveLock等待)
CREATE TABLE会自动请求AccessExclusiveLock(创建新表需要排他性权限),卡滞说明这个锁被其他进程占用了。在Heroku场景下常见原因:
- 你的Rails应用还在运行,应用进程可能正在尝试访问这个即将创建的表(比如预加载的查询、后台任务),或者只是保持着数据库连接间接占用了资源;
- 之前的迁移进程没彻底终止,残留的进程还持有锁;
- Heroku数据库的连接池被占满,导致迁移请求无法获取足够资源。
具体解决办法
第一步:排查并释放阻塞的锁
不管是本地还是Heroku,先找到持有冲突锁的进程,杀掉它释放锁:
1. 连接到PostgreSQL数据库
- 本地:用
psql命令连接你的数据库,比如psql -d your_db_name; - Heroku:执行
heroku pg:psql直接连接到你的数据库实例。
2. 查询当前锁状态
执行以下SQL,找出所有持有锁的进程及对应的操作:
SELECT pid, usename, pg_stat_activity.query, pg_locks.mode, pg_locks.locktype, pg_locks.relation::regclass AS table_name FROM pg_locks JOIN pg_stat_activity ON pg_locks.pid = pg_stat_activity.pid WHERE pg_locks.relation IS NOT NULL;
重点找mode为AccessExclusiveLock的记录,对应的pid就是阻塞其他请求的进程ID。
3. 终止阻塞进程
执行SQL杀掉对应的进程:
SELECT pg_terminate_backend(你的阻塞进程pid);
比如找到pid是12439,就执行SELECT pg_terminate_backend(12439);
第二步:针对不同场景的额外优化
本地应用卡滞的额外优化
- 检查长时间运行的事务:执行以下SQL找出
idle in transaction的事务(这些事务会一直持有锁不释放):
找到后杀掉这些进程,同时检查代码里的事务逻辑,避免在事务中执行耗时操作(比如调用外部API、大量数据处理),确保事务及时提交/回滚。SELECT pid, query, now() - query_start AS duration, state FROM pg_stat_activity WHERE state = 'idle in transaction'; - 确保迁移操作正常完成:如果是之前的迁移没跑完,先清理残留进程,再重新运行迁移。
Heroku迁移卡滞的额外优化
- 迁移前暂停应用:执行
heroku maintenance:on开启维护模式,此时应用会停止处理请求,不会占用数据库连接和锁。然后运行迁移heroku run rails db:migrate,完成后执行heroku maintenance:off恢复应用。这是最稳妥的办法,避免应用和迁移抢资源。 - 清理残留的迁移进程:执行
heroku ps查看所有运行的进程,如果有之前的run rails db:migrate进程还在挂着,用heroku ps:stop <进程名>杀掉它(比如heroku ps:stop run.1234)。 - 检查数据库连接池:如果连接池被占满,迁移可能无法获取连接。可以临时调整Heroku的数据库连接池大小,或者先重启应用释放连接:
heroku restart。
内容的提问来源于stack exchange,提问作者Nicolas Maloeuvre
相关产品推荐
相关产品推荐

