【问题标题】:Strange behaviour from MYSQL 5 (Database Isolation)MYSQL 5(数据库隔离)的奇怪行为
【发布时间】:2011-04-20 08:25:41
【问题描述】:

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

我在这里做错了什么?

【问题讨论】:

  • 表在更新操作期间被锁定?
  • 你用的是什么数据库引擎?
  • @Bobby: 一切都在那里:MySQL 和 InnoDB
  • @a_horse_with_no_name:哦,我错过了创建表,抱歉。​​
  • 在第 5 步,如果您从窗口 2 运行相同的选择 - 您会得到 Dublin 还是 Wil

标签: mysql database innodb transaction-isolation


【解决方案1】:

这与隔离级别有关。如果您将隔离级别提高到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 - 尽管在大多数情况下,如果您执行@987654334,数据库的行为与您预期的一样@。

但是,在显式开始和结束所有事务(包括那些只是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)

【讨论】:

  • REPEATABLE_READ 隔离级别不应该影响结果,因为我已经通过触发提交命令结束了两个窗口中的事务。所以当我在第 5 步时没有交易活动。
  • 我上面的 cmets 是基于我在网上看到的 REPEATABLE_READ 隔离级别:Repeatable Read 隔离级别允许事务在它返回给应用程序的所有数据行上获取读锁,并在其上写锁它插入、更新或删除的所有数据行。通过使用可重复读取隔离级别,在同一事务中多次发出的 SELECT SQL 语句将始终产生相同的结果。
  • @ Vicky - 我认为当你使用autocommit=0; 并且没有在每个事务之前显式执行START TRANSACTION; 时,MySQL 认为是“同一事务”有点奇怪。看看我上面的更新 - 它在适当的地方使用 START TRANSACTION; 并在仅使用 REPEATABLE READ 时获得您期望的结果
  • 是的...非常感谢您的详细解释。
  • 另外,我发现这个 URL 可以说明这种行为:dev.mysql.com/doc/refman/5.0/en/innodb-consistent-read.html。 MYSQL 中的“多版本并发控制”会导致这种行为。
猜你喜欢
  • 2019-06-14
  • 1970-01-01
  • 1970-01-01
  • 2023-03-19
  • 1970-01-01
  • 1970-01-01
  • 2010-12-27
  • 2012-02-02
  • 1970-01-01
相关资源
最近更新 更多