如何处理具有多个线程的陈旧数据?

时间:2018-12-20 19:01:42

标签: sql multithreading postgresql transactions locks

假设我有以下伪代码:

SELECT count(*) FROM users WHERE email = 'bob@gmail.com'
>>>> MARKER A
if (count > 0) return;
else INSERT INTO users VALUES ('bob@gmail.com')

因此,基本上,仅在电子邮件尚不存在时插入它。我知道我可能会使用某种INSERT IF NOT EXISTS查询,但是假设我们使用了这个示例。

因此,如果上面的代码在线程A上运行,并且线程B实际上在MARKER A的用户中插入了“ bob@gmail.com”,则线程A具有“陈旧数据”,并将尝试插入“ bob@gmail.com” ',以为计数仍为0,但实际上现在为1。由于我们在电子邮件上具有唯一索引,因此这将出错。

我应该使用什么工具来防止此问题?从我对交易的了解中,它们基本上使一组操作原子化,因此上面的代码将完全执行或根本不执行。它不能确保用户表被锁定以防止更新正确吗?所以我不能只将上面的代码包装在事务中并使其成为线程安全的?

我应该实现应用程序级锁定吗?我是否应该确保在执行此操作时,它必须获取该锁才能访问users表,以便其他任何线程都不能对其进行更改?我觉得锁定整个表是我想避免的性能问题。

1 个答案:

答案 0 :(得分:3)

在多线程应用程序上,插入前检查是一种已知的反模式。甚至不要尝试。

正确的方法是让数据库来处理它。在列上添加UNIQUE约束,如下所示:

alter table users add constraint uq1 unique(email);

只需尝试在数据库中插入该行。如果成功,一切都会好起来。如果失败,则说明有其他线程插入了该行。

或者,您可以在整个表上发出LOCK。那也可以,但是您的应用程序的性能将变得很糟糕。