postgresql - Postgres 9.3 : Sharelock issue with simple INSERT

标签 postgresql database-deadlocks

更新:以下可能的解决方案

我有一个大型的配置文件语料库,其中包含我试图推送到数据库中的键/值对。许多键和值在配置文件中重复出现,因此我使用 3 个表存储数据。一个用于所有唯一键值,一个用于所有唯一对值,一个列出每个文件的所有键/值对。

问题: 我正在使用多个并发进程(以及连接)将原始数据添加到数据库中。不幸的是,在尝试向键和值表中添加值时,我检测到了很多死锁。我尝试了几种不同的插入数据的方法(如下所示),但总是以“检测到死锁”错误告终

TransactionRollbackError: deadlock detected
DETAIL: Process 26755 waits for ShareLock on transaction 689456; blocked by process 26754. Process 26754 waits for ShareLock on transaction 689467; blocked by process 26755.

我想知道是否有人可以阐明导致这些僵局的确切原因,并可能为我指明解决问题的方法。查看我正在使用的 SQL 语句(在下面列出),我真的不明白为什么存在任何相互依赖性。

感谢阅读!

示例配置文件:

example_key this_is_the_value
other_example other_value
third example yet_another_value

表定义:

    CREATE TABLE keys (
        id SERIAL PRIMARY KEY,
        hash UUID UNIQUE NOT NULL,
        key TEXT);

    CREATE TABLE values (
        id SERIAL PRIMARY KEY,
        hash UUID UNIQUE NOT NULL,
        key TEXT);

    CREATE TABLE keyvalue_pairs (
        id SERIAL PRIMARY KEY,
        file_id INTEGER REFERENCES filenames,
        key_id INTEGER REFERENCES keys,
        value_id INTEGER REFERENCES values);

SQL 语句:

最初我试图使用这个语句来避免任何异常:

    WITH s AS (
        SELECT id, hash, key FROM keys
            WHERE hash = 'hash_value';
    ), i AS (
        INSERT INTO keys (hash, key)
        SELECT 'hash_value', 'key_value'
        WHERE NOT EXISTS (SELECT 1 FROM s)
        returning id, hash, key
    )
    SELECT id, hash, key FROM i
    UNION ALL
    SELECT id, hash, key FROM s;

但即使像这样简单的事情也会导致死锁:

    INSERT INTO keys (hash, key)
        VALUES ('hash_value', 'key_value')
        RETURNING id;
  • 在这两种情况下,如果我因为插入的散列而抛出异常 值不是唯一的,我使用保存点来回滚更改和 另一个语句,仅选择我要查找的 ID。
  • 我在唯一字段中使用散列,作为一些键和值 太长无法编入索引

带有保存点的 python 代码(使用 psycopg2)的完整示例:

key_value = 'this_key'
hash_val = generate_uuid(value)
try:
    cursor.execute(
        '''
        SAVEPOINT duplicate_hash_savepoint;
        INSERT INTO keys (hash, key)
            VALUES (%s, %s)
            RETURNING id;
        '''
        (hash_val, key_value)
    )

    result = cursor.fetchone()[0]
    cursor.execute('''RELEASE SAVEPOINT duplicate_hash_savepoint''')
    return result
except psycopg2.IntegrityError as e:
    cursor.execute(
        '''
        ROLLBACK TO SAVEPOINT duplicate_hash_savepoint;
        '''
    )

    #TODO: Should ensure that values match and this isn't just
    #a hash collision

    cursor.execute(
        '''
        SELECT id FROM keys WHERE hash=%s LIMIT 1;
        '''
        (hash_val,)
    )
    return cursor.fetchone()[0]

更新: 所以我相信我对 another stackexchange site: 有提示

具体来说:

UPDATE, DELETE, SELECT FOR UPDATE, and SELECT FOR SHARE commands behave the same as SELECT in terms of searching for target rows: they will only find target rows that were committed as of the command start time1. However, such a target row might have already been updated (or deleted or locked) by another concurrent transaction by the time it is found. In this case, the would-be updater will wait for the first updating transaction to commit or roll back (if it is still in progress). If the first updater rolls back, then its effects are negated and the second updater can proceed with updating the originally found row. If the first updater commits, the second updater will ignore the row if the first updater deleted it2, otherwise it will attempt to apply its operation to the updated version of the row.

虽然我仍然不确定相互依赖性在哪里,但似乎在不提交的情况下处理大量键/值对可能会导致类似这样的结果。果然,如果我在添加每个单独的配置文件后提交,就不会发生死锁。

最佳答案

看起来你处于这种情况:

  1. 要插入的表有一个主键(或任何类型的唯一索引)。
  2. 在一个事务中执行对该表的多个 INSERT(而不是在每个事务后立即提交)
  3. 要插入的行以随机顺序出现(关于主键)
  4. 行被插入到并发事务中。

这种情况会造成以下死锁机会:

假设有两个 session ,每个 session 启动一个事务。

  1. session #1:插入主键为“A”的行
  2. session #2:使用 PK“B”插入行
  3. session #1:尝试插入主键为“B”的行 => Session #1 等待直到 Session #2 提交或回滚
  4. session #2:尝试插入主键为“A”的行 => Session #2 等待 Session #1。

此后不久,死锁检测器意识到两个 session 现在都在等待对方,并终止其中一个 session 并报告致命的检测到死锁错误。

如果您遇到这种情况,最简单的解决方案是在插入新条目后,在尝试将任何新行插入表中之前执行 COMMIT。

关于postgresql - Postgres 9.3 : Sharelock issue with simple INSERT,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28367447/

相关文章:

postgresql - 获取分组值除以总数

mysql - 查询正在锁定表,无法终止该进程

sql - SQL Server 上的死锁跟踪

mysql - 如何使选择查询在具有 4 列(sno、sname、job、dept)、有 10 条记录的简单表上运行超过 30 分钟?

postgresql - 如何使用 Vertx JDBC Client 使用列表作为 SQL 查询的参数源?

postgresql - 根据默认顺序加入 2 组

sql - SQL死锁问题

插入和更新中的 Postgresql 死锁

sql - Postgres : Defining the longest streak (in days) per developer

ruby-on-rails - rake 中止!加载错误 : no such file to load -- active_record/connection_adapters/postgresql/explain_pretty_printer