更新:下面的潜在解决方案
我有一大堆配置文件,包含我试图推入数据库的键/值对。在配置文件中重复了许多键和值,因此我使用3个表存储数据。一个用于所有唯一键值,一个用于所有唯一对值,另一个列出每个文件的所有键/值对。
问题: 我使用多个并发进程(以及连接)将原始数据添加到数据库中。不幸的是,在尝试向键和值表添加值时,我遇到了很多检测到的死锁。我尝试了几种不同的插入数据的方法(如下所示),但最终总是检测到死锁#34;错误
TransactionRollbackError:检测到死锁
详情:过程26755 在交易689456上等待ShareLock;被进程26754阻止。 过程26754在事务689467上等待ShareLock;被阻止了 过程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;
带有保存点的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和SELECT FOR SHARE命令 在搜索目标行方面表现与SELECT相同:它们 将只查找从命令start开始提交的目标行 时间1。但是,这样的目标行可能已经更新(或 当它被另一个并发事务删除或锁定时 找到。在这种情况下,可能的更新程序将等待第一个 更新事务以提交或回滚(如果它仍在 进展)。如果第一个更新程序回滚,那么它的效果是 否定,第二个更新程序可以继续更新 最初找到了一排。如果第一个更新程序提交,则第二个更新程序 如果第一个更新程序删除它2将忽略该行,否则它 将尝试将其操作应用于行的更新版本。
虽然我仍然不确定共同依赖的位置,但似乎在没有提交的情况下处理大量的键/值对可能会导致类似这样的事情。果然,如果我在添加每个单独的配置文件后提交,则不会发生死锁。
答案 0 :(得分:12)
看起来你处于这种情况:
这种情况会造成以下死锁机会:
假设有两个会话,每个会话都开始了一次交易。
此后不久,死锁检测器意识到两个会话现在正在等待彼此,并且终止其中一个会话时发现致命的死锁检测错误。
如果您在这种情况下,最简单的解决方案是在插入新条目之后进行COMMIT,然后再尝试在表中插入任何新行。