数据库mysql+MVCC
1.mysql事务隔离级别
ACID:
原子性:undo log(mvcc)
一致性:由原子性,持久性,隔离性保证
隔离性:锁
持久性:redo log
-
读未提交,读已提交,可重复读,串行化。
-
读未提交:一个事务读取到其他事务没有提交的数据,这个称为脏读。
-
读已提交:事务读取已提交的数据,在一个事务中,读取的数据在过程中被另外一个事务修改了,造成两次读取的数据不一致,这种情况称为不可重复读。
-
可重复读:mysql默认的隔离级别,它解决了脏读的问题,同时保证了一个事务内的多次读取数据是一致的,但是还是会产生幻读的情况,幻读是指当一个事务查询一个范围内的数据,另一个并发的事务往这个范围插入或者删除数据,造成事务A再次查询这个范围的数据,两次查询结果集不同。
-
串行化:最高的隔离级别,事务之间的执行是串行的,事务之间不会造成干扰。
MVCC机制
- 当前读:像select lock in share mode(共享锁),select for update(排他锁),这些操作都是一种当前读,以为读取的是记录的最新版本,读取时还要保证不能被其他事务修改会对读取的记录加锁。
- 快照读:读取到的不一定是最新版本,可能是之前的历史版本,像不加锁的select操作就是快照读,是为了提高数据库的并发查询能力,基于多版本并发控制来实现(MVCC)。
mvcc实现原理主要依赖记录中的隐式字段,undo log,read view来实现的。
** 隐式字段:**
每一行除了自定义的字段外,还有数据库的隐式的DB_TRX_ID,DB_ROW_PTR,DB_ROW_ID等字段。
- DB_TRX_ID:6字节,记录最近修改这条记录的事务id。
- DB_ROW_PTR:7字节,回滚指针,指向这条记录的上一个版本,配合undo log日志指向该记录的上一个版本。
- DB_ROW_ID:如果数据表没有主键,那么innodb会自动生成一个6字节的row_id。
undo log日志
被称为回滚日志,在进行insert,update,delete时产生的方便回滚日志。
当进行insert时,产生的undo log日志只在回滚的时候需要,在事务提交之后可以被删除。
当进行update和delete时,产生的undo log日志不仅会在回滚的时候需要,在快照读的时候也需要,因此不能随便删除。
undog 日志生成链
1.某个事务编号为1的事务向表中插入一条记录,此时的数据状态为:
2.假设第二个事务编号为2的事务对name做出修改,在修改时,会对当前行加排它锁,并把当前行数据拷贝到undo log日志中,作为旧记录,拷贝完毕,修改name字段,并将隐藏字段的事务id(DB_TRX_ID)修改为2,将回滚指针(DB_ROW_PTR)指向undo log日志中的副本记录,事务提交后,释放锁。
3.假设又有一个事务修改当前行的age字段,在修改时,加排它锁,并将当前行数据复制到undo log日志,发现undo log日志中已经有当前行的记录了,那么最新的旧数据作为链表的表头,插在当前行的undo log日志的最前面,并修改当前行的age字段,隐藏的事务id字段为3,回滚指针指向刚刚拷贝的undolog的副本记录。事务提交,释放锁。
Read View
最大的作用是来做可见性判断的,当某个事务在执行快照读的时候,对该记录创建一个read view的视图,把它作为条件去判断当前事务能看到那个版本的数据,有可能读到的是最新数据,也有可能读到的是undolog中的某个版本的数据。
Read view遵循的可见性算法主要是将要修改得到记录的事务id(DB_TRX_ID)取出来,与系统中其他的活跃的事务id进行对比,如果DB_TRX_ID与readview的属性做了对比,不满足可见性,那么通过回滚指针(DB_ROW_PTR)取出redo log中的事务id(DB_TRX_ID)作比较,遍历链表中的事务id,直到找到满足条件的DB_TRX_ID,那么这个事务id对应的记录就是当前事务能看到的最新的老版本的数据。
- read view的可见性规则:
read view 中的三个全局属性: - trx_list:维护read view生成时活跃的事务id(1,2,3)
- up_limit_id:记录trx_list列表中事务id的最小id (1)
- low_limit_id:read view生成时刻下一个尚未分配的事务的id(4)
具体的比较规则:
1.若DB_TRX_ID<UP_LIMIT_ID,则当前事务能看到DB_TRX_ID所在的记录。否则进入下一个判断。
3.mvcc的整体处理流程
假设四个事务在同时执行:
4.RC,RR级别下的innodb快照读有什么区别?
生成ReadView的时机不同,从而造成RC,RR级别下的快照读结果不同。
RC级别下ReadView在查询行记录的时候生成。
RR级别在开启事务之前生成。
5.mysql幻读是怎么解决的?
幻读:事务A按某条件进行查询,期间事务B插入相同条件的一些数据,事务A再次查询,两次查询的结果不一样。
如果查询是快照读不会产生幻读的情况,只有快照读加当前读一起的时候才会差生幻读。
如果产生幻读一般是通过for update来解决,本质是通过临键锁来解决的。