我怎样才能处理PostgreSQL中的竞争条件?

时间:2013-05-20 15:36:50

标签: postgresql concurrency

我在Postgresql上遇到了一些错误,这似乎与这种竞争条件有关。

我有一个用Twisted Python编写的进程/守护进程。描述它的最简单方法是作为Web爬虫 - 它拉取页面,解析链接,并记录它所看到的内容。由于HTTP阻塞,Twisted运行多个延迟到线程的“并发”进程。

这是竞争条件......

当我遇到网址缩短时,会发生以下逻辑:

result= """SELECT * FROM shortened_link WHERE ( url_shortened = %(url)s ) LIMIT 1;"""
if result:
   pass
else:
   result= """INSERT INTO shortened_link ( url_shortened ..."

引发了一个令人惊讶的数字或psycopg2.IntegrityError,因为url_shortened上的唯一索引被违反。

select / insert实际上一起运行。据我所知,看起来有两条缩短的链接排在一起。

Process A: Select, returns Null
Process B: Select, returns Null
Process A: Insert , success
Process B: Insert , integrity error

任何人都可以建议任何提示/技巧来处理这个问题吗?我想避免显式锁定,因为我知道这会打开另外一组问题。

3 个答案:

答案 0 :(得分:2)

在一个命令中完成所有操作:

result= """
INSERT INTO shortened_link ( url_shortened ...
SELECT %(url)s
where not exists (
    select 1
    from shortened_link
    WHERE url_shortened = %(url)s
);"""

只有在该链接不存在时才会插入。

答案 1 :(得分:2)

实际上没有一种解决方案可以避免需要能够处理唯一约束违规错误的可能性。如果您的框架无法执行此操作,那么我将SQL包装在PL / pgSQL函数或过程that can中。

鉴于你可以处理错误,你也可以不测试是否存在唯一值,只是尝试插入,让EXCEPTION子句处理任何错误。

答案 2 :(得分:1)

您需要某种互斥锁,否则您将不得不忍受由于竞争条件而导致的冗余。

如果选择使用互斥锁 - 则不一定需要使用数据库级锁。您可以简单地锁定Twisted进程以阻止处理类似缩短URL的其他线程。

如果您选择避开锁定,请删除url_shortened字段上的唯一约束。您可以定期将这些记录移动到包含每个缩短网址的唯一副本的“干净”表格。