深入分析事务

一、概念

作为单个逻辑工作单元执行的一系列操作,要么都执行,要么都不执行。

通俗的理解,事务是一组原子操作单元,从数据库角度说,就是一组SQL指令,要么全部执行成功,若因为某个原因其中一条指令执行有错误,则撤销先前执行过的所有指令。更简单的说就是:要么整体成功,要么整体失败。

 一个数据库事务通常包含了一个序列的对数据库的读/写操作。它的存在包含有以下两个目的:

  1. 为数据库操作序列提供了一个从失败中恢复到正常状态的方法,同时提供了数据库即使在异常状态下仍能保持一致性的方法。
  2. 当多个应用程序在并发访问数据库时,可以在这些应用程序之间提供一个隔离方法,以防止彼此的操作互相干扰。

二、ACID 特性

 ACID,指数据库事务正确执行的四个基本要素的缩写。包含:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。一个支持事务(Transaction)的数据库,必须要具有这四种特性,否则在事务过程(Transaction processing)当中无法保证数据的正确性,交易过程极可能达不到交易方的要求。

2.1 原子性

事务作为一个执行整体,包含在其中的所有对数据库的操作要么整体成功,要么整体失败。
事务的原子性
 想要保证事务的原子性,就意味着需要在操作发生异常时,对该事务所有之前执行过的操作进行回滚
 在 MySQL 中,这个回滚是通过回滚日志(Undo Log)实现的。简单的说,回滚日志就是记录了你所有操作的逆操作,在需要回滚时,就把这个事务的回滚日志里的操作全部执行一次。

比如你的事务里每一个 create 其实都对应了一个效果跟其相反的 delete 语句,他们被记录在回滚日志里,当事务发生异常触发 rollback 时,就按照日志逻辑地将回滚日志里的操作全部执行,从而达到“撤销”操作的效果。

2.2 一致性

 事务的一致性定义基本可以理解为是事务对数据完整性约束的遵循。这些约束可能包括主键约束、外键约束或是一些用户自定义约束。事务执行的前后都是合法的数据状态,不会违背任何的数据完整性,这就是“一致”的意思。
事务的一致性
 当然这个含义中也隐含着对开发者的要求,就是不能写出错误的事务逻辑,比如银行的转账不能只加钱不减钱,这是应用层面的一致性要求。

2.3 隔离性

多个事务并发执行时,一个事务的执行不应影响其他事务的执行。

2.3.1 隔离原因

 如果没有事务隔离,会出现以下 3 种严重的问题:

  • 脏读
     一个事务读取到了另一个事务未提交(回滚)的数据。
    事务的脏读

    第一个动作:A 给 B 转账 100 元。
    第二个动作:B 提交了事务,余额增加了 100 元,但 A 还没提交事务。
    第三个动作:当 B 提交了事务,A 还尚未提交事务时,C 读取了 B 的余额,则 C 读取到的是已经增加 100 元的余额。
    第四个动作:如果出现异常(如 网络问题),A 回滚,就会导致 B 也跟着回滚。则 C 读取到的数据就是脏数据,简称脏读。

    如何解决脏读?

    1. 如果 AB 这两个动作是操作的同一个数据库,那么可以把 AB 的动作放在同一个事务里面,C 就不会出现脏读。
    2. 如果不是同一个数据库,而是 AB 不同数据库,或微服务操作,那就只能采用分布式事务来解决。
  • 不可重复读
     一个事务对同一行数据重复读取两次,但是却得到了不同的数据值。
    事务的不可重复读
    如何解决不可重复读?
      在数据读出来后给该数据加把锁,类似 select * from T where ID=2 for update,明确数据读取出来就是为了变更操作,所以加了一把锁,防止别人修改它。
    脏读和不可重复读有什么区别?
      脏读:   读到的数据是前一个事务提交的数据。
      不可重复读:读到的数据是前一个事务提交的数据。

  • 幻读
     当 A 事务在读取某个范围内的记录时,B 事务又在该范围内出入了新的纪录,当 A 事务再次读取该范围内的记录时,就产生了幻读。
    事务的幻读
    幻读和不可重复读有什么区别?
      相同点:幻读和不可重复读都是读到了另一个事务已提交的数据(脏读是未提交)
      不同点:
       不可重复读:读到的都是同一个数据值(同一条数据)。
       幻读:是针对一批整体的数据(数据总数或一张表)。
    ###2.3.2 隔离级别
     为了防止出现脏读、不可重复读、幻读等情况,我们就需要根据我们的实际需求来设置数据库的隔离级别。数据库都有哪些隔离级别呢?

  • 读未提交(Read Uncommitted)
    可以读到未提交的内容。因此,在这种隔离级别下,查询是不会加锁的,也由于查询的不加锁,所以这种隔离级别的一致性是最差的,可能会产生“脏读”、“不可重复读”、“幻读”。

    如无特殊情况,基本是不会使用这种隔离级别的。

  • 读提交(Read Committed)
    只能读到已经提交了的内容。这是各种系统中最常用的一种隔离级别,也是 SQL Server 和 Oracle 的默认隔离级别。这种隔离级别能够有效的避免脏读,但除非在查询中显示的加锁,如:

    select * from T where ID=2 lock in share mode;
    select * from T where ID=2 for update;
    

     不然,普通的查询是不会加锁的。那为什么“读提交”同“读未提交”一样,都没有查询加锁,但是却能够避免脏读呢?

    这就要说道另一个机制“快照(snapshot)”,而这种既能保证一致性又不加锁的读也被称为“快照读(Snapshot Read)”。假设没有“快照读”,那么当一个更新的事务没有提交时,另一个对更新数据进行查询的事务会因为无法查询而被阻塞,这种情况下,并发能力就相当的差。而“快照读”就可以完成高并发的查询,不过,“读提交”只能避免“脏读”,并不能避免“不可重复读”和“幻读”。

  • 可重复读(Repeated Read)
     专门针对“不可重复读”这种情况而制定的隔离级别,自然,它就可以有效的避免“不可重复读”。而它也是 MySql 的默认隔离级别。
     在这个级别下,普通的查询同样是使用的“快照读”,但是,和“读提交”不同的是,当事务启动时,就不允许进行“修改操作(Update)”了,而“不可重复读”恰恰是因为两次读取之间进行了数据的修改,因此,“可重复读”能够有效的避免“不可重复读”,但却避免不了“幻读”,因为幻读是由于“插入或者删除操作(Insert or Delete)”而产生的。

  • 串行化(Serializable)
     这是数据库最高的隔离级别,这种级别下,事务“串行化顺序执行”,也就是一个一个排队执行。这种级别下,“脏读”、“不可重复读”、“幻读”都可以被避免,但是执行效率奇差,性能开销也最大,所以基本没人会用。

 根据以上描述,可整理出如下在不同隔离级别下能出现的现象关系表格:

隔离级别脏读不可重复读幻读
读未提交(Read Uncommitted)
读已提交(Read Committed)
可重复读(Repeated Read)
可串行化(Serializable)

2.3.3 隔离实现

 隔离的实现说到底其实是并发控制,因此不同隔离级别的实现,其实就是采用了不同的并发控制机制。


  1.  这个自然是最简单的,也是相当常用的并发控制机制了。
     不过在一个事务中,自然是不可能把整个数据库都加锁的,而是只对要访问的数据加锁(具体的粒度有行、表等)。而这些资源锁也是理所当然地分为共享锁(读锁)和互斥锁(写锁)两种。

    读锁可以保证操作并发执行而不受影响,写锁则保证了更新数据库时不会受到其他事务的干扰。

  2. 时间戳
     用时间戳实现隔离性,需要为记录配置两个字段:

    • 读时间戳:用于保存所有访问该记录的事务中的最大时间戳(最后读取时间)
    • 写时间戳:用于保存将记录改到当前值的事务的时间戳(最后修改时间)

     这样的事务在并行执行时,用的是乐观锁,先任由事务对数据进行修改,在写回去的时候在判断记录的时间戳有没有修改,如果没有被修改,就写入,否则,就生成一个新的时间戳并再次尝试更新数据。

    PostgreSQL就使用了这种思想来控制事务。

  3. 多版本和快照隔离
     通过维护多个版本的数据,数据库便可以允许事务并发执行遇到互斥锁时,转而读取旧版本的数据快照。这样就能显著地提升读取的性能。我们简称这一手段为 MVCC。

2.4 持久性

已被提交的事务对数据库的修改应该永久保存在数据库中。
事务的持久性
 保证持久性的策略就是 Write Ahead Logging。在事务提交之前,备份一份事务的操作日志在磁盘上,备份成功再允许事务成功提交。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值