交易基础。2 个事务并发运行的结果是什么?

Jim*_*Jim 6 mysql innodb concurrency transaction

我有一个关于交易的相当基本的问题。
假设Account有一个列金额的表。现在我们有以下代码:

BEGIN  
$amount = SELECT amount FROM ACCOUNT WHERE id = 123;  
$amount = $amount - 100;  
UPDATE ACCOUNT SET amount = $amount WHERE id = 123;    
COMMIT
Run Code Online (Sandbox Code Playgroud)

现在,如果此代码段由 2 个事务并发运行,T1 和 T2 会发生什么?
我知道在 MySQL 中默认隔离级别是REPEATABLE READ.
所以假设最初的金额是 500。
那么当 2 笔交易完成时,最终的金额是 300 还是 100?
我的意思是当他们开始从表中读取时,我猜这是一个共享锁。
当一个人更新表时,它会获得一个排他锁吗?那么另一笔交易会发生什么?它会继续处理它开始时“查看”的数量,即 500?

Rol*_*DBA 3

REPEATABLE READ隔离级别中没有隐式共享锁。

您需要更改交易顺序如下:

隔离级别

您可以切换到SERIALIZABLE隔离级别并禁用自动提交。这样,该行就被隐式锁定在共享模式下。

如果您想保留REPEATABLE READREAD COMMITTED,则必须在执行 UPDATE 之前手动调用SELECT ... FOR UPDATE 。

查询(可选建议)

您还应该尝试

UPDATE ACCOUNT SET amount = amount - 100 WHERE id = 123; 
Run Code Online (Sandbox Code Playgroud)

以及禁用的自动提交和可串行化隔离级别。

我建议这样做是因为您正在执行两个查询(SELECT 和 UPDATE)来更改金额。您只需通过一个查询即可完成此操作。

你的问题

由于锁定读取而不是隐式使用REPEATABLE READ,因此 500 有可能被使用两次。

你问

是否可以在连接级别设置隔离级别?即不触及全局隔离级别?

是的,您可以在会话中设置隔离级别

SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL   {
    REPEATABLE READ
  | READ COMMITTED
  | READ UNCOMMITTED
  | SERIALIZABLE    }
Run Code Online (Sandbox Code Playgroud)