隔离级别,不跳过任何数据

kfgdxczn  于 2021-06-20  发布在  Mysql
关注(0)|答案(1)|浏览(339)

假设我们想从mysql(innodb)表中读取新事件 event . 我们记得我们看到的最后一个(自动递增)id,并查询 WHERE id > @LastSeenId .
如果ID1到ID10可用,除了ID4还未提交之外,该怎么办?
显然,重要的是我们永远不要跳过任何可能存在的行。如果我们跳过id 4,我们将再也看不到它,而且将永远错过它,这是必须防止的。
我假设这取决于查询运行的隔离级别。
1我理解得对吗 Serializable (没有其他级别)将提供所需的行为?
也就是说,它将等待影响与条件匹配的行的任何未提交事务的提交/回滚( id > @LastSeenId )?
2更具体地说,如果我们不显式地使用数据库事务,那么结果是否由默认隔离级别确定(即使是对于单个事务) SELECT 询问?
对于上下文,我们使用官方的mysql connector for.net。

mftmpeh8

mftmpeh81#

这是你可以用mysql客户端和两个windows来测试自己的东西。
打开window1,进入mysql客户机,创建一个表,并用将要提交的值填充它。

mysql1> use test;

mysql1> create table event (id serial primary key);

mysql1> insert into event values (1), (2), (3), (5);

现在开始翻译。插入值4,如示例所示。

mysql1> begin;

mysql1> insert into event values (4);

不要提交最后一次插入。
打开window2,进入mysql客户端,为会话设置事务隔离,查询数据范围。

mysql2> use test;

mysql2> set tx_isolation = serializable;

mysql2> begin;

mysql2> select * from event where id >= 1;

select在此阶段挂起,等待。
这是因为在serializable级别,所有select查询都隐式地尝试获取共享锁,就好像添加了 LOCK IN SHARE MODE (或 FOR SHARE 在mysql 8.0语法)子句中添加到select查询的末尾。这是锁定读取。它试图在id值的范围内获取一个间隙锁,但它还不能获取该锁,因为window1创建了一个未提交的行,该行在该范围内。
现在在窗口1中,提交事务(确保在50秒后窗口2超时之前执行此操作):

mysql1> commit;

窗口2立即返回,现在它看到了完整的数据值集。

mysql2> select * from event where id >= 1;
+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
|  4 |
|  5 |
+----+

现在尝试在窗口1中插入新行:

mysql1> begin;

mysql1> insert into event values (6);

现在这个Windows挂了。为什么?因为它试图锁定它插入的行,但是window2仍然在行的范围上保持一个间隙锁 where id > 1 -其中包括新值6。
这就是mysql确保可重复读取的方式。它使用间隙锁来防止插入新行,因为新行会影响当前事务试图保留的行集,新行将进入它锁定的范围。
最后,如果窗口2没有完成其事务并释放其间隙锁,窗口1将超时:

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

所有这些都与自动增量无关。正如您在我的示例insert中所看到的,我正在重写自动增量。它只与行和间隙上的锁有关,而不管这些行中的值是如何生成的。
我还要指出,您试图解决的问题本质上是发布/订阅模型,它更适合于消息队列技术,而不是rdbms技术。您应该考虑使用消息队列作为rdbms的补充技术。

相关问题