首页 > 解决方案 > 运行简单 UPDATE 时 PostgreSQL 中的死锁

问题描述

update cities set cdb_data = NULL, updated_at = now() where cities.id = 1;

我们循环遍历城市并使用 cdb_data 作为 rails 代码的一部分更新城市,但是我们不断收到以下错误。

ActiveRecord::StatementInvalid: PG::TRDeadlockDetected: ERROR:  deadlock detected
DETAIL:  Process 26741 waits for ShareLock on transaction 2970537161; blocked by process 26818.
Process 26818 waits for ShareLock on transaction 2970537053; blocked by process 26741.
HINT:  See server log for query details.
CONTEXT:  while updating tuple (39,15) in relation "cities"
UPDATE "cities" SET "cdb_data" = $1, "updated_at" = $2 WHERE "cities"."id" = $3

更新城市对象的 Ruby 代码

    city              = City.find_or_create_by(uuid: city_data['uuid'])
    city.name         = city_data['name']
    city.state_id     = city_data['state_id']
    city.cdb_data     = city_data
    city.save

我不知道这个错误发生在哪条记录上,为什么?即使在本地或暂存中进行生产转储,这似乎也不会发生。任何帮助将非常感激。

我正在运行服务器,heroku所以我不确定我是否可以看到 postgres 日志。

标签: ruby-on-railspostgresqlruby-on-rails-5postgresql-9.5database-deadlocks

解决方案


两个这样的事务很容易死锁。

为了避免这个问题,请确保当你“循环穿过城市”时,你总是以相同的顺序这样做,使用类似的东西:

FOR c IN
   SELECT * FROM city
   WHERE /* whatever */
   ORDER BY city.id
LOOP
   /* perform the update */
END LOOP;

推荐阅读