事务及事务ACID特性的理解

首先,我们需要弄清楚为什么会出现事务。事务从本质上来说是为了应用层服务的。也就是说是为了方便业务系统的开发,简化业务系统的编程模型,事务并不是伴随着数据库系统天生就有的。事务需要具有原子性(atomicity)、一致性(consistency)、隔离性(isolation)和持久性(durability),也就是常说的事务的ACID四大特性。

原子性(atomicity)
一个事务的原子性是指一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚。

一致性(consistency)
通常书本上的解释是数据库总是从一个一致性的状态转移到另外一个一致性的状态。看完之后是不是还是不知道什么是一致性?其实我看完之后也不是很明白。经过查找了一番资料,记录一下我对一致性的理解。简单来说就是数据(注意:此处是数据,而不是数据库)从处于一种语义上有意义且正确的状态,转移到另外一种语义上有意义且正确的状态。什么是符合语义上的有意义且正确的状态?就是指符合业务系统的业务要求,且在业务系统中数据库中各数据是有意义且正确的状态。

举个例子。

张三要向李四支付100元,而张三的账户中只有90元,并且我们给定账户余额这一列的约束是,不能小于0,那么很明显这条事务执行会失败,因为 90 - 100 = -10,小于我们给定的约束了。

在这个例子里,支付之前我们数据库里的数据都是符合约束的,但是如果事务执行成功了,我们的数据库数据就破坏约束了,因此事务不能成功,这里我们说事务提供了一致性的保证。我们再看一个例子

张三要向李四支付100元,而张三的账户中只有90元,我们的账户余额列没有任何约束,但是我们业务上不允许账户余额小于0,因此支付完成后我们会检查张三的账户余额,发现余额小于0了,于是我们进行了事务的回滚

这个例子里,如果事务执行成功了,虽然没有破坏数据库的约束,但是破坏了我们应用层的约束。而事务的回滚保证了我们的约束,因此也可以说事务提供了一致性保证。最后我们再看个例子

张三向李四支付100元,而张三的账户中只有90元,我们的账户余额没有任何约束。然后支付成功了。

估计按照很多人的理解,事务不是保证一致性么?直观上账户余额为什么能为负呢?但这里事务执行前后和执行后,我们的系统没有任何的约束被破坏,一直都是保持正确的状态。所以数据库还是具有一致性的。

综上所述,一致性就是对数据可见性的约束,保证在一个事务中多次操作的数据中间状态对其他的事务不可见。因为这些中间状态是一种过渡状态,与事务的开始状态和事务的结束状态是不一致的。
可能你会有疑问,

  1. 数据中间状态对其他事务不可见,这不是事务的隔离性么?为什么还要单独说一致性和隔离性?
    一致性和隔离性的侧重点不同,一致性强调事务前后数据在业务系统有意义且是正常的,而隔离性强调事务的中间状态对其他事务是不可见的。
  2. 数据中间状态对其他的事务不可见,要么转账全部成功,要么转账全部失败,这不是事务的原子性么?为什么还要单独说一致性和原子性?
    一致性和原子性侧重点也不同,原子性的强调事务中多个步骤要么全部成功,要么全部失败,不存在部分成功的状态。
  3. 事务隔离级别中有一级是未提交读(read uncommitted),中间状态的数据对外部是可见的,这个时候会出现脏读,明显违背一致性,怎么解释?
    事务的ACID特性是描述最理想的事务应该怎么做,是一个强一致的状态,如果需要做到这点,需要使用排它锁把事务排成一排,即Serialization可串行化事务隔离级别,这样性能就大大降低了。现实是骨感的,所以使用了不同隔离级别来破坏一致性,以求获取更好的性能。

隔离性(isolation)
通常来说是一个事务所做的修改在最终提交之前,对其他的事务是不可见的。严格上来说目前只要serializtion隔离级别符合要求,但是这样会导致性能大大的降低,因此现实中常常为了性能而不得不进行妥协,使用比较弱的隔离性来达到比较好的性能。
ANSI规定的隔离级别有四种,分别是未提交读(read uncommitted)、提交读(read committed)、可重复读(repeatable read)和可串行化(serializable)。

  • 未提交读(read uncommitted)
    未提交读是指事务中的修改,即使没有提交,对其他事务也都是可见的。事务中可以读取未提交的数据,这也被称为脏读(dirty read)。
  • 提交读(read committed)
    大多数数据库系统的默认隔离界别是提交读(但是MySQL不是)。提交读是指一个事务开始时,只能看见已经提交的事务所做的修改。换句话说,一个事务从开始知道提交之前,所做的任何修改对其他书屋都是不可见的。这个级别有时候也叫做不可重复读(nonrepeatable),因为两次执行同样的查询,可能会得到不一样的结果。例如,事务1首先查询某条数据是否存在,然后再更新这条记录,在这两次操作之间事务2删除了这条记录,导致事务1更新记录失败。提交读会有幻读(phantom read)的问题。所谓幻读,指的是当某个事务在读取某个范围内的记录时,另外一个事务在该范围内插入了新的记录,当之前的事务再次读取同范围的记录时,会产生幻行(phantom row),即多读取一些记录(另一个事务插入了新记录)或者少读取一些记录(另一个事务删除了一些记录)。
  • 可重复读(repeatable read)
    该级别保证了在同一个事务中多次读取同样记录的结果是一样的。MySQL默认隔离级别就是可重复读。MySQL通过MVCC(多版本并发控制)来实现可重复读,该隔离级别也会存在幻读问题。
  • 可串行化(serializable)
    可串行化是事务最高隔离级别。他通过强制事务串行执行,来避免前面说的幻读问题。但是这样会导致性能大大降低。

从上述描述来看,看起来可重复读和不可重复读级别都是指同一个事务前后读取的数据不一致,但是侧重点不一样。可重复读是强调同一个事务中两次查询之间第二个事务删除或者插入了一些记录导致了第一个事务看到不一样的结果,而提交度则是指同一个事务中两次查询之间第二个事务更新了记录导致第一个事务看到不一样的结果。

使用Spring进行开发的时候可能会发现Spring还有一种默认隔离级别default,Spring下的这种隔离级别是指使用DB默认的事务隔离级别。即把事务隔离级别交给DBA。

在基于锁的并发控制中,隔离级别决定了锁的持有时间。“C”-表示锁会持续到事务提交。 “S” –表示锁持续到当前语句执行完毕。如果锁在语句执行完毕就释放则另外一个事务就可以在这个事务提交前修改锁定的数据,从而造成混乱。

隔离级别写操作读操作范围操作(…where…)
未提交读SSS
提交读CSS
可重复读CCS
可串行化CCC

持久性(durability)
持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。当然现实中没有数据库可以做到绝对不丢失数据,如果已经做到这点那还要备份做什么?所以现实中会根据成本在持久性方面来做取舍。

参考:
知乎-如何理解数据库事务中的一致性的概念
事务隔离级别浅析

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值