这与隔离级别有关。如果您将隔离级别提高到SERIALIZABLE
(MySQL 中的默认值为REPEATABLE READS
),您将不会获得“幻读”。
隔离级别和幻读在Wikipedia 页面上进行了描述,用于数据库事务隔离。
如果我像你一样运行它,但使用更高的隔离级别,我会得到你期望的结果。
第 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
- 需要权衡取舍。最值得注意的是,数据库在执行 a 时会获取范围锁,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)