阅读 172

MySQL实践:事务隔离级别

前言

  最近学习了《高性能MySQL》第一章,学习到了事务隔离级别这个概念,感觉这块比较容易用实验来验证,每个级别的预期结果都很明确,所以自己进行了小实验来验证各个隔离级别。

隔离级别简介

  SQL标准中定义了4种事务的隔离级别,分别为:

  • READ UNCOMMITTED(未提交读):事务中的修改,即使没有被提交,对其它事务也是可见的。
  • READ COMMITTED(提交读):一个事务只能看见已提交的事务所做的修改。
  • REPEATABLE READ(可重复读):保证一个事务中多次读取同样记录的结果是一致的。
  • SERIALIZABLE(可串行化):强制事务串行执行,在读的每一行数据上都加锁。

事前准备

用到的配置

  数据库用的是腾讯云MySQL,版本为5.7;MySQL存储引擎为InnoDB;客户端为Navicat,版本为11。运行时在Navicat里开两个命令行界面,一个称为A,一个称为B,用它们来模拟操作:

image.png

表的结构

  用的表比较简单,如下:

image.png

查看和更改隔离级别

  首先用show variables like 'tx_isolation';来查看目前的事务隔离级别如下: image.png

  由于我们要做关于事务隔离级别的实验,所以需要更改事务隔离级别,用set [作用域] transaction isolation level [事务隔离级别]进行更改,作用域可以取‘session’和‘global’,代表会话级和全局,事务隔离级别就是上面提到的四个级别的英文单词。

  如set session transaction isolation level read uncommitted;就是进行了会话级的更改,将事务隔离级别改为了'read uncommitted'J(未提交读)。如下,可以轻松更改。

image.png

  接下来对4个事务隔离级别进行验证。

未提交读

  首先运行语句更改事务隔离级别,上面已经展示了,这里不赘述了,下同。
初始时,两个客户端查询内容是一样的

image.png

  在A中开始事务并运行update person set name = 'bob' where id = 1更改记录,但不提交。

image.png

  接着在B中查看记录。

image.png

  在A中并没有提交事务,B中却读到了A中的更改。

  在A中执行rollback,发现B中记录又变为了之前的记录。

image.png

image.png

  这就是未提交读了,事务没有提交,修改都可以被别的事务看见,这也被称为‘脏读’。

提交读

  初始数据如下:

image.png

  在A中开始事务运行update person set name = 'bob' where id = 1更改记录,但不提交。

image.png

  接着在B中查看记录。

image.png

  可以看出,未提交的事务语句,没有对B的查询起到影响。接下来提交A的事务。

image.png

image.png

  此时,B中的数据得到了改变,这就是提交读,只能看见别的事务提交了的修改,解决了‘脏读’的问题。但是同一个事务里两次读到的数据不一样,这被称为‘不可重复读’。

可重复读

  初始数据如下:

image.png

  在A中开始事务并运行update person set name = 'rose' where id = 1更改记录,但不提交。

image.png

  接着在B中查看数据。

image.png

  在B中,并没有看到这次更改,接着提交A中的事务。

image.png

image.png

  即便A的事务已经提交,B中还是没有看见。那么可重复读级别解决了‘脏读’和‘不可重复读’的问题。

  《高性能MySQL》一书里提到:在可重复读这个级别,虽然解决了‘脏读’和‘不可重复读’的问题,但是还有‘幻读’这个问题。所谓‘幻读’就是某事务在读取某个范围内的记录时,另一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行。书上还说,InnoDB通过MVCC和间隙锁解决了这个问题,让我们来看看是不是这样。在A中运行insert into person values(3,'rose');来插入一条记录并且提交:

image.png

  在B中进行查看

image.png

  在B中确实没有读到新纪录。不过如果我们在B中运行插入语句insert into person values(3,'bob')如下:

image.png

  发现并不能插入成功,提示插入的记录主键重复了。。。当然,这种情况或许不能称为‘幻读’了。

可串行化

  在A中执行读操作后,在B中执行update 语句,发现一直在等待。

image.png

image.png

  提交了A的事务之后,B立马成功。

image.png

image.png

  说明即使是读也会加锁,几乎无法多事务并发。

总结

  本文对MySQL的各个隔离级别进行了实验验证,直观的体验了各个隔离级别的特点。之后需要对MVCC的原理和间隙锁的原理进行探究。

文章分类
后端
文章标签