赞
踩
在网上有一个总结的很好的一句话,在这里记下来:
说一下二阶段提交
表示redo log分为prepare阶段和commit阶段这样的两个阶段提交。它的流程是这样的:
为什么需要二阶段提交?
用反证法来证明,如果没有二阶段提交:
数据恢复阶段会使用到binlog和redo log:
binlog和redolog的组提交:
如果数据的并发量较高的话,即使是顺序的写入磁盘,频繁的IO操作会使得服务器的性能大幅下降。为了提高磁盘的写入效率,可以把多个日志攒到一起再写入磁盘,这就是组提交。
而由于使用了binlog和redo log,所以这两个日志是都要使用组提交的,不然没使用组提交的哪一个一样会成为瓶颈所在。
这个组提交和之前的双一设置我感觉应该是不冲突的,每次写binlog或者redo log的时候都会等待多个事务一起过来成组的写入,依然是符合双一设置的,不过就是事务还会有一个等待的过程。
它主要有三个阶段:
1.flush阶段
2.sync阶段
3.commit阶段
它的每个阶段都分别有一个队列和一把锁,他们的流程如下:
flush阶段:
sync阶段:
commit阶段:
上面是关于mysql事务的提交及binlog、redolog原理的讨论
一些问题:
1.redo log写入磁盘的时机?
redo log是在commit提交的时候才会将其持久化到磁盘,而在prepare阶段也会写入redo log文件一些记录,在网上看到了大量的文章,有些提到prepare阶段是只是写入到文件,有些提到是写入到磁盘。
那么我就说说我的看法把,我认为redo log在二阶段的commit阶段之前就会被持久化到磁盘,而这个持久化就是 prepare阶段做的事情,并且在commit阶段写入redo log时并不会直接写入磁盘,而是以异步的方式写入磁盘。为什么呢?(注意我们这里说的commit阶段说的是二阶段提交中的commit阶段,要和事务的commit提交区分开来,事务的提交就是用的二阶段提交)
在二阶段提交和mysql数据恢复里面说过,也用反证法说明了为什么需要二阶段提交,因为不采用二阶段提交不能保证数据的安全性,采用二阶段提交,即使是prepare完成后,无论 binlog有没有持久化就宕机了,依然可以正确提交或者回滚数据,但是如果prepare阶段并没有持久化redo log,而binlog却被持久化了,只有binlog没有redo log,那么二阶段提交就没有意义了。有binlog存在磁盘中,磁盘中就一定有对应的redo log来恢复数据,继续提交,否则就会导致binlog和实际数据的不一致。
mysql一共有四种隔离级别和三种并发读的异常,下面介绍一下:
脏读 | 不可重复读 | 幻读 | |
---|---|---|---|
未提交读 | √ | √ | √ |
已提交读 | × | √ | √ |
可重复读 | × | × | √ |
串行化 | × | × | × |
1.脏读:指的是一个事务读到了另外一个事务未提交的数据,而那个事务可能会将该数据回滚,就会导致该数据不存在,这就是脏读。
2.不可重复读:指的是一个事务的前后几次读同一个数据得到了不同的结果。原因是在两次读中间有其他的事务提交了对该数据的更新或者删除操作。
3.幻读:当一个事务在读取一个范围的记录的时候,另外一个事务又在这个范围插入了新的记录,导致这个事务再次读取这个范围的记录的时候,就会产生幻行。
然后介绍一下四种隔离级别:
1.未提交读:这其实就是毫无隔离可言,即使是一个事务中没有提交的数据,一样能在别的事务中可见,所以上面那三种并发 问题都会发生。
2.已提交读:这个隔离级别可以保证的是一个事务已经提交的数据,才能被其他的事务可见。
3.可重复读:能够保证在一个事务内重复对某一数据的查询一致是同样的记录,即使这个数据行被其他的事务修改了。但是对于其他事务新增数据行,这边有可能会产生幻读。
4.串行化:对同一行记录,写操作会加写锁,读操作会加读锁,这样的安全系数是最高的,但是这也导致Mysql的并发处理能力太低,性能太差。
上面的已提交读和可重复读就是使用MVCC来实现的。
那么我们就来介绍一下MVCC吧
MVCC的全称是Multi Version Concurrency Control ,即多版本并发控制,在Mysql Innodb中,就是使用这个来实现已提交读和可重复读的隔离级别的。因为使用悲观锁来避免并发问题的话,会对性能产生一定的影响,导致其可支撑的并发量不高,那么为了提高性能,就需要一种不加锁的方式来实现并发控制,而MVCC就是这样的一种机制。
MVCC的原理
在MySQL中,MVCC实现已提交读和可重复读是通过undo log日志版本链和快照(read view)来实现的。read view中有以下内容:
而在MySQL数据行的记录中,还隐藏着两列数据:
当我们开启一个事务去查询某些数据的时候,就会创建一个read view快照,然后查到符合where条件的记录的时候,就会通过roll_pointer访问日志版本链,下面是它对可见性判断的流程:
需要注意的是,对于可重复读级别来说,readView快照是在第一次进行select操作时生成的,而如果后面发生了增删改等操作,又会基于当前读来生成一份新的快照,依据这个新的快照来进行版本可见性的管理。
而对于已提交读来说,它的每次读取操作都会生成一个新的快照来进行版本可见性管理,因此一旦有其他事务提交了对该事物的一些更改,根据最新的快照就能够顺利的读取到这些变更。
Mysql幻读的理解?mvcc到底能不能解决幻读?
首先是对幻读的理解:当一个事务在读取一个范围的记录的时候,另外一个事务又在这个范围插入了新的记录,导致这个事务再次读取这个范围的记录的时候,就会产生幻行。
mvcc可以解决部分幻读问题,在当前读的情况下还是可能会出现幻读问题。
通过上面的描述,加之对mvcc的理解,一般情况下,在只使用快照读的时候,mvcc利用可重复读的快照机制还是可以保证不会出现幻读的。
在使用update/delete/insert的时候,mvcc会使用当前读的机制,意思就是这三条命令一定是基于数据库中的最新的数据进行操作的,比如事务A查询某个范围的记录,事务B插入一条记录并提交,事务A也在这里插入一条同样id的记录,那么由于事务A在插入前会执行当前读,插入数据重复报错,这里不能插入重复数据是正常的,我觉得不能算是幻读的范畴。所以我并不认为这三个命令基于当前最新的数据进行修改就是幻读(其实我觉得还是理解原理就行了,幻读只是一个人为定义的名词,看你自己对幻读的定义,只要知道目前操作会产生的后果,是不是符合幻读的定义也就没那么重要了)。
我举个我认为是幻读的例子:
事务A | 事务B | |
---|---|---|
时间1 | select * from table where id between 1 and 10; | |
时间2 | insert into table values(1,15); | |
时间3 | update table set age=10 where id between 1 and 10; | |
时间4 | select * from table where id between 1 and 10; |
在上面的例子中:
那么在Mysql中如何去解决当前读的时候出现幻读的情况呢?
我们可以利用MVCC+锁来解决当前读下的幻读问题。
先看看这两种锁的类型:
再看看innoDB支持三种行锁定方式:
而在MySQL的默认隔离级别可重复读级别下,
那么我们是怎么利用行锁来解决幻读的呢?
在上面的例子中我们,在事务A刚开始的时候就使用select * from table where id between 1 and 10 for update;来给这一段范围的数据行加上Next-Key Lock,那么事务B想要去修改它的时候,就会被阻塞,直到事务A被提交释放该锁为止。这样就能够保证在事务的处理期间,其他的事务不会对这个事务锁住的数据行修改,从而导致幻读了。(这里要注意的是,在Innodb中,修改数据行的操作时会默认加上写锁的,所以事务B的写操作才会被阻塞)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。