sql - 如何处理数据库中的并发更新?

标签 sql concurrency

在SQL数据库中处理并发更新的常用方法是什么?

考虑一个简单的SQL模式(未显示约束和默认值。),例如

create table credits (
  int id,
  int creds,
  int user_id
);

目的是为用户存储某种信用。类似stackoverflow的声誉。

如何处理对该表的并发更新?
一些选择:
  • update credits set creds= 150 where userid = 1;
    在这种情况下,应用程序检索当前值,计算新值(150)并执行更新。如果其他人在同一时间做同样的事,那将带来灾难。
    我猜想包装当前值的回归并在交易中更新将解决这个问题,例如Begin; select creds from credits where userid=1; do application logic to calculate new value, update credits set credits = 160 where userid = 1; end;在这种情况下,您可以检查新信用是否小于0,如果负信用没有意义,则将其截断为0。
  • update credits set creds = creds - 150 where userid=1;
    这种情况无需担心并发更新,因为DB会处理一致性问题,但存在的缺点是信誉很可能变成负面,这对于某些应用程序可能没有意义。

  • 简而言之,处理上述(非常简单)问题的可接受方法是什么,如果数据库抛出错误怎么办?

    最佳答案

    使用交易:

    BEGIN WORK;
    SELECT creds FROM credits WHERE userid = 1;
    -- do your work
    UPDATE credits SET creds = 150 WHERE userid = 1;
    COMMIT;
    

    一些重要的注意事项:
  • 并非所有数据库类型都支持事务。特别是,MySQL的旧默认数据库引擎(版本5.5.5之前的默认数据库引擎)MyISAM不会。如果您使用的是mysql,请使用InnoDB(新的默认值)。
  • 事务可能会由于无法控制的原因而中止。如果发生这种情况,您的应用程序必须准备从头开始重新开始。
  • 您需要将隔离级别设置为SERIALIZABLE,否则第一个选择可以读取其他事务尚未提交的数据(事务不像编程语言中的互斥锁一样)。如果同时存在正在进行的SERIALIZABLE事务,则某些数据库将引发错误,并且您必须重新启动事务。
  • 一些DBMS提供SELECT .. FOR UPDATE,它将锁定select检索的行,直到事务结束。

  • 将事务与SQL存储过程结合在一起可以使后者更易于处理;应用程序将仅在事务中调用一个存储过程,并在事务中止时重新调用它。

    关于sql - 如何处理数据库中的并发更新?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1195858/

    相关文章:

    php - 为什么 case 表达式不适用于 PHP 中使用的 SQL 查询连接?

    mysql - 对 TableA 和 TableB 的 SELECT 查询 IF TableB.col 值(0 或 1) 我为 0 和 1 创建新列

    MySQL:有没有办法从结果中消除重复的条目

    concurrency - erlang流程和消息传递体系结构

    java - 这个引用转义单线程程序

    java - Thread 类的 onSpinWait​() 方法 - Java 9

    go - 解决 goroutines 死锁

    java - 在 hibernate 中过滤计算列? (拥有)

    php - 我的并发编程逻辑有什么问题吗?

    SQL 最小/最大组按问题