You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的事务(这些事务会一直持有锁不释放):
    SELECT
      pid,
      query,
      now() - query_start AS duration,
      state
    FROM pg_stat_activity
    WHERE state = 'idle in transaction';
    
    找到后杀掉这些进程,同时检查代码里的事务逻辑,避免在事务中执行耗时操作(比如调用外部API、大量数据处理),确保事务及时提交/回滚。
  • 确保迁移操作正常完成:如果是之前的迁移没跑完,先清理残留进程,再重新运行迁移。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 08:23:00