1、ACID
事务是数据库区别于文件系统的重要特性之一。
InnoDB的事务完全符合ACID特性。
原子性(Atomicity):操作过程不可分割,要么全部成功,要么全部失败
一致性(Consistency):完整性约束不被破坏
隔离性(Isolution):事务提交前对其他事务不可见
持久性(Durability):事务一旦提交,其结果就是永久性的
In弄DB中的ACID特性依靠如下机制实现:
隔离性由锁来实现;
原子性和持久性由redolog来实现;
一致性由undolog来实现。
2、隔离级别
事务的隔离性是数据库处理数据的几大基础之一,而隔离级别其实就是提供给用户用于在性能和可靠性做出选择和权衡的配置项。
InnoDB遵循了SQL:标准中的四种隔离级别:
RAEDUNCOMMITED:使用查询语句不会加锁,可能会读到未提交的行(DirtyRead);
READCOMMITED:只对记录加记录锁,而不会在记录之间加间隙锁,所以允许新的记录插入到被锁定记录的附近,所以再多次使用查询语句时,可能得到不同的结果(Non-RepeatableRead);
REPEATABLEREAD:多次读取同一范围的数据会返回第一次查询的快照,不会返回不同的数据行,但是可能发生幻读(PhantomRead);
SERIALIZABLE:InnoDB隐式地将全部的查询语句加上共享锁,解决了幻读的问题;
MySQL中默认的事务隔离级别就是,但是它通过Next-Key锁也能够在某种程度上解决幻读的问题。
接下来,我们将数据库中创建如下的表并通过个例子来展示在不同的事务隔离级别之下,会发生什么样的问题:
CREATETABLEtest(idINTNOTNULL,UNIQUE(id));
2.1脏读
当事务的隔离级别为READUNCOMMITED时,我们在SESSION2中插入的未提交数据在SESSION1中是可以访问的。
2.2不可重复读
当事务的隔离级别为时,虽然解决了脏读的问题,但是如果在先查询了一个范围的数据,在这之后中插入一条数据并且提交了修改,在这时,如果中再次使用相同的查询语句,就会发现两次查询的结果不一样。
不可重复读的原因就是,在的隔离级别下,存储引擎不会在查询记录时添加间隙锁,锁定id5这个范围。
2.3幻读
重新开启了两个会话和,在中我们查询全表的信息,没有得到任何记录;在中向表中插入一条数据并提交;由于的原因,再次查询全表的数据时,我们获得到的仍然是空集,但是在向表中插入同样的数据却出现了错误。
这种现象在数据库中就被称作幻读,虽然我们使用查询语句得到了一个空的集合,但是插入数据时却得到了错误,好像之前的查询是幻觉一样。
在标准的事务隔离级别中,幻读是由更高的隔离级别解决的,但是它也可以通过MySQL提供的Next-Key锁解决:
REPERATABLEREAD和其实是矛盾的,如果保证了前者就看不到已经提交的事务,如果保证了后者,就会导致两次查询的结果不同,MySQL为我们提供了一种折中的方式,能够在模式下加锁访问已经提交的数据,其本身并不能解决幻读的问题,而是通过文章前面提到的Next-Key锁来解决。
3、重做日志(redolog)
3.1write-aheadlogging
预写式日志(write-aheadlogging,WAL),是一种实现事务日志的标准方法,其中心思想是对数据文件的修改必须发生在这些修改已经记录了日志之后。
如果遵循这个过程,那么就不需要在每次事务提交的时候都把数据页冲刷到磁盘,因为即便出现宕机的情况,我们也可以用日志来恢复数据库:任何尚未附加到数据页的记录都将先从日志记录中重做(这叫向前滚动恢复,也叫做redo)。
使用WAL的第一个主要的好处就是显著地减少了磁盘写的次数。因为在日志提交的时候只有日志文件需要冲刷到磁盘;而不是事务修改的所有数据文件。在多用户环境里,许多事务的提交可以用日志文件的一次fsync()来完成。而且,日志文件是顺序写的,因此同步日志的开销要远比同步数据页的开销要小。这一点对于许多小事务对磁盘的随机写入更是如此。
3.2基本概念
MySQL作为一个存储系统,为了保证数据的可靠性,最终得落盘;又为了数据写入的速度,需要引入基于内存的“缓冲池”。也就是说,数据是先缓存在缓冲池中,然后再以某种方式刷新到磁盘,在某个时间段之内,缓存中的数据与磁盘的数是不一致的,如果这个时候宕机会导致缓存数据的丢失。
引入redolog的目的就是为了防止以上问题的发生。
当向InnoDB写用户数据时,先写redolog,然后redolog根据一定的规则持久化到磁盘,变成redologfile,用户数据则在buffer中(比如数据页、索引页)。如果发生宕机,重启后则读取磁盘上的redologfile进行数据的恢复。
从这个角度来说,InnoDB事务的持久性是通过redolog来实现的。
下面以一个更新事务为例,宏观上把握redolog流转过程,如下图所示:
第一步:先将原始数据从磁盘中读入内存中来,修改数据的内存拷贝
第二步:生成一条重做日志并写入redologbuffer,记录的是数据被修改后的值
第三步:当事务