Mysql实验:理解事务隔离级别

以下为文稿:具体可见 B站视频 实验理解 Mysql 事务隔离级别 欢迎关注:三岁于辛

今天的实验课开始前,我开启了三个终端连接我的数据库实例:第一个使用的库是 information_schema,它提供了访问数据库元数据的方式。另外两个使用的库是 awesome,也就是我们接下来要操作的数据库实例。

我们先看下 information_schema 中的表,它有很多,这里先埋个坑,后面有空都一一填上,今天只用到一张表作为辅助就可以了。那就是 INNODB_TRX,它提供了当前innodb引擎内每个事务的信息。我们看下它的表结构,字段很多,这里不展开,我们只用 trx_id,trx_state,trx_isolation_level 三个字段来辅助理解。先 select 下,当前它是空的。

我们回到刚才创建的另外两个 session 中,通过 begin 开启事务,然后在一个 session 中查询数据。这时我们再查看事务表中的信息,发现它已经有一条数据了,但是我们明明是开启了两个事务,为什么事务表中只有一条数据,这里可以理解为你只是告知数据库要开启事务了,但是接下来你是不是真的要操作呢?或者说你要如何操作呢?数据库是不知道的,所以只有基于你后续的行为之后数据库才知道你要做什么。这里有一个细节展开说的就是,操作对于数据事务来说,又可分为快照读和当前读的,快照读可理解为事务开始那一刻生成的内存快照,后续查询都基于那一时刻的数据,一般来说就是简单 select 时产生,就是我本例中的样子,看 trx_id 是一个很大的值,再展开的话就是视图的概念了,涉及到的内容就是 undo log + mvcc 了。这个坑后面填。而当前读就是读操作那一时刻,库里是什么样就是什么样,涉及的是磁盘+buffer 了。

接下来,我们在另一个终端也是查询数据,这时再看事务信息,已经是两条数据了,而且事务的隔离级别都是 RR 的。

好的,这时我在其中一个终端开始做更新操作后再看事务信息,发现刚才的 trx_id 已经变成了一个不大的值,这个值才可以被真正的理解为事务ID,仅仅是简单 select 操作是不会分配事务 ID 的。我们看到的超大值也是一个简单的计算值而已。我们两个 session 中各自查看数据,发现发生当前读的年龄已经是 18 了,但是快照读的数据依然是 20。确认下当前事务隔离级别都是 RR 的,所以我们可以知道 RR 级别是不会有脏读也不会有不可重复读的。

下面我们把事务提交,并把当前的事务隔离级别调整成读已经提交,也就 RC 级别。开启事务后我们查看事务表中的数据,发现已经多了一条 RC 级别的数据了。我们在非 RC 级别的 session 中把数据更新,再来看看数据是否已经被读到,这时 select 是没有变化的,但是当我们把非 RC 的 session 事务 commit 后。再看数据已经发生变化了,此次读到了 30 。这就是说在 RC 级别下是存在不可重复读的。

这时,我们把刚才提交事务的 session 隔离级别改成 read uncommitted。然后在 RC 级别的 session 中更改数据且不提交事务,我再看看另一个 session 中的数据,发现已经读到了 40 了。这就是 read uncommitted 级别下是会出现脏读的。

最后我们再来实验下幻读的情况,我们将刚才的脏读 session 改成默认的 RR 级别。然后确认我们的数据中最大的一个 ID 是 10,所以我们要查 ID 为 11 的数据肯定是不存在的。这里我在 RC 级别的 session 中插入一条 ID 为 9 的数据。在 RR 的 session 中直接查找 ID 为 11的数据是不存在的,但是当我们也想插入一条 ID 为 11 的数据时就出现了重复 key 的报错了。这就是说在 RR 级别下是存在幻读的。

今天的实验到此结束,最后总结下。

事务隔离级别 脏读 不可重复读 幻读
Read Uncommitted
Read Committed (RC)
Repeatable Read (RR)
Serializable

Mysql实验:理解事务隔离级别
https://blog.isnap.cn/posts/f020ccb1/
作者
三岁于辛
发布于
2022年6月22日
许可协议