数据库常见面试题——事务

数据库事务的四大特性(ACID)

  • 原子性(Atomic):事务包含的所有操作要么全部成功,要么全部失败回滚;成功必须要完全应用到数据库,失败则不能对数据库产生影响;
  • 一致性(Consistency):事务执行前和执行后必须处于一致性状态,
  • 隔离性(Isolation):当多个用户并发访问数据库时,数据库为每一个用户开启的事务,不被其他事务的操作所干扰,多个并发事务之间要相互隔离;
  • 持久性(Durability):一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便在数据库系统遇到故障的情况下也不会丢失事物的操作。

事务隔离级别以及各级别下的并发访问问题

事务并发访问引起的问题以及如何避免



更新丢失——mysq所有事务隔离级别在数据库层面上均可避免

更新丢失:

更新丢失
取款事务存款事物
查询转账余额为100元 
 查询转账余额为100元
 存入20,余额变为120元
 提交事物
  
取出10元,余额改为90元 
回滚事物,余额恢复为100元更新丢失

 

解决方案:所有事物隔离级别,即READ-UNCOMMITTED以上

READ-UNCOMMITTED(可读取未提交):写事务阻止其他写事务,避免了更新遗失。但是没有阻止其他读事务。

问题:脏读


脏读——READ-COMMITTED事务隔离级别以上可避免

脏读:也就是当数据库的一个事务A正在使用一个数据但还没有提交,另外一个事务B也访问到了这个数据,还使用了这个数据,这就会导致事务B使用了事务A没有提交之前的数据。

解决方案:事物隔离级别READ-COMMITTED以上

READ-COMMITTED(可读已提交):写事务会阻止其他读写事务。读事务不会阻止其他任何事务。

问题:不可重复读


不可重复读——REPEATABLE-READ事务隔离级别以上可避免

不可重复读:在一个事务A中多次操作一个数据,在这两次或多次访问这个数据的中间,事务B也操作此数据,并使其值发生了改变,这就导致同一个事务A在两次操作这个数据的时候值不一样,这就是不可重复读。

解决方案:事物隔离级别REPEATABLE-READ以上

REPEATABLE-READ(可重复读):读事务会阻止其他写事务,但是不会阻止其他读事务。

问题:幻读


幻读——SERIALIZABLE事物隔离级别可以避免

幻读:是指事务不独立执行产生的一种现象。事务A读取与搜索条件相匹配的若干行。事务B以插入或删除行等方式来修改事务A的结果集,然后再提交。这样就会导致当A本来执行的结果包含B执行的结果,这两个本来是不相关的,对于A来说就相当于产生了“幻觉”。

解决方案:事物隔离级别SERIALIZABLE以上

SERIALIZABLE(序列化):可避免幻读。读加共享锁,写加排他锁。这样读取事务可以并发,但是读写,写写事务之间都是互斥的,基本上就是一个个执行事务,所以叫序列化。


总结:(SQL Server、oracle默认为READ-COMMITTED,MySQL默认为REPEATABLE-READ)

事务隔离级别
事务隔离级别更新丢失脏读不可重复读幻读
READ-UNCOMMITTED避免发生发生发生
READ-COMMITTED避免避免发生发生
REPEATABLE-READ避免避免避免发生
SERIALIZABLE避免避免避免避免

InnoDB可重复读隔离级别下如何避免幻读

  • 表象:快照读(非阻塞读)——伪MVCC(MVCC:多版本控制)
  • 内在: next-key锁(行锁+gap锁)

快照读和当前读

快照读:简单的select操作,属于快照读,不加锁。(当然,也有例外)

select * from table where ?;

当前读:特殊的读操作,插入/更新/删除操作,属于当前读,需要加锁。

select * from table where ? lock in share mode;--共享锁,其他全为排它锁

select * from table where ? for update;

insert into table values (…);

update table set ? where ?;

delete from table where ?;

从图中,可以看到,一个Update操作的具体流程。当Update SQL被发给MySQL后,MySQL Server会根据where条件,读取第一条满足条件的记录,然后InnoDB引擎会将第一条记录返回,并加锁 (current read)。待MySQL Server收到这条加锁的记录之后,会再发起一个Update请求,更新这条记录。一条记录操作完成,再读取下一条记录,直至没有满足条件的记录为止。因此,Update操作内部,就包含了一个当前读。同理,Delete操作也一样。Insert操作会稍微有些不同,简单来说,就是Insert操作可能会触发Unique Key的冲突检查,也会进行一个当前读。

参考:这是一个厉害的博客


RC、RR级别下的 InnoDB的非阻塞读如何实现

 

  • 数据行里的DB_TRX_ID、 DB_ROLL_PTR、DB_ROW_ID字段
  1. DB_TRX_ID:该字段用来记录最近一次对该条记录进行修改的事务的ID
  2. DB_ROLL_PTR:回滚指针(重建)
  3. DB_ROW_ID:( InnoDB创建的隐主键字段)
  • undo日志

  • read view

当我们执行快照读的时候,会创建一个readview,指的是当前我们可以看到的是哪一个版本的值。

readview可见性算法:将要修改的数据的DB_TRX_ID与其他系统活跃的事务id相比,如果DB_TRX_ID比这些活跃事务ID大,就通过DB_ROLL_PTR取出undo日志上一层的DB_TRX_ID,直到DB_TRX_ID小于这些活跃事务ID位置,这样就保证了我们获取到的数据版本是最稳定的版本。

正是因为创建read view(快照的时机不同),才形成了RC、RR级别两种隔离级别的不同可见性:在REPEATABLE-READ级别下,一个事务下,RR 第一条 dml 语句(增删改查)读数据会对已存在的 undo log 按read view的规则创建一个快照。同一个事务下,多次执行dml 语句,也是只能读到同一个快照,即第一个快照的内容;在READ-COMMITTED中,一个事务下,RC 每条 dml 语句(增删改查)读数据都会创建一个新的 read view (快照),其DB_TRX_ID 每次读都 +1。

这就是为什么在RC下可以看到别的快照读已提交的对表记录的增删改;而在RR下,主要看read view的首次使用时间:如何是在别的事务做出增删改之前的使用的,此后,其他事务做出增删改,就不知道事务做出改变的原因了。

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值