一、有什么用?解决了什么问题
mvcc多版本并发控制,为了提高是数据库并发性能,来解决我们的读写冲突问题,以不加锁的方式来解决读写冲突问题
二、靠什么实现
隐藏字段
数据库的表在定义的时候,除了我们声明的字,另外还会包含一些隐藏字段 DB_TRX_ID:最近修改的事务ID,创建这条记录或者最后一次修改该记录的事务id DB_ROLL_PTR:回滚指针,指向了记录的上一个版本 DB_ROW_ID:隐藏主键,如果表没有主键的话,会自动生成一个6字节的rowId 执行步骤: 1.加锁 2.把老数据拷贝到undolog 3.修改数据 4.将回滚指针指向老记录 5.事务提交释放锁
undolog
回滚日志,在执行inser delete updata 操作时时候,方便回滚的日志 undulog会形成一个版本链表,链首存的是版本的最新旧纪录,链尾存储的是版本最旧的旧记录。 undulog版本不会一直叠加,purge线程会进行异步删除
案例
事务一:插入一条语句,此时上一个记录的版本地址为null
事务二:将name修改为李四 1.加锁 2.将nane为张三(修改之前你的数据拷贝到undolog)
3.修改数据 4.将回滚指针指向老记录
事务三:将age修改为21 2.将nane为李四(修改之前你的数据拷贝到undolog) 3.修改数据
4.将回滚指针指向老记录
readView
读视图,在事务进行快照读的是产生的读视图,保存的并不是实际的数据,而是事务的相关信息 包含三个非常重要的字段 trx_list:当前系统活跃的事务ID的列表 up_limit_id:活跃例表中事务ID最小的值 low_limit_id:系统尚未分配的下一个事务ID
案例一
此时select是读取的到最新数据的 在事务1t3时间段,select操作会触发快照读,就会产生读视图
可见性算法
1.首先比较DB_TRX_ID<UP_LIMIT_ID如果小于,则当前事务能看到DB_TRX_ID所在的记录,如果大于等于进入下一个判断。 2.接下来判断DB_TRX_ID>=low_limit_id,如果大于等于则代表DB_TRX_ID所在的记录在ReadView生成后才出现,那么对于当前事务肯定不可见,如果小于,则进入下一个判断 3.判断DB_TRX_ID是否在活跃事务中,如果在,则代表ReadView生成时刻,这个事务还是活跃状态,还没有Commit ,修改的数据,当前事务是看不到的,如果不在,则说明这个事务在ReadView生成之前就已经开始Commit,那么修改的结果是能看见的。
案例二
select🩱:所产生的快照读ReadView
select👙;所产生的快照读ReadView
根据我们现在的理解select👙读取的数据是最新的数据? 但是并不是最新数据,案例一和select👙所产生的readView是一样的,可见性算法也是一样的,为什么读取不到最新数据。 因此大胆做一个猜测,select🩱;部分使用的是readView跟select👙的readView是同一个readView 除了事务ID其他都和select🩱产生的readView一致
通过这个readView读取不到最新数据的,因此第二次的ReadView沿用了第一次产生的ReadView
三、什么时候沿用readView?
这个时候就是和隔离级别挂钩了 如果是RC隔离级别,那么每次在进行快照读的首都会生成新的ReadView 如果是RR隔离级别,那么只有在当前事务第一次进行快照读的时候生成readView,之后的快照读都会沿用第一次生成的readView
总结
隔离级别是可调控的参数,通过用户控制这个可调控的参数,控制每次看到的数据,MVCC和ReadView是实现机制的体现。不管是RC还是RR都避免不了产生幻读的问题 幻读 幻读是通过加锁的方式来解决的 产生幻读的根本原因:当前读和快照读一起使用,如果一个事务只有一个快照,那么永远不会出现幻读原因。
|