MySQL 事务未根据完整性约束违规异常进行隔离

标签 mysql transactions

我有一个名为 migration_logs 的键值表有 2 列键,唯一,值均为 varchar(255)。

CREATE TABLE `migration_logs` (
 `key` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
 `value` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
 UNIQUE KEY `migration_logs_key_unique` (`key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

然后我有以下 Laravel 代码:

class MigrationLog {
    ...
    public static function beginMigration($userId)
    {
        $shouldMigrate = false;
        $key = "luser:$userId/migration_pushed";
        \DB::beginTransaction();

        if (!self::query()->where('key', '=', $key)->first()) {
            self::create([
                'key'   => $key,
                'value' => Carbon::now(),
            ]);
            $shouldMigrate = true;
        }

        \DB::commit();

        return $shouldMigrate;
    }
}

注意:您可能会认为我没有正确捕获异常,这是事实。但我的问题是关于 MySQL 的行为,而不是关于 PHP 代码中的异常处理。

你不需要了解 Laravel 来帮助我。 beginMigration() 的作用是检查某个键是否存在,如果不存在,则为其设置一些值,所有这些都在 mysql 事务内进行。隔离级别是MySql默认的。

当两个进程使用相同的输入调用 beginMigration() 时,我希望一个进程设置 key ,另一个进程不执行任何操作。

但是,我发现一种情况,当一个进程设置 key 而另一个进程尝试设置它时,因此 SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry抛出异常。

我想知道这怎么可能?怎么了?以及如何解决?

MySQL版本:5.6.27

最佳答案

更新

事务的隔离确保在数据库事务中只能看到已提交的数据。

   BEGIN
   INSERT INTO mytable (a,b,c ) VALUES ('x', 'y', 'z' );
   INSERT INTO mytable2 (d,e,f ) VALUES ('u', 'v', 'w' );
   COMMIT

数据库的其他用户将看不到“x”、“y”、“z”和“u”、“v”、“w”或两者都看不到。还不到交易的一半。

当您在事务中查询时,您正在查询已提交的数据。因此,两个进程插入相同的数据,都会看到空间可用。

当数据库系统执行冲突提交时,它确保只有第一个事务成功,而所有其他事务都失败(通过抛出异常)。

phplaravel 文档来看,commit 的预期行为似乎是一个异常(exception)。

显示的代码会将 $shouldMigrate 设置为 true,但失败时会引发异常。

如果 $shouldMigrate 的作用域位于函数之外,那么这将导致失败。此外,可能还有一个 try/catch block 正在阻止进程中止。 catch 的处理可能不会传播错误。

try {
    \DB::commit();
} catch (Exception $e) {
    echo 'Exception',  $e->getMessage(), "\n";
    $shouldMigrate = false;
}

这将是我能想到的最简单的修复。

关于MySQL 事务未根据完整性约束违规异常进行隔离,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34490447/

相关文章:

MySQL 创建 IF 查询以显示 Col_A 所在的层

php - 本地主机上的 MySQL 连接被拒绝

mysql - Hibernate事务锁具有外键的表

java - 如何在 JPA 中回滚事务?

MYSQL UPDATE 和 ORDER BY RAND 的错误使用

mysql - 在 Linux 中设置 Mysql++

mysql - 仅显示 select SQL 中的前 5 个字符?

mysql - 如何判断是否已经在 ruby​​ on rails 的数据库事务中?

transactions - 我应该为文件使用 Blob 存储还是 Azure VM 存储?

java - Spring Data Rest 事务边界