如何在REPEATABLE READ?(MySQL)中产生“幻像读取”

r1zhe5dt  于 2023-02-11  发布在  Mysql
关注(0)|答案(7)|浏览(244)

使用“可重复读取”,应该可以产生一个幻像读取,但如何做到呢?我需要它作为一个例子,教计算机科学的学生。
我认为我必须在一个非索引字段x上执行“SELECT ... WHERE x〈=888”,其中不存在上限888,然后在另一个连接上插入一个值刚好小于888的新行。
但它不起作用。我需要一张很大的table吗?还是别的什么?

x33g5p2x

x33g5p2x1#

RR隔离级别上MySQL中的"幻像读"隐藏得很深,但仍然可以重现,步骤如下:
1.创建表ab(a int主键,b int);
1.治疗1:
开始
从ab中选择 *;//空集
1.治疗2:
开始
插入ab值(1,1);
承诺;
1.治疗1:
从ab中选择 *;//空集,缺少预期的幻像读取。
更新ab,设置b = 2,其中a = 1;//1行受影响。
从ab中选择 *;//1行。在此处读取幻像!!!
承诺;

6jygbczu

6jygbczu2#

艾瑞克
我刚从测试中得到它有非常多的行数。
在InnoDB mysql上,你永远不会找到带有read commited或更严格隔离级别的幻像。
可重复读取:对于一致性读取,与READ COMMITTED隔离级别有一个重要区别:同一事务中的所有一致读取都读取第一次读取建立的快照。此约定意味着,如果在同一事务中发出多个纯文本(非锁定)SELECT语句,则这些SELECT语句彼此之间也是一致的。请参见13.6.8.2“一致的非锁定读取”一节。
但您无法在已提交读取的隔离级别中找到幻像:这是必要的,因为必须阻止“幻像行”才能使MySQL复制和恢复正常工作。
更多详细信息:http://dev.mysql.com/doc/refman/5.1/en/set-transaction.html
我认为您将需要转移到另一个数据库品牌来向您的学生显示幻影。我同时使用MSSQLSERVEROracle
嗯......很遗憾你的第一个问题。

ffvjumwh

ffvjumwh3#

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;
ssm49v7z

ssm49v7z4#

对于隔离级别REPEATABLE READ,InnoDB引擎重现幻像读取的可能性值得怀疑,因为InnoDB使用Multiversion concurrency control-对于每一行,MVCC引擎知道插入和删除行时的事务号,并且可以重现行更新历史。
因此,所有后续SELECT语句都将在事务开始时显示表的状态,但由同一事务插入、删除或更新的行除外。不会出现由其他事务提交的新行,因为它们的插入事务号将大于此事务的插入事务号,并且行的范围在此无关紧要。
我能够为Apache Derby数据库再现隔离级别REPEATABLE READ的幻像读取,因为它不使用多版本并发控制(在编写本答案时版本为www.example.com)。10.8.2.2 in the moment of writing of this answer).
要重现,请设置适当的事务级别(在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应该看到另一行;

hmtdttj4

hmtdttj45#

您可以在MySQL的**REPEATABLE READ中生成幻像读取**。
首先,设置**REPEATABLE READ**:

SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

然后,使用**idname创建person表格**,如下所示。

person表格:

| 身份证|姓名|
| - ------|- ------|
| * * 一个**|"约翰"|
| * * 二**|* * 大卫**|
然后,执行以下步骤MySQL查询。* 我使用了2个命令提示符
| 流量|交易1(T1)|交易2(T2)|解释|
| - ------|- ------|- ------|- ------|
| * * 步骤1**|* * 一米六米一x**||* * T1启动。|
| * * 第二步
||* * 一米七三**|* * T2启动。|
| * * 步骤3
|* * 一米八米一x*** * 约翰一号*** * 2大卫**||* * T1读取2行。|
| * * 步骤4
||* * 一米九十一**|* * T2将包含3Tom的行插入到person表格中。|
| * * 第五步
||* * 一米十三十一x**|* * T2提交。|
| * * 第六步
|* * 一米十四分一秒*** * 约翰一号*** * 2大卫**||* * T1在T2提交后读取2行。####* 暂时不会发生幻像读取!!|
| * * 第七步
|* * 一米十五纳一x**||* * 令您惊讶的是,T1可以将T2刚刚插入的新行从Tom更新为Lisa|
| * * 第8步
|* * 1米18英寸1x*** * 约翰一号*** * 2大卫*** * 3丽莎**||* * 令您惊讶的是,T1在T2提交后读取了3行。####* 发生幻像读取!!|
| * * 第七步
|* * 一米十九寸**||* * T1提交。|
此外,我在
Postgresql中的REPEATABLE READ中执行了上述步骤,但没有发生幻像读取**。

dced5bon

dced5bon6#

由于不存在范围锁,因此可能发生幻像读取,示例如下(伪代码):
螺纹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)

在维基百科还有另一个例子幻影读:体模读数|维基百科
这里重要的是事务同步,您可以使用同步点。

EDIT使用mysql sleep函数的示例(未测试):

``

--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;

``

snz8szmq

snz8szmq7#

为了补充Dani的正确答案,您可以使用Microsoft Sql Server向您的学生展示这种行为。
Sql Server显示了文档here中声明的可重复读取隔离级别中的幻像读取。
正如here所解释的那样,Postgres与InnoDb采用了相同的概念,在可重复读取中也不会发生幻像读取,因此也不适合您的教学目的。
Sql Server提供了另一个隔离级别,快照,它执行MySql InnoDb和Postgres在可重复读取中执行的操作(这是一种无锁、基于版本的可重复读取实现,没有幻像读取,但不可序列化)。
Sql Server Express是免费的,尽管你确实需要一台Windows计算机。你也可以为自己获得一个Windows Azure帐户,并在线显示Sql Azure的行为。

相关问题