首先谈一下mysql的4大特性,也是事务的前置特性。
原子性由undolog保证,隔离性是由锁和mvcc保证,持久性由redolog保证;一致性则是前面三个保证的。
这里要区别一下binlog,binlog是再sqlserver级别的,而undolog与redolog是再innoDB级别的。
为什么会有redolog这个东西呢?
我们知道,数据库在断电的时候,数据是易失的,也就是说,数据来不及从内存刷到磁盘上。为了解决这个问题,引入了redolog。在引入redolog之前,需要了解下顺序IO与随机IO。
随机IO:在读写过程中,写的磁盘往往会在不同的磁盘块,这会由额外的IO开销。
顺序IO:如果只是在文件后面以追加的形式,也就是说,磁盘块会放在一起,那么速度会变的很快。
在上图中,内存到磁盘的写是随机IO,很慢,但是内存到redolog中,以追加的方式写,速度很快。
但是注意,如果这两个地方都断电了,那么这数据真正的丢失了。
redolog:当发生数据修改的时候,innodb引擎会先将记录写到redo log中,并更新内存,此时更新就算完成了,同时innodb会在合适的时间将记录写到磁盘中。redolog是固定大小的,是循环写的过程。
有了redolog之后,inndb就可以保证及时数据库异常重启,之前的记录也不会丢失,较crash-safe。
undolog:undolog是为了实现事务的原子性,在mysql数据库innodb存储引擎中,还用undolog来实现多版本并发控制(MVCC);在操作任何数据之前,首先将数据备份到一个地方(undolog)。然后进行数据的修改,如果出现了错误i或者用户执行了rollback语句,那么可以利用undo log中的备份数据恢复到开始之前的状态。注意,undo log是逻辑日志,一般是deltele时,undo或记录insert。
binlog:binlog是server层的日志,主要做mysql功能层面的事情。
与redo日志区别:
说到redolog能保证数据的原子性,也就是能恢复数据,那么binlog也有这样的功能,这两个log需要保持数据的一致性吗?
先说这两个日志的区别。Redo log解决数据恢复的问题。而Bin log用到主从同步。
在主从同步的过程中,一定会用到binlog,保证主从数据的一致性。在经过IO Thread后,到了从机,会以relaylog形式存储起来。
为了保证redolog与binlog数据的一致性,这里有两阶段提交方式(2PC).
如果断电redolog和binlog时,redolog会丢弃数据,而断电发生在binglog与commit时,用redolog恢复。Binlog写完后redolog称为提交状态。
两阶段提交是指redolog有个repare过程与commit过程,中间包含了binlog写的过程。
数据恢复的过程涉及到一阶段提交与二阶段提交。有prepare状态与commit状态。数据和日志是分开的。是先生成redolog再生成binlog。保证数据的最终一致性。两阶段提交时保证数据的一致的。保证在commit后,binlog与redolog都有内容。系统崩溃的话,redolog与binlog都可以。
undolog包含了旧版本的信息。
具体结合在MVCC里。
有个问题,为什么redolog不能代替binlog做重做日志?这是因为redolog里面的内容并不全,binlog是保存的全量数据库信息,所以用binlog做。
redolog file其实是在磁盘中的,在buffer pool后面,有个log buffer与OS buffer。
主要控制innodb将log buffer中的数据写入日志文件并flush磁盘的时间点,值分别为0,1,2。指的是undolog或者redolog写日志,先到日志缓存区,再到os缓冲区最后到磁盘的undolog 文件中。
多版本并发控制
数据库的并发场景:读读,不会有问题;读写:有数据安全问题,可能会产生脏读、幻读、不可重复读的问题。写写:数据安全问题,可能存在更新丢失问题。
MVCC用于解决并发读写的问题。
当前读:读取的都是最新的数据,不会读取历史的数据。Update、delete、insert、select lock in share mode,select for unpdate;
快照读:读取的都是历史版本的数据。Select
MVCC维持了数据的多个版本,使得读写之间没有冲突。
MVCC实现机制由三部分组成,分别为隐藏字段、undolog、readview
1.隐藏字段
不同事务或者相同事务对于同一条数据的修改会形成一个版本的链表,链首最新的旧记录。
后台有个线程purge来定时的清除无用的undolog数据。
3.Readview
这里的快照读,也就是在默认的RR级别下,一般的操作为select时,触发的快照读。
readview有三个值:
分别为trx_list、up_limit_id、low_limit_id。
再根据可见性算法判断能否读取当前数据。
比如:
事务2被commit,没有事务2了,最小的是事务1;尚未分配的是事务4;.DB_TRX_Id=2
根据可见性算法,为什么最终的结果缺不一样?
大胆做出一个假设,绿色用了橙色的readview。
结论:
事务开启之后,第一次进行快照读的时候会生成readview,之后的快照读会沿用第一次生成的readview。
v
MVCC就是解决了不可重复读的问题。
事务1,select后,事务2插入一条数据,事务1再次查询,任然和上一次查询一致。但是一旦用了更新,那么会显示3条已经更新,产生了幻读的现象。实际上是把快照读与当前读一起使用产生了这样的问题。
幻读的问题:读到2条数据,但是更新却是3条数据,所以幻读产生的原因是:如果事务中,都用快照读,没有幻读的问题,但是快照读与当前读一起使用才会产生幻读的问题。