如何制作幻像读物?

时间:2011-03-26 19:55:10

标签: mysql sql database isolation-level

使用“可重复读取”应该可以产生幻像读取,但是如何?我需要一个教学CS学生的例子。

我认为我必须在非索引字段x上创建一个“SELECT ... WHERE x< = 888”,上限888不存在,然后在另一个连接上插入一个新行,其值恰好在下面888。

除非它不起作用。我需要一张非常大的桌子吗?或其他什么?

6 个答案:

答案 0 :(得分:15)

埃里克

我来自测试它的行数非常多。

你永远不会在InnoDB mysql上发现有读取提交或更严格的隔离级别的幻像。它在文档中解释:

REPEATABLE READ:对于一致性读取,与READ COMMITTED隔离级别存在重要差异:同一事务中的所有一致性读取读取第一次读取建立的快照。此约定意味着如果在同一事务中发出多个普通(非锁定)SELECT语句,则这些SELECT语句也相互一致。请参见第13.6.8.2节“一致的非锁定读取”。

但是您也无法在读取提交隔离级别中找到幻像:这是必要的,因为必须阻止“幻像行”才能使MySQL复制和恢复正常工作。

更详细的信息:http://dev.mysql.com/doc/refman/5.1/en/set-transaction.html

我认为您需要转移到另一个数据库品牌,以向您的学生展示幻影。我同时使用MSSQLSERVEROracle

嗯......对你的第一个问题很遗憾。

答案 1 :(得分:8)

"幻像阅读"在MySQL上,RR隔离级别隐藏得很深,但仍然可以重现它。以下是步骤:

  1. create table ab(一个int主键,b int);

  2. TX1:
        开始;
        从ab中选择*; //空集

  3. Tx2:
        开始;
        插入ab值(1,1);
        承诺;
  4. Tx1:
        从ab中选择*; //空集,预期幻像读缺失。
        更新ab set b = 2其中a = 1; // 1行受影响
        从ab中选择*; // 1行幻影在这里阅读!!!!
        提交;

答案 2 :(得分:4)

为隔离级别重现InnoDB引擎的幻像读取的可能性REPEATABLE READ是值得怀疑的,因为InnoDB使用Multiversion concurrency control - 对于每一行,MVCC引擎在插入和删除行时都知道事务编号,并且可以重现行更新的历史记录。

因此,所有后续的SELECT语句都将在事务开始时显示表的状态,但同一个事务插入,删除或更新的行除外。不会出现其他事务提交的新行,因为它们的插入事务编号大于此事务的插入事务编号,并且行的范围无关紧要。

我能够为Apache Derby数据库重现PHANTOM READS的隔离级别REPEATABLE READ,因为它不使用多版本并发控制(在编写此答案的时刻版本10.8.2.2)。

要重现,请设置正确的事务级别(在ij中 - Derby的SQL客户端):

-- Set autocommit off
autocommit off;
-- Set isolation level corresponding to ANSI REPEATABLE READ
set isolation rs;

T1:

SELECT * FROM TableN;

T2:

INSERT INTO TableN VALUES(55, 1);
COMMIT;

T1再次:

SELECT * FROM TableN;

现在T1应该再看一行;

答案 3 :(得分:2)

InnoDB应该像其他人所写的那样防止幻像读取。

但InnoDB有一个与锁定相关的不同奇怪行为。当查询获取锁时,它始终获取该行的最新版本的锁。请尝试以下

CREATE TABLE foo (i INT PRIMARY KEY, val INT);
INSERT INTO foo (i, val) VALUES (1, 10), (2, 20), (3, 30);

然后在两个并发会话中(打开两个终端窗口):

-- window 1                               -- window 2
START TRANSACTION;
                                          START TRANSACTION;

                                           SELECT * FROM foo;

 UPDATE foo SET val=35 WHERE i=3;

                                           SELECT * FROM foo;

这应该在两个SELECT中显示val = 10,20,30,因为REPEATABLE-READ意味着第二个窗口只看到事务开始时存在的数据。

然而:

                                           SELECT * FROM foo FOR UPDATE;

第二个窗口等待获取第3行的锁定。

COMMIT;

现在第二个窗口中的SELECT完成,并显示val = 10,20,35的行,因为锁定行会导致SELECT查看最新提交的版本。 InnoDB中的锁定操作就像它们在READ-COMMITTED下运行一样,无论事务的隔离级别如何。

你甚至可以来回切换:

                                           SELECT * FROM foo;

                                           SELECT * FROM foo FOR UPDATE;

                                           SELECT * FROM foo;

                                           SELECT * FROM foo FOR UPDATE;

答案 4 :(得分:0)

可能会发生幻像读取,因为不存在范围锁,那么一个例子是(伪代码):

线程1

Transaction 1

Update TableN set X=2 where X=1

wait(s1)
Select TableN where X=1

Commit 

线程2

Transaction 2:

insert into tableN(id, X) values(55,1)
commit;
notify(s1)

在维基百科中还有另一个幻像读取示例:Phantom Reads|wikipedia

这里重要的是事务同步,你可以使用同步点。

编辑使用mysql睡眠功能的示例(未测试):

--on thread 1
Create TableN(id int, x int);
insert into TableN(id, X) values(1,1);
insert into TableN(id, X) values(2,1);
insert into TableN(id, X) values(3,1);

BEGIN TRANSACTION; Update TableN set X=2 where X=1 SELECT SLEEP(30) FROM DUAL; select TableN from where X=1; COMMIT;

--In other thread, before 20 secs;

BEGIN TRANSACTION; insert into TableN(id, X) values(55,1);

COMMIT;

答案 5 :(得分:0)

为了补充Dani的好答案,您可以使用Microsoft Sql Server向学生展示这种行为。

Sql Server按文档here声明的可重复读隔离级别显示幻像读取。

Postgres订阅与InnoDb相同的概念,如here所述。使用Postgres时,在可重复读取时不会发生幻像读取,因此也不适合您的说教目的。

Sql Server提供另一个隔离级别,即快照,它可以执行MySql InnoDb和Postgres在可重复读取中执行的操作(这是一个无锁,基于版本的可重复读取实现,没有幻像读取,但不可序列化)。

虽然您确实需要Windows机器,但Sql Server Express是免费的。您还可以获得一个Windows Azure帐户,并在线显示Sql Azure的行为。