我打开了两个命令窗口来使用我的数据库 (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/