浅谈ACID 事务分析

概念

事务就是由一系列对系统中对数据进行操作的组成的一个程序可执行的单元,狭义上的事务特指数据库的事务

  • 事务是一系列的动作,他们综合在一起 才是一个完整的工作单元,这些动作必须全部 完成,如果一个失败的话,那么事务就会会滚到原始状态

  • 事务的特性(ACID):

    1.原子性(Atomicity):事务必须是一个原子的操作序列单元,一次事务只允许存在两种状态,全部成功或全部失败,任何一个操作失败都将导致整个事务失败
    2.一致性(Consistency):事务的执行不能破坏系统数据的完整性和一致性,如果未完成的事务对系统数据的修改有一部分已经写入物理数据库,这时系统数据就处于不一致状态
    3.隔离性(Isolation):在并发的环境中,不同的事务操作相同的数据时,需相互隔离不能互相干扰
    4.持久性(Durability):事务一旦提交,对系统数据的变更就是永久性的,必须被永久保存下来,即使服务器宕机 了,只要数据库能够重启,就一定能够恢复到事务成功结束时的状态

事务并发处理问题

如果没有锁定且多个用户同时访问一个数据库,则当他们的事务同事使用相同的数据时可能会发生问题(丢失事务修改,读脏数据,不可重复度,产生幽灵数据)

现在假设数据库有一张表
浅谈ACID 事务分析

第一类数据丢失更新(lost update)
  • 回滚丢失:在完全未隔离事务的情况下,两个事物更新同一条数据资源,某一事物异常终止,回滚造成第一个完成的更新也同时丢失。
    浅谈ACID 事务分析

在T1时刻开启了事务1,T2时刻开启了事务2,
在T3时刻事务1从数据库中取出了id=“402881e535194b8f0135194b91310001"的数据,
T4时刻事务2取出了同一条数据,
T5时刻事务1将age字段值更新为30,
T6时刻事务2更新age为35并提交了数据
但是T7事务1回滚了事务age最后的值依然为20,事务2的更新丢失了,
这种情况就叫做"第一类丢失更新(lost update)”。

脏读(dirty read)

事务没提交,提前读取
如果第二个事务查询到第一个事务还未提交的更新数据,形成脏读
浅谈ACID 事务分析

在T1时刻开启了事务1,T2时刻开启了事务2,
在T3时刻事务1从数据库中取出了id=“402881e535194b8f0135194b91310001"的数据,
在T5时刻事务1将age的值更新为30,但是事务还未提
T6时刻事务2读取同一条记录,获得age的值为30,但是事务1还未提交
若在T7时刻事务1回滚了事务2的数据就是错误的数据(脏数据),
这种情况叫做” 脏读(dirty read)"。

虚读(phantom read)
  • 一个事务执行两次查询,第二次结果集包含第一次中没有或者某些行已被删除,造成两次结果不一致,只是另一个事务在这两次查询中间插入了或者删除了数据造成的.
    浅谈ACID 事务分析

在T1时刻开启了事务1,T2时刻开启了事务2,
T3时刻事务1从数据库中查询所有记录,记录总共有一条,
T4时刻事务2向数据库中插入一条记录,T6时刻事务2提交事务。
T7事务1再次查询数据数据时,记录变成两条了。
这种情况是"虚读(phantom read)"。

不可重复读(unrepeated read)
  • 一个事务两次读取同一行数据,结果得到不同状态的结果,如中间正好另一个事务更新了该数据,结果两次结果相异,结果不可信任

浅谈ACID 事务分析

在T1时刻开启了事务1,T2时刻开启了事务2,
在T3时刻事务1从数据库中取出了id=“402881e535194b8f0135194b91310001"的数据,此时age=20,
T4时刻事务2查询同一条数据
T5事务2更新数据age=30,T6时刻事务2提交事务,
T7事务1查询同一条数据,发现数据与第一次不一致。
这种情况就是"不可重复读(unrepeated read)”

第二类丢失更新(second lost updates)

- 覆盖丢失

  • 不可重复读的特殊情况,如果两个事务都读取同一行,然后两个都进行写操作,并提交,第一个事务所做的改变就会丢失。
    浅谈ACID 事务分析

在T1时刻开启了事务1,T2时刻开启了事务2,T3时刻事务1更新数据age=25,T5时刻事务2更新数据age=30
T6时刻提交事务,
T7时刻事务2提交事务,把事务1的更新覆盖了。
这种情况就是"第二类丢失更新(second lost updates)"。

并发问题总结

不可重复读的重点是修改,同样的条件,你读取过的数据,再次读取出来发现不一样了,
幻读的重点在于新增或者删除,同样的条件,第一次和第二次读取出来的记录不一样了
第一次更新丢失(回滚丢失)
第二次更新丢失(覆盖丢失)

隔离级别

  • 解决并发的问题是什么?
    采用有效的隔离机制

  • 怎样实现事物的隔离?
    隔离级别的实现必须加锁

在开发的时候一般需要设置隔离等级
数据库采用四种隔离等级

1.未提交读(read uncommitted)
为最低事务隔离级别,一个事务能读取到别人未提交事务的未提交的更新事务,这种不安全,可能出现丢失更新,脏读,不可重复读,和幻读.

2.读已提交(read committed)
一个事务能读取到别的事务提交的更新数据,不能看到未提交的更新数据,不会出现丢失更新,脏读,但可能出现不可重复读,和幻读

3读已提交(repeatable read)
保证同一事务中先后执行的多次查询将返回同一结果,不受其他事务的影响,不会出现丢失更新,脏读,不可重复读,但可能出现幻读

4 序列化(SERIALIZABLE)
最高隔离级别,不允许事务并发执行,而必须串行化执行,最安全,不可能出现更新,脏读,不可重复读,幻读,但是效率最低

浅谈ACID 事务分析

  • 隔离级别越高,数据库的事务执行并发能力越差,能处理的操作越少,所以一般的,推荐 可重复读(REPEATABLE READ)级别来保证数据的一致性,而对于幻读的问题,可以通过加锁来解决
    浅谈ACID 事务分析
    - 最后提一下不可重复读和幻读的区别?

不可重复读对应的表的操作是更改(UPDATE),而幻读对应的表的操作是插入(INSERT),两种的应对策略不一样,对于不可重复读,只需要采用行级锁防止该记录被更新即可,而对于幻读必须加个表级锁,防止在表中插入数据

欢迎一起交流一起学习
浅谈ACID 事务分析