08事务隔离:事务到底是隔离的还是不隔离的

Posted 2021-04-25 22:30 +0800 by ZhangJie ‐ 1 min read

分享:  

启动事务

启动事务的方式有哪些:

  • autocommit=1,每条语句是一个独立的事务,比如select、update、delete;
  • 通过begin/start transaction来启动一个事务,但是该语句并不是事务的起点,起点是在后面的第一条sql语句执行的时候;
  • start transaction with consistent snapshot,立即启动一个新的事务,和begin/start transaction不同,该语句是一个事务的起点;

视图的概念

在mysql里,视图,有两种意思:

  • 一个是“view”,它是一个用查询语句定义的虚拟表,如执行create view select * from table,该语句执行的时候执行查询语句获得结果并创建视图,可以在视图上执行查询操作,查询语法与在表上的查询方式类似;

  • 另一个是InnoDB在实现MVCC时用到的“一致性读视图”,即consistent read view,用于支持RC(read commited,读提交)和RR(repeatable read,可重复读)隔离级别的实现;

    它没有物理结构,作用是事务执行期间用来定义“当前事务能看到什么数据”。

“快照”在MVCC里是怎么工作的

在可重复读隔离级别下,事务在启动的时候就“创建了个快照”,这个快照是基于整库的。

但是这里的创建快照,并不是复制一份完整的数据作为只读,肯定不能这样实现,想想一下一个数据库如果数量很大,复制的存储开销也太大了。

mysql MVCC里实现的这个快照非常聪明:

  • InnoDB里每个事务都有一个唯一的事务ID,叫transaction id。它是在事务开始的时候向InnoDB的事务系统申请的,是按申请顺序严格递增的。

  • 每行数据也是有多个版本的,这里的版本就用transaction id来表示。哪个数据版本更加新一点旧一点,还是根据生成该版本时的顺序来决定的,每行数据的transaction id则用来维护一个一致性读视图;

  • 当对某行数据进行更新操作时,会申请一个新的事务id,并插入新行数据,并更新字段trx_id为事务id,此时,插入了新的数据并不会删除旧的,旧的还是保存着的。但是新版的行数据有办法能找到旧版本的数据;

    注意新生成一个版本数据时,也会插入一行undo log,一个事务可以借助其事务id,从当前数据版本开始读,然后结合每行数据的trx_id和undo log,来读取到当前事务可见的数据版本,来实现一致性读视图,也就实现了可重复读;

    就是当前事务id可能是100,现在对应行的数据当前版本是102,100这个事务就顺着数据行的当前版本开始找,直到发现一个版本<=100时才行,也就保证了一致性读,这里就是根据数据行102版本的undo log找到前一条数据行,重复这个过程,直到发现一个版本<=100。

通过这种方式,实现了秒级快照的能力!

当前读(current read)

如果事务中涉及到一些更新类的操作的话,这里的更新是在数据“最新版本”上进行的更新,也就是说在“当前读”的版本上进行更新。后续的读,看上去读取到的就是最新值。

这可能会让我们觉得,与我们之前MVCC里面一致性读时说的一些有矛盾。其实没有矛盾的,只是更新操作的时候是在当前读的最新数据上进行更新。而后续读取的时候依然是按照MVCC里一致性读的方式来的。

如果更新时不是按照当前读来更新,那么就会造成以前已经提交的事务更新操作丢失了。

有几种办法可以实现当前读:

  • 更新操作肯定是当前读了;
  • select + lock in share mod,也是当前读;
  • select + for update,也是当前读;