照例,咱们先来一个场景~mysql
面试官:"知道事务的四大特性么?"
你:"懂,ACID嘛,原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)!"
面试官:"大家是用mysql数据库吧,能简单说说innodb中怎么实现这四大特性的么?“
你:"我只知道隔离性是怎么作的balabala~~"
面试官:"仍是回去等通知吧~"面试
OK,回到正题。说到事务的四大特性原子性(Atomicity
)、一致性(Consistency
)、隔离性(Isolation
)、持久性(Durability
),懂的人不少。可是稍微涉及细节一点,这四大特性在数据库中的实现原理是怎么样的?那就没有几我的可以答得上来了。所以,咱们这篇文章着重讨论一下四大特性在Mysql中的实现原理。sql
咱们以从A帐户转帐50元到B帐户为例进行说明一下ACID,四大特性。数据库
根据定义,原子性是指一个事务是一个不可分割的工做单位,其中的操做要么都作,要么都不作。即要么转帐成功,要么转帐失败,是不存在中间的状态!
若是没法保证原子性会怎么样?
OK,就会出现数据不一致的情形,A帐户减去50元,而B帐户增长50元操做失败。系统将无端丢失50元~并发
根据定义,隔离性是指多个事务并发执行的时候,事务内部的操做与其余事务是隔离的,并发执行的各个事务之间不能互相干扰。
若是没法保证隔离性会怎么样?
OK,假设A帐户有200元,B帐户0元。A帐户往B帐户转帐两次,金额为50元,分别在两个事务中执行。若是没法保证隔离性,会出现下面的情形
日志
如图所示,若是不保证隔离性,A扣款两次,而B只加款一次,凭空消失了50元,依然出现了数据不一致的情形!code
ps
:可能有细心的读者已经发现了,mysql中是依靠锁来解决隔离性问题。嗯,咱们后面来讲明。blog
根据定义,持久性是指事务一旦提交,它对数据库的改变就应该是永久性的。接下来的其余操做或故障不该该对其有任何影响。事务
若是没法保证持久性会怎么样?
在Mysql中,为了解决CPU和磁盘速度不一致问题,Mysql是将磁盘上的数据加载到内存,对内存进行操做,而后再回写磁盘。好,假设此时宕机了,在内存中修改的数据所有丢失了,持久性就没法保证。内存
设想一下,系统提示你转帐成功。可是你发现金额没有发生任何改变,此时数据出现了不合法的数据状态,咱们将这种状态认为是数据不一致的情形。
根据定义,一致性是指事务执行先后,数据处于一种合法的状态,这种状态是语义上的而不是语法上的。
那什么是合法的数据状态呢?
oK,这个状态是知足预约的约束就叫作合法的状态,再通俗一点,这状态是由你本身来定义的。知足这个状态,数据就是一致的,不知足这个状态,数据就是不一致的!
若是没法保证一致性会怎么样?
例一:A帐户有200元,转帐300元出去,此时A帐户余额为-100元。你天然就发现了此时数据是不一致的,为何呢?由于你定义了一个状态,余额这列必须大于0。
例二:A帐户200元,转帐50元给B帐户,A帐户的钱扣了,可是B帐户由于各类意外,余额并无增长。你也知道此时数据是不一致的,为何呢?由于你定义了一个状态,要求A+B的余额必须不变。
问题一:Mysql怎么保证一致性的?
OK,这个问题分为两个层面来讲。
从数据库层面,数据库经过原子性、隔离性、持久性来保证一致性。也就是说ACID四大特性之中,C(一致性)是目的,A(原子性)、I(隔离性)、D(持久性)是手段,是为了保证一致性,数据库提供的手段。数据库必需要实现AID三大特性,才有可能实现一致性。例如,原子性没法保证,显然一致性也没法保证。
可是,若是你在事务里故意写出违反约束的代码,一致性仍是没法保证的。例如,你在转帐的例子中,你的代码里故意不给B帐户加钱,那一致性仍是没法保证。所以,还必须从应用层角度考虑。
从应用层面,经过代码判断数据库数据是否有效,而后决定回滚仍是提交数据!
问题二: Mysql怎么保证原子性的?
OK,是利用Innodb的undo log
。
undo log
名为回滚日志,是实现原子性的关键,当事务回滚时可以撤销全部已经成功执行的sql语句,他须要记录你要回滚的相应日志信息。
例如
undo log
记录了这些回滚须要的信息,当事务执行失败或调用了rollback,致使事务须要回滚,即可以利用undo log中的信息将数据回滚到修改以前的样子。
ps
:具体的undo log日志长啥样,这个能够写一篇文章了。并且写出来,看的人也很少,姑且先这么简单的理解吧。
问题三: Mysql怎么保证持久性的?
OK,是利用Innodb的redo log
。
正如以前说的,Mysql是先把磁盘上的数据加载到内存中,在内存中对数据进行修改,再刷回磁盘上。若是此时忽然宕机,内存中的数据就会丢失。
怎么解决这个问题?
简单啊,事务提交前直接把数据写入磁盘就行啊。
这么作有什么问题?
因而,决定采用redo log
解决上面的问题。当作数据修改的时候,不只在内存中操做,还会在redo log
中记录此次操做。当事务提交的时候,会将redo log
日志进行刷盘(redo log
一部分在内存中,一部分在磁盘上)。当数据库宕机重启的时候,会将redo log
中的内容恢复到数据库中,再根据undo log
和binlog
内容决定回滚数据仍是提交数据。
采用redo log的好处?
其实好处就是将redo log
进行刷盘比对数据页刷盘效率高,具体表现以下
redo log
体积小,毕竟只记录了哪一页修改了啥,所以体积小,刷盘快。redo log
是一直往末尾进行追加,属于顺序IO。效率显然比随机IO来的快。ps
:不想具体去谈redo log
具体长什么样,由于内容太多了。
问题四: Mysql怎么保证隔离性的?
OK,利用的是锁和MVCC机制。仍是拿转帐例子来讲明,有一个帐户表以下
表名t_balance
id | user_id | balance |
---|---|---|
1 | A | 200 |
2 | B | 0 |
其中id是主键,user_id为帐户名,balance为余额。仍是以转帐两次为例,以下图所示
至于MVCC,即多版本并发控制(Multi Version Concurrency Control),一个行记录数据有多个版本对快照数据,这些快照数据在undo log
中。
若是一个事务读取的行正在作DELELE或者UPDATE操做,读取操做不会等行上的锁释放,而是读取该行的快照版本。
因为MVCC机制在可重复读(Repeateable Read)和读已提交(Read Commited)的MVCC表现形式不一样,就不赘述了。
可是有一点说明一下,在事务隔离级别为读已提交(Read Commited)时,一个事务可以读到另外一个事务已经提交的数据,是不知足隔离性的。可是当事务隔离级别为可重复读(Repeateable Read)中,是知足隔离性的。
本文讲了Mysql中事务ACID四大特性的实现原理,但愿你们有所收获。