【问题标题】:How do you handle stale data with multiple threads?你如何处理多线程的陈旧数据?
【发布时间】:2018-12-20 19:01:41
【问题描述】:

假设我有以下伪代码:

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 实际上将 'bob@gmail.com' 插入到 MARKER A 的用户中,那么线程 A 具有“陈旧数据”并会尝试插入 'bob@gmail.com ',以为计数仍然是 0,但实际上它现在是 1。这将出错,因为我们在电子邮件上有一个唯一索引。

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

我应该实现应用程序级锁定吗?我是否应该确保当这个操作发生时,它必须获得锁才能访问用户表,这样其他线程才能对其进行更改?我觉得锁定整个表是我想避免的性能损失。

【问题讨论】:

    标签: sql multithreading postgresql transactions locks


    【解决方案1】:

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

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

    alter table users add constraint uq1 unique(email);
    

    尝试在数据库中插入行。如果成功,一切都很好;如果失败,则其他线程已经插入了该行。

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

    【讨论】:

    • +1。添加UNIQUE 约束还允许您使用UPSERTINSERT INTO users VALUES ('bob@gmail.com') ON CONFLICT ON CONSTRAINT uq1 DO NOTHING;
    猜你喜欢
    • 2012-05-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-07
    • 2015-02-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多