来自MYSQL 5的奇怪行为(数据库隔离)

时间:2011-04-20 08:25:42

标签: 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步 我去了命令窗口#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步 现在我在第二个窗口中发出commit,以确保即使第二个会话也能正确地提交所有更改:

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

第5步 现在既然已经从两个窗口发出了提交,我认为一切都很好,两个会话也必须同步,所以我进入第一个命令窗口并发出以下命令:

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

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

我在这里做错了什么?

1 个答案:

答案 0 :(得分:4)

这与隔离级别有关。如果将隔离级别提高到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)

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

更新 - 明确处理交易

由于我们在这些脚本中设置了autocommit=0;,我们确实应该明确地处理事务,而不是期望START TRANSACTION - 尽管在大多数情况下,数据库的行为与您期望的一样{ d执行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)