1、如何保证【原子性】?
A-原子性(Atomicity):事务中的操作要么都不做,要么就全做。
是利用Innodb的 :undo log
undo log
名为回滚日志,是实现原子性的关键,当事务回滚时能够撤销所有已经成功执行的sql语句,他需要记录你要回滚的相应日志信息。
- (1)当你delete一条数据的时候,就需要记录这条数据的信息,回滚的时候,insert这条旧数据
- (2)当你update一条数据的时候,就需要记录之前的旧值,回滚的时候,根据旧值执行update操作
- (3)当年insert一条数据的时候,就需要这条记录的主键,回滚的时候,根据主键执行delete操作
undo log
记录了这些回滚需要的信息,当事务执行失败或调用了rollback,导致事务需要回滚,便可以利用undo log中的信息将数据回滚到修改之前的样子。
2、如何保证【一致性】?
C-一致性(Consistency):事务执行的结果必须是从数据库从一个一致性状态转换到另一个一致性状态。
- 从数据库层面,数据库通过原子性、隔离性、持久性来保证一致性。也就是说ACID四大特性之中,C(一致性)是目的,A(原子性)、I(隔离性)、D(持久性)是手段,是为了保证一致性,数据库提供的手段。数据库必须要实现AID三大特性,才有可能实现一致性。例如,原子性无法保证,显然一致性也无法保证。
- 从应用层面,通过代码判断数据库数据是否有效,然后决定回滚还是提交数据!如果你在事务里故意写出违反约束的代码,一致性还是无法保证的。例如,你在转账的例子中,你的代码里故意不给B账户加钱,那一致性还是无法保证。因此,还必须从应用层角度考虑。
3、如何保证【隔离性】?
I-隔离性(Isolation):一个事务的执行不能被其他事务干扰;
OK,利用的是【锁】和MVCC(Multi Version Concurrency Control)多版本并发控制机制。还是拿转账例子来说明,有一个账户表如下
表名t_balance
id | user_id | balance |
---|---|---|
1 | A | 200 |
2 | B | 0 |
其中id是主键,user_id为账户名,balance为余额。还是以转账两次为例,如下图所示
4、如何保证【持久性】?
D-持久性(Durability):一个事务一旦提交,它对数据库中数据的改变就应该是永久性的;
OK,是利用Innodb的 redo log
正如之前说的,Mysql是先把磁盘上的数据加载到内存中,在内存中对数据进行修改,再刷回磁盘上。如果此时突然宕机,内存中的数据就会丢失。
怎么解决这个问题?
简单啊,事务提交前直接把数据写入磁盘就行啊。
这么做有什么问题?
- 只修改一个页面里的一个字节,就要将整个页面刷入磁盘,太浪费资源了。毕竟一个页面16kb大小,你只改其中一点点东西,就要将16kb的内容刷入磁盘,听着也不合理。
- 毕竟一个事务里的SQL可能牵涉到多个数据页的修改,而这些数据页可能不是相邻的,也就是属于随机IO。显然操作随机IO,速度会比较慢。
于是,决定采用redo log
解决上面的问题。当做数据修改的时候,不仅在内存中操作,还会在redo log
中记录这次操作。当事务提交的时候,会将redo log
日志进行刷盘(redo log
一部分在内存中,一部分在磁盘上)。当数据库宕机重启的时候,会将redo log
中的内容恢复到数据库中,再根据undo log
和binlog
内容决定回滚数据还是提交数据。
采用redo log的好处?
其实好处就是将redo log
进行刷盘比对数据页刷盘效率高,具体表现如下
redo log
体积小,毕竟只记录了哪一页修改了啥,因此体积小,刷盘快。redo log
是一直往末尾进行追加,属于顺序IO。效率显然比随机IO来的快。