mysql - MYSQL 5 的奇怪行为(数据库隔离)

标签 mysql database innodb transaction-isolation

我打开了两个命令窗口来使用我的数据库 (MySQL5)。

下面是我正在使用的表结构(应该注意的是,我已经通过执行 set autocommit=0; 关闭了自动提交):

表结构:

CREATE TABLE  `ajax`.`zipcodes` (
  `ZIPCODE` varchar(5) NOT NULL,
  `CITY` varchar(50) DEFAULT NULL,
  `STATE` varchar(2) DEFAULT NULL,
  PRIMARY KEY (`ZIPCODE`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

以下是事件顺序:

第 1 步: 在命令窗口 1 中,我执行了以下命令,您也可以看到输出:

mysql> insert into ajax.zipcodes values(5, 'Wil', 'AK');
Query OK, 1 row affected (0.00 sec)

第 2 步 在第二个命令窗口中,我在命令下方触发并挂起(它似乎在等待提交命令成为前一个窗口的问题)

mysql> update ajax.zipcodes set city='Dublin' where zipcode=5;

第 3 步 我去了 Command window#1,并执行了 commit;你可以看到下面的输出:

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

同时,我可以看到之前挂起的第二个窗口也执行了命令并打印如下输出:

mysql> update ajax.zipcodes set city='Dublin' where zipcode=5;
Query OK, 1 row affected (3.63 sec)
Rows matched: 1  Changed: 1  Warnings: 0

第 4 步 现在,我在第二个窗口中发出提交,以确保即使在第二个 session 中也能正确提交所有更改:

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

第 5 步 现在,由于从两个窗口都发出了提交,我认为一切正常并且两个 session 也必须同步,所以我转到第一个命令窗口并发出以下命令:

mysql> select * from zipcodes where zipcode=5;
+---------+------+-------+
| ZIPCODE | CITY | STATE |
+---------+------+-------+
| 5       | Wil  | AK    |
+---------+------+-------+
1 row in set (0.00 sec)

我很惊讶,因为我期望 City 的值是 'Dublin' 因为来自第二个命令窗口的更改(即 update)已经在 第 4 步 中提交,但我仍然在 City 列中得到 Wil

我在这里做错了什么?

最佳答案

这与隔离级别有关。如果将隔离级别提高到 SERIALIZABLE(MySQL 中的默认值为 REPEATABLE READS),您将不会获得“幻读”。

Wikipedia page for database transaction isolation 中描述了隔离级别和幻读.

如果我像您一样运行此程序,但使用更高的隔离级别,我会得到您期望的结果。

第 1 节

mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)

mysql> SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE TABLE  `ajax`.`zipcodes` (
    ->   `ZIPCODE` varchar(5) NOT NULL,
    ->   `CITY` varchar(50) DEFAULT NULL,
    ->   `STATE` varchar(2) DEFAULT NULL,
    ->   PRIMARY KEY (`ZIPCODE`)
    -> ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Query OK, 0 rows affected (0.07 sec)

mysql> insert into ajax.zipcodes values(5, 'Wil', 'AK');
Query OK, 1 row affected (0.00 sec)

第 2 节

mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)

mysql> SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
Query OK, 0 rows affected (0.00 sec)

mysql> update ajax.zipcodes set city='Dublin' where zipcode=5;

第 1 节

mysql> commit;
Query OK, 0 rows affected (0.04 sec)

第 2 节

/* continued from previous (was frozen) */
Query OK, 1 row affected (7.54 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from zipcodes;
+---------+--------+-------+
| ZIPCODE | CITY   | STATE |
+---------+--------+-------+
| 5       | Dublin | AK    |
+---------+--------+-------+
1 row in set (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.04 sec)

mysql> select * from zipcodes;
+---------+--------+-------+
| ZIPCODE | CITY   | STATE |
+---------+--------+-------+
| 5       | Dublin | AK    |
+---------+--------+-------+
1 row in set (0.00 sec)

第 1 节

mysql> select * from zipcodes;
+---------+--------+-------+
| ZIPCODE | CITY   | STATE |
+---------+--------+-------+
| 5       | Dublin | AK    |
+---------+--------+-------+
1 row in set (0.00 sec)

注意:这并不一定意味着您应该始终使用SERIALIZABLE - 需要权衡取舍。最值得注意的是,数据库在执行 SELECT 时会获取范围锁,您会遇到更多基于锁定的冲突。

更新 - 显式处理交易

因为我们在这些脚本中设置了 autocommit=0;,所以我们确实应该明确地处理事务,而不是期待 START TRANSACTION - 尽管在大多数情况下,如果您执行了 START TRANSACTION,数据库的行为与您预期的一样。

但是,在显式开始和结束所有事务(包括那些只是SELECT的事务)时运行原始示例,您会得到不同的结果:

第 1 节

mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)

mysql> SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE TABLE  `ajax`.`zipcodes` (
    ->   `ZIPCODE` varchar(5) NOT NULL,
    ->   `CITY` varchar(50) DEFAULT NULL,
    ->   `STATE` varchar(2) DEFAULT NULL,
    ->   PRIMARY KEY (`ZIPCODE`)
    -> ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Query OK, 0 rows affected (0.07 sec)

mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into ajax.zipcodes values(5, 'Wil', 'AK');
Query OK, 1 row affected (0.00 sec)

第 2 节

mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)

mysql> SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
Query OK, 0 rows affected (0.00 sec)

mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)

mysql> update ajax.zipcodes set city='Dublin' where zipcode=5;

第 1 节

mysql> commit;
Query OK, 0 rows affected (0.04 sec)

第 2 节

/* continued from previous (was frozen) */
Query OK, 1 row affected (8.32 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from zipcodes;
+---------+--------+-------+
| ZIPCODE | CITY   | STATE |
+---------+--------+-------+
| 5       | Dublin | AK    |
+---------+--------+-------+
1 row in set (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.04 sec)

mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from zipcodes;
+---------+--------+-------+
| ZIPCODE | CITY   | STATE |
+---------+--------+-------+
| 5       | Dublin | AK    |
+---------+--------+-------+
1 row in set (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.04 sec)

第 1 节

mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from zipcodes;
+---------+--------+-------+
| ZIPCODE | CITY   | STATE |
+---------+--------+-------+
| 5       | Dublin | AK    |
+---------+--------+-------+
1 row in set (0.00 sec)

关于mysql - MYSQL 5 的奇怪行为(数据库隔离),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5727505/

相关文章:

mysql - 一列有 2 行相同的行,我想先显示它

sql-server - 需要好的方案/工作流程来使用 Subversion 管理数据库对象

sql-server - 复制具有标识列更改的一系列行 SQL Server 2014

Mysql自增复合

mysql - InnoDB MySQL 选择查询锁定

mysql - 按 id 和月份获取最后一个值、倒数第二个值和倒数第三个值

php - 根据来自不同表的 id 计算所有行

mysql - SQL 缺少记录问题

mysql - OpenShift添加墨盒MySQL 5.5引擎InnoDB

php - 无法将 FOR UPDATE 附加到标准推进