面试_mysql_事务及其一致性问题和解决方法

70 阅读4分钟

一、事务

事务的定义:

事务指的是满足 ACID 特性的一组操作,可以通过 Commit 提交,也可以使用 Rollback 回滚。它的任务是在并发场景下为线程提供隔离,保持数据库一致性状态



事物的特性:

原子性一致性隔离性持久性,这四个属性通常称为ACID特性。

1、原子性(Atomicity)

事务作为一个整体被执行,对数据库的操作要么全部执行,要么都不执行,已执行的部分回滚。

用回滚保证原子性:回滚日志(Undo Log)记录着事务所执行的修改操作,回滚时反向执行这些修改即可。


2、一致性(Consistency)

数据库从一个【正确有效】的状态转移到另一个【正确有效】的状态。

【正确有效】:满足数据库的预定约束。例如,A账户有90元,现在让他给B转100元,而银行的约束是账户余额不得小于0,那么这笔转账如果成功,就相当于有从正确状态转移到了错误状态,因为打破了约束。

通过原子性,持久性,隔离性最终实现数据的一致性。


3、隔离性(Isolation)

并发事务之间互不影响。 相互独立。

用【读写锁+MVCC】来保证隔离性


4、持久性(Durability)

一个事务一旦提交,他对数据库的修改应该永久保存在数据库中。 即使系统崩溃,事务执行的结果也不能丢失。

用重做保证持久性:重做日志(Redo Log)记录的是数据页的物理修改。



Java是怎样实现事务的?

在方法上使用 @Transactional 注解。检测到该注解时,Spring Framework 默认使用 AOP 代理,在代码运行时生成一个代理对象 。





二、并发一致性问题

在并发环境下,事务的隔离性很难保证,因此会出现很多并发一致性问题。

丢失修改

A和B两事务先后修改一个数据,B的修改覆盖了A的修改


读脏数据

A修改一个数据还未提交,B 此时读取这个数据。随后 A 提交了修改,那么 B 读到的数据就是脏数据。


不可重复读

A 读取一个数据,然后B 对该数据做了修改。如果 A 再次读取这个数据,会发现读出的值发生了变化。


幻影读

A 读取某个范围的数据,然后B 在这个范围内插入数据,如果 A 再次读取这个范围的数据,会发现读出的数据量发生变化,出现了“虚幻”的新纪录。


产生并发一致性问题的主要原因是破坏了事务的隔离性,数据库自身提供了事务的隔离级别,更高级的,可以用锁来做并发控制。





三、四种隔离级别(从高到低)

  1. 串行化:事务间完全隔离,不能并发,只能串行执行。可避免脏读、不可重复读、幻读的发生。


  2. 可重复读:一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。可避免脏读、不可重复读,但幻读仍有可能发生。(mysql默认)


  3. 读提交:事务只能看见已经提交的数据,也就是说,一个事务提交之后,它所做的修改才会被其他事务看到。可避免脏读的发生。(oracal默认)


  4. 读未提交:一个事务可以看到其他事务未提交的变更。最低级别,任何情况都无法保证。


隔离级别越高,并行性越低,数据库性能越低,当前事务处理的中间结果对其它事务不可见程度越高。


MySQL在可重复读隔离级别下不会出现幻读的现象,这是因为Innodb提供了 MVCC间隙锁 来解决。

对于广义的数据库,可重复读隔离级别下是会出现幻读的。





四、谈谈你对MVCC 的了解?

MVCC叫做多版本并发控制。它为事务分配单向增长的时间戳,每次修改保存一个版本。读操作只读该事务开始前的数据库快照,并且可以读到多个版本的数据,从而解决了脏读、不可重复读和幻读问题。

注意:MVCC只能解决快照读下的幻读问题,而当前读下的幻读问题需要用间隙锁来解决。

快照读与当前读的区别:快照读,读的是历史版本,不用加锁。当前读,读取的是最新版本,需要加锁。





blog.cnkj.site/Interview/I…