参考:https://zhuanlan.zhihu.com/p/117476959
目录
3、mysql如何解决幻读 + 总结几种隔离级别如何出现的逻辑
1、事务:数据库逻辑的基本单元,ACID;
原子性(Atomicity
)、一致性(Consistency
)、隔离性(Isolation
)、持久性(Durability
)
除了基础的定义,这四大特性在数据库中的实现原理是怎么样的?没有几个人能够答得上来。因此,我们这篇文章着重讨论一下四大特性在Mysql中的实现原理。
A - 原子性:根据定义,原子性是指一个事务是一个不可分割的工作单位,其中的操作要么都做,要么都不做,不存在中间的状态。
C - 一致性(四个中的最终目标,其他AID都是为了实现C):根据定义,一致性是指事务执行前后,数据处于一种合法的状态,这种状态是语义上的而不是语法上的。
那什么是合法的数据状态呢?这个状态是满足预定的约束就叫做合法的状态,再通俗一点,这状态是由你自己来定义的。满足这个状态,数据就是一致的,不满足这个状态,数据就是不一致的!I - 隔离性:根据定义,隔离性是指多个事务并发执行的时候,事务内部的操作与其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
D -持久性:根据定义,持久性是指事务一旦提交,它对数据库的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
问题一:Mysql怎么保证一致性?
这个问题分为两个层面来说。
- 从数据库层面,数据库通过原子性、隔离性、持久性来保证一致性。也就是说ACID四大特性之中,C(一致性)是目的,A(原子性)、I(隔离性)、D(持久性)是手段,是为了保证一致性,数据库提供的手段。数据库必须要实现AID三大特性,才有可能实现一致性。例如,原子性无法保证,显然一致性也无法保证。
但是,如果你在事务里故意写出违反约束的代码,一致性还是无法保证的。例如,你在转账的例子中,你的代码里故意不给B账户加钱,那一致性还是无法保证。因此,还必须从应用层角度考虑。
- 从应用层面,通过代码判断数据库数据是否有效,然后决定回滚还是提交数据!
问题二: Mysql怎么保证原子性?
利用Innodb的
undo log,
Undo log是用来记录数据更新前的值,保证数据更新失败能够回滚。undo log
名为回滚日志,记录了回滚所需要的相应日志信息。根据undo log,当事务回滚时能够撤销所有已经成功执行的sql语句。举个例子:
- (1)当你delete一条数据的时候,就需要记录这条数据的信息,回滚的时候,insert这条旧数据
- (2)当你update一条数据的时候,就需要记录之前的旧值,回滚的时候,根据旧值执行update操作
- (3)当年insert一条数据的时候,就需要这条记录的主键,回滚的时候,根据主键执行delete操作
undo log
记录了这些回滚需要的信息,当事务执行失败或调用了rollback,导致事务需要回滚,便可以利用undo log中的信息将数据回滚到修改之前的样子。
问题三: Mysql怎么保证持久性?
利用Innodb的redo log,
Redo log用来记录某数据块被修改后的值,可以用来恢复未写入 data file 的已成功事务更新的数据。
问题:正如之前说的,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来的快。
问题四: Mysql怎么保证隔离性?
利用的是锁和MVCC机制。
MVCC,即多版本并发控制(Multi Version Concurrency Control),一个行记录数据有多个版本对快照数据,这些快照数据在
undo log
中。 如果一个事务读取的行正在做DELELE或者UPDATE操作,读取操作不会等行上的锁释放,而是读取该行的快照版本。 由于MVCC机制在可重复读(Repeateable Read)和读已提交(Read Commited)的MVCC表现形式不同,就不赘述了。但是有一点说明一下,在事务隔离级别为读已提交(Read Commited)时,一个事务能够读到另一个事务已经提交的数据,是不满足隔离性的。但是当事务隔离级别为可重复读(Repeateable Read)中,是满足隔离性的。
2、mysql默认隔离级别--可重复读:
对于其他事务的修改,读不到;
对于其他事务的新增,能读到 -->幻读--->mysql解决幻读办法:行锁 + 间隙锁(有索引的、没索引的)
3、mysql如何解决幻读 + 总结几种隔离级别如何出现的逻辑
mysql在RR的隔离级别下,究竟是通过MVCC解决幻读的还是通过行锁的next key算法解决的? - yangsoon的回答 - 知乎
总结:
- MySQL 的 InnoDB 引擎才支持事务,其中可重复读是默认的隔离级别。
- 读未提交和串行化基本上是不需要考虑的隔离级别,前者不加锁限制,后者相当于单线程执行,效率太差。
- 读提交解决了脏读问题,行锁解决了并发更新的问题。并且 MySQL 在可重复读级别解决了幻读问题,是通过行锁和间隙锁的组合 Next-Key 锁实现的。