2

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

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

这是比赛条件...

当我遇到 url Shorter 时,会发生以下逻辑:

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 上的唯一索引被违反了。

选择/插入实际上确实紧密地一起运行。据我所知,看起来 2 个缩短的链接彼此相邻。

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

任何人都可以提出任何提示/技巧来处理这个问题吗?我想避免显式锁定,因为我知道这会引发一系列其他问题。

4

3 回答 3

2

确实没有一种解决方案可以避免需要能够处理唯一约束违反错误的可能性。如果您的框架不能做到这一点,那么我会将 SQL 包装在一个 PL/pgSQL 函数或过程

鉴于您可以处理错误,您最好不要测试唯一值的存在而只是尝试插入,让任何错误都由 EXCEPTION 子句处理。

于 2013-05-20T15:53:31.440 回答
2

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

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

仅当该链接不存在时才会插入。

于 2013-05-20T15:46:13.783 回答
1

您要么需要某种互斥锁,要么必须忍受由于竞争条件而发生的冗余。

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

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

于 2013-05-20T15:46:39.397 回答