锁
事务的隔离性由锁来实现
一、并发事务访问相同记录
1、读-读:并发事务相继读取相同的记录,不会引起什么问题。
2、写-写:并发事务对相同的记录做出改动。出现脏写问题,因此在多个未提交事务相继对一条记录做改动时,需要让他们排队执行。【加锁】
锁结构:trx信息:T1 这个锁结构是哪个事务生成的
is_waiting:false 当前事务是否还在等待
当T1改动了这条记录后,就生成了一个锁结构与该记录关联。因为之前没有别的事务为这条记录加锁,所以is_waiting属性是false,获取锁成功/加锁成功。然后就可以继续执行操作了。
在事务T1提交之前,另一个事务T2也想对该条记录做改动,那么先看看有没有锁结构与这条记录关联,发现有一个锁结构与之关联后,然后也生成了一个锁结构与这条记录关联,但是锁结构的is_waiting:true,表示当前事务需要等待。获取锁失败/加锁失败。
事务T1提交之后,就会把该事务生成的锁结构释放,然后看看还有没有别的事务在等待获取锁,发现事务T2还在等待获取锁,所以把事务T2对应的锁结构的is_waiting属性设置为false,然后把事务对应的线程唤醒,让它继续执行,此时T2就算获取到了锁。
3、读-写或写-读:一个事务进行读取操作,另一个进行改动操作。这种情况下可能发生脏读、不可重复读、幻读的问题。
解决脏读、不可重复读、幻读:
方案1:读操作利用多版本并发控制(MVCC),写操作进行加锁
MVCC:生成一个ReadView,通过ReadView找到符合条件的记录版本(历史版本由undo日志构建)。查询语句只能读到在生成ReadView之前已提交事务所做的更改,在生成ReadView之前未提交的事务或者之后才开启的事务所做的更改是看不到的。而写操作肯定针对的是最新版本的记录,读记录的历史版本和改动记录的最新版本本身并不冲突,也就是采用MVCC时,读写操作并不冲突。
方案二:读写都采用加锁的方式
MVCC:读写操作彼此不冲突,性能更高
加锁:读写操作彼此需要排队执行,影响性能