对应的类型是TRX_UNDO_INSERT_REC
假设现在需要回滚一条insert语句,只需要把undo log拿出来,就知道在哪个表里插入的数据,主键是什么,直接定位到哪个表和主键对应的缓存页,从里面删除掉之前insert语句插入进去的数据就可以了。
read uncommitted,读未提交,不允许脏写(第一类丢失更新)发生,因为它不允许两个事务在没提交的情况下去更新同一行的数据的值。
RC,RR,serialize
MySQL依托于MVCC机制,就能让RR级别避免不可重复读和幻读的问题。
每条数据都有两个隐藏的字段,一个是trx_id,一个是roll_pointer,trx_id就是最近一次更新这条数据的事务id,roll_pointer就是指向更新了这个事务之前生成的undo log。
上图是多事务并发时候数据行以及undolog的内容。
执行一个事务的时候,生成一个ReadView,里面比较关键的东西有4个:
一开始的时候,数据库如下:
这时候,两个事务并发执行过来了,事务A(id=45)和事务B(id=59),事务A直接开启一个ReadView,ReadView里的m_ids就包含了事务A和事务B的两个id,45和59。然后min_trx_id就是45,max_trx_id就是60,creator_trx_id就是45,是事务A自己。事务A第一次查询这行数据时,判断当前这行数据trx_id是否小于ReadView中的min_trx_id,发现trx+id=32,是小于ReadView里的min_trx_id就是45的,说明事务A之前,这个事务早已经提交了,所以可以查到这行数据。事务B开始执行,将数据修改为值B,trx_id设置为自己的id,59。同时roll_pointer指向了修改之前生成的一个undo log,接着这个事务B就提交了。如图所示:
事务A再次查询,发现trx_id为59,那么这个trx_id是大于ReadView的min_trx_id(45),和小于ReadView里的max_trx_id(60)的,说明更新这条数据的事务,很可能就是跟自己差不多同时开启的,于是会看一下trx_id=59,是否在ReadView的m_ids列表里。查找,发现果然是。于是顺着undo log日志链条往下找,找到最近一条undo log,trx_id是32,发现trx_id=32,是小于ReadView的min_trx_id(45)的,说明undo log版本必然是在事务A开启之前就执行提交的,于是就查询此undo log里的值。这就是undo log多版本链的作用,它可以保存一个快照链条,让事务可以读到之前的值。
RC级别的核心是每次发起查询都重新生成一个ReadView。第一次查询的时候,发现数据的trx_id在自己的min和max之间和m_ids之中,所以它是一个活跃的事务修改的,并且此时尚未提交,所以它会顺着undo log版本链查找以及提交的事务。第二次查询的时候,重新开启一个新的READ VIEW,发现数据的trx_id不在m_ids中,说明该事务已经提交,所以直接读取数据行的数据。这就是RC级别的实现原理。
可重复读的核心是只在事务开始时生成一个ReadView,后续再次读的时候,即使事务B提交了,但是事务A它的ReadView还是刚开始事务时的,所以认为B还是活跃的,所以继续沿着undo log版本链查询,读取到始终一样。MySQL的可重复读还解决了幻读,因为新插入的数据记录的trx_id要么大于事务A的max_trx_id,要么在m_ids之中 (而且非本身)所以认为插入的数据记录时不能被读取的。
之前我们讨论的其实都是别人在更新数据的时候,你怎么读的问题,脏读,不可重复,幻读。接下来看看写的问题。
事务A先加锁了,等待锁状态为false,事务B想要修改同一行数据,也尝试获取锁,将自己的等待状态设置为true,事务A修改完之后,将事务B的等待状态改为false,事务B就获取到锁了。
当有人在更新数据的时候,其他事务读取这行数据,默认情况下需要枷锁吗?不用,因为基于mvvc机制,不会去读最新的值,这样子也避免了频繁加锁。如果要自己加锁也可以:
-- 加共享锁
select * from table lock in share mode
-- 加排他锁
select * from table for update
DDL语句会阻塞所有增删改操作,但这是通过MySQL通用的元数据锁实现的,也就是Metadata Locks,这不是表锁的概念。表锁其实InnoDB存储引擎的概念,InnoDB存储引擎提供了自己的表级锁,跟DDL的元数据锁不一样。
如果事务在表里执行增删改操作,那在行级会加独占锁,此时同时会在表级加一个意向独占锁,如果有事务在表里执行查询操作,那么会在表级别加一个意向共享锁,而不会加行级共享锁。意向独占锁和意向共享锁他们时不会互斥的。
上面描述的是表级独占锁和表级共享锁,以及更新数据和查询数据库默认自动加的意向独占锁和意向共享锁,他们互相之间的互斥关系。实际上查询一行数据时,MySQL是不会帮我们加表级或行级锁的。