【问题标题】:Postgres deadlocks during concurrent upserts from temporary tables从临时表并发更新插入期间 Postgres 死锁
【发布时间】:2020-06-22 23:44:53
【问题描述】:

我有一个由 Airflow 控制的进程,它生成许多任务,执行并发插入 Postgres 数据库。

每个任务都需要一个 pandas 数据框,将行插入到临时表中,然后从临时表中更新插入到目标表中。这会导致死锁,但我很难理解如何缓解这个问题。我已经提取了这里的重要部分,但如果我没有包含足够的信息,请告诉我。

我在 python 3.8.2、postgres 11.7、airflow 1.10.10 中,并使用 psycopg2 作为 odbc 连接。

# create temp table like target table
temp_table_sql = 'CREATE TEMP TABLE mur_global_raw_tmp_61400102 (Like mur_global_raw INCLUDING IDENTITY);'
cur.execute(temp_table_sql)

# serialize dataframe and copy to temp table
pd_df_serial = StringIO()
pd_df.to_csv(pd_df_serial, sep='\t', header=False, index=False)
pd_df_serial.seek(0)

cur.copy_from(pd_df_serial, temp_table_name, null="", columns=pd_df.columns.to_list())
conn.commit()

# upsert from temp table to target table
pd_df_insert_sql = 'INSERT INTO mur_global_raw(lat,lon,time,analysed_sst)
    (SELECT lat,lon,time,analysed_sst FROM mur_global_raw_tmp_61400102
    as tmp_vals ORDER BY lat,lon,time,analysed_sst)
    ON CONFLICT DO NOTHING;'

cur.execute(pd_df_insert_sql)
conn.commit()

这是临时表的架构。

   Column    |           Type           | Collation | Nullable |             Default              | Storage | Stats target | Description
--------------+--------------------------+-----------+----------+----------------------------------+---------+--------------+-------------
 ind          | bigint                   |           | not null | generated by default as identity | plain   |              |
 lat          | double precision         |           |          |                                  | plain   |              |
 lon          | double precision         |           |          |                                  | plain   |              |
 time         | timestamp with time zone |           |          |                                  | plain   |              |
 analysed_sst | double precision         |           |          |                                  | plain   |              |

这是目标表的架构。

    Column    |           Type           | Collation | Nullable |             Default              | Storage | Stats target | Description
--------------+--------------------------+-----------+----------+----------------------------------+---------+--------------+-------------
 ind          | bigint                   |           | not null | generated by default as identity | plain   |              |
 lat          | double precision         |           |          |                                  | plain   |              |
 lon          | double precision         |           |          |                                  | plain   |              |
 time         | timestamp with time zone |           |          |                                  | plain   |              |
 analysed_sst | double precision         |           |          |                                  | plain   |              |
Indexes:
    "mur_global_raw_pkey" PRIMARY KEY, btree (ind)

最后,这是来自服务器日志的示例:

2020-06-22 23:03:36 UTC::@:[3570]:LOG: checkpoint starting: xlog
2020-06-22 23:03:42 UTC:xxxxx(38068):postgres@public_data_raw:[13975]:WARNING: there is no transaction in progress
2020-06-22 23:03:43 UTC:xxxxx(38090):postgres@public_data_raw:[13993]:ERROR: deadlock detected
2020-06-22 23:03:43 UTC:xxxxx(38090):postgres@public_data_raw:[13993]:DETAIL: Process 13993 waits for ShareLock on transaction 42977; blocked by process 14014.
Process 14014 waits for ShareLock on transaction 42981; blocked by process 14021.
Process 14021 waits for ShareLock on transaction 42980; blocked by process 13993.
Process 13993: INSERT INTO mur_global_raw(lat,lon,time,analysed_sst) (SELECT lat,lon,time,analysed_sst FROM mur_global_raw_tmp_75410038 as tmp_vals ORDER BY lat,lon,time,analysed_sst) ON CONFLICT DO NOTHING;
Process 14014: INSERT INTO mur_global_raw(lat,lon,time,analysed_sst) (SELECT lat,lon,time,analysed_sst FROM mur_global_raw_tmp_41473761 as tmp_vals ORDER BY lat,lon,time,analysed_sst) ON CONFLICT DO NOTHING;
Process 14021: INSERT INTO mur_global_raw(lat,lon,time,analysed_sst) (SELECT lat,lon,time,analysed_sst FROM mur_global_raw_tmp_28913605 as tmp_vals ORDER BY lat,lon,time,analysed_sst) ON CONFLICT DO NOTHING;
2020-06-22 23:03:43 UTC:xxxxx(38090):postgres@public_data_raw:[13993]:HINT: See server log for query details.
2020-06-22 23:03:43 UTC:xxxxx(38090):postgres@public_data_raw:[13993]:CONTEXT: while inserting index tuple (1969403,34) in relation "mur_global_raw"
2020-06-22 23:03:43 UTC:xxxxx(38090):postgres@public_data_raw:[13993]:STATEMENT: INSERT INTO mur_global_raw(lat,lon,time,analysed_sst) (SELECT lat,lon,time,analysed_sst FROM mur_global_raw_tmp_75410038 as tmp_vals ORDER BY lat,lon,time,analysed_sst) ON CONFLICT DO NOTHING;

这些死锁持续且定期发生,因此希望我可以解决设计中的某个组件来避免它们。我对正在发生的锁的理解显然不足以解决现阶段的问题。

如果有人能帮助我理解导致这种三向死锁的锁和事务,我将不胜感激。当然,如果您有如何避免它的想法,我也欢迎。

衷心感谢 SO 社区。​​p>

【问题讨论】:

  • 神秘,因为你使用ORDER BY
  • 除了生成的标识列的主键之外,您是否有任何唯一索引/约束?如果不是,为什么是 ON CONFLICT?
  • 我还没有独特的约束——我需要大量回填历史数据,然后再建立索引。当存在独特的限制时,将上述内容与未来的 upserts 放在一起。您是否看到 ON CONFLICT 和/或 upsert 逻辑通常可能会影响@jjanes 的死锁问题?
  • 我不知道是什么导致了死锁。由于您还没有唯一索引,因此我现在将放弃 ON CONFLICT 并查看是否可以解决它。无论采用哪种方式,它都为追踪问题提供了宝贵的信息。

标签: python postgresql


【解决方案1】:

我最好的解决方法是在启动 upsert 之前添加一个独占锁,如下所示:

LOCK TABLE mur_global_raw IN EXCLUSIVE MODE;

欢迎任何 cmets。

【讨论】:

  • 这将阻止并发的 autovacuum worker 运行,所以如果你一直这样做,你就会遇到麻烦。
  • 好点,感谢 cmets。您能帮我了解在没有锁定的情况下允许 autovaccum 进程顺利运行所需的长度和频率吗?
  • 这是一个关于 autovaccum 的非常可靠的概要,它看起来像:percona.com/blog/2018/08/10/… 至少在历史数据的主要回填期间,此表上没有真正的更新/删除案例。此外,即使回填过程连续运行,插入之间也有至少 10 分钟(通常更长)的时间,此时表上没有锁。
  • log_autovacuum_min_duration转为0,在日志中查看表格需要多长时间。退后一步:由于您的解决方案有效地禁用了并发性,那么在多个线程中导入有什么好处?
  • @ConnorDibble 我们你能找到并发解决方案吗?
【解决方案2】:

如果您找不到更好的方法,请捕获死锁错误并重复事务。如果死锁经常发生,那会很烦人并且会损害性能,但它比表锁要好,因为它不会阻止 autovacuum 完成它的重要工作。

也许您可以减少批处理的大小或持续时间以减少死锁的可能性。

【讨论】:

  • 我确实使用了批量大小但无济于事,但我会重新审视这一点。也许我减少批量大小的想法还不够。我会看看如何让每个单独的插入非常短。目前,每批可能需要 3 到 5 分钟。
  • 几分钟非常长。调整那里可能是您前进的最佳方式。使用准备好的语句或 - 甚至更好 - COPY.
猜你喜欢
  • 2017-05-10
  • 1970-01-01
  • 2016-11-14
  • 2010-10-03
  • 1970-01-01
  • 1970-01-01
  • 2017-11-16
  • 2018-03-04
  • 1970-01-01
相关资源
最近更新 更多