数据库事务

摘要:

事务是最小的逻辑执行单元,也是数据库并发控制的基本单位,其执行的结果必须使数据库从一种一致性状态变到另一种一致性状态。事务具有四个重要特性,即原子性(Atomicity)、一致性(Consistency)、隔离性 (Isolation)和持久性 (Durability)。本文首先叙述了数据库中事务的本质及其四大特性(ACID)的内涵,然后重点介绍了事务隔离性的动机和内涵,并介绍了数据库为此所提供的事务隔离级别以及这些事务隔离级别能解决的事务并发问题。介于并发安全与并发效率的平衡,我们一般不会一味地提高事务隔离级别来保证事务并发安全性,而是通过结合其他机制(包括笔者提到的乐观锁和悲观锁机制)来解决数据库事务并发问题。

一. 事务概述

一般而言,用户的每次请求都对应一个业务逻辑方法,并且每个业务逻辑方法往往具有逻辑上的原子性。此外,一个业务逻辑方法往往包括一系列数据库原子访问操作,并且这些数据库原子访问操作应该绑定成一个整体,即要么全部执行,要么全部不执行,通过这种方式我们可以保证数据库的完整性。也就是说,事务是最小的逻辑执行单元,是数据库维护数据一致性的基本单位。

总的来说,事务是一个不可分割操作序列,也是数据库并发控制的基本单位,其执行的结果必须使数据库从一种一致性状态变到另一种一致性状态。事务具有四个重要特征,即原子性(Atomicity)、一致性(Consistency)、隔离性
(Isolation)和持久性 (Durability)。

(1). 原子性(Atomicity)

原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚。 因此,事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不会对数据库有任何影响,也就是说,事务是应用中不可再分的最小逻辑执行体。

(2). 一致性(Consistency)

一致性是指事务执行的结果必须使数据库从一种一致性状态变到另一种一致性状态,也就是说,一个事务执行之前和执行之后数据库都必须处于一致性状态。拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。

(3). 隔离性 (Isolation) — 与事务并发直接相关

隔离性是指并发执行的事务之间不能相互影响。也就是说,对于任意两个并发的事务 T1 和 T2,在事务 T1 看来,T2 要么在 T1 开始之前就已经结束,要么在 T1 结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。关于事务的隔离性下文会重点探讨。

(4). 持久性 (Durability)

持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。换句换说,事务一旦提交,对数据库所做的任何改变都要记录到永久的存储器中(通常就是保存到物理数据库)。

二. 事务隔离性的内涵

以上介绍完了事务的基本概念及其四大特性(简称ACID),现在重点来说明下事务的隔离性。我们知道,当多个线程都开启事务操作数据库中的数据时,数据库系统要能进行隔离操作以保证各个线程获取数据的准确性。也就是说,事务的隔离性主要用于解决事务的并发安全问题,那么事务的隔离性解决了哪些具体问题呢?

1、事务并发带来的问题

(1). 脏读

脏读是指在一个事务处理过程中读取了另一个事务未提交的数据。比如,当一个事务正在多次修改某个数据,而当这个事务对数据的修改还未提交时,这时一个并发的事务来访问该数据,就会造成数据的脏读。看下面的例子:

公司发工资了,领导把5000元打到singo的账号上,但是该事务并未提交,而singo正好去查看账户,发现工资已经到账,是5000元整,非常高兴。可是不幸的是,领导发现发给singo的工资金额不对,是2000元,于是迅速回滚了事务,修改金额后,将事务提交,最后singo实际的工资只有2000元,singo空欢喜一场。

出现的上述情况就是我们所说的脏读,即对于两个并发的事务(事务A:领导给singo发工资、事务B:singo查询工资账户),事务B读取了事务A尚未提交的数据。特别地,当隔离级别设置为 Read Committed 时,就可以避免脏读,但是仍可能会造成不可重复读。特别地,大多数数据库的默认级别就是Read committed,比如Sql Server , Oracle。

(2). 不可重复读

不可重复读是指:对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔该数据被另一个事务修改并提交了。例如,事务 T1 在读取某一数据,而事务 T2 立马修改了这个数据并且提交事务,当事务T1再次读取该数据就得到了不同的结果,即发生了不可重复读。不可重复读和脏读的区别是,脏读是某一事务读取了另一个事务未提交的脏数据,而不可重复读则是读取了前一事务提交的数据。看下面的例子:

singo拿着工资卡去消费,系统读取到卡里确实有2000元,而此时她的老婆也正好在网上转账,把singo工资卡的2000元转到另一账户,并在singo之前提交了事务,当singo扣款时,系统检查到singo的工资卡已经没有钱,扣款失败,singo十分纳闷,明明卡里有钱,为何……

上述情况就是我们所说的不可重复读,即两个并发的事务(事务A:singo消费、事务B:singo的老婆网上转账),事务A事先读取了数据,事务B紧接着更新了数据并提交了事务,而事务A再次读取该数据时,数据已经发生了改变。当隔离级别设置为Repeatable read时,可以避免不可重复读。这时,当singo拿着工资卡去消费时,一旦系统开始读取工资卡信息(即事务开始),singo的老婆就不可能对该记录进行修改,也就是singo的老婆不能在此时转账。特别地,MySQL的默认隔离级别就是 Repeatable read。

(3). 幻读

幻读是事务非独立执行时发生的一种现象,即在一个事务读的过程中,另外一个事务可能插入了新数据记录,影响了该事务读的结果。例如,事务 T1 对一个表中所有的行的某个数据项执行了从“1”修改为“2”的操作,这时事务T2又对这个表中插入了一行数据项,而这个数据项的数值还是为“1”并且提交给数据库。这时,操作事务 T1 的用户如果再查看刚刚修改的数据,会发现还有一行没有修改,其实这行是从事务T2中添加的,就好像产生幻觉一样,这就是发生了幻读。幻读和不可重复读都是读取了另一条已经提交的事务(这点与脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是数据记录插入/删除问题,二者关注的问题点不太相同。看下面的例子:

singo的老婆工作在银行部门,她时常通过银行内部系统查看singo的信用卡消费记录。有一天,她正在查询到singo当月信用卡的总消费金额为80元,而singo此时正好在外面胡吃海塞后在收银台买单,消费1000元,即新增了一条1000元的消费记录并提交了事务,随后singo的老婆将singo当月信用卡消费的明细打印到A4纸上,却发现消费总额为1080元,singo的老婆很诧异,以为出现了幻觉,幻读就这样产生了。当隔离级别设置为Serializable(最高的事务隔离级别)时,不仅可以避免脏读、不可重复读,还可以避免幻读。但同时代价也花费最高,性能很低,一般很少使用,因为在该级别下并发事务将串行执行。

2、小结

总的来说,事务的隔离性主要用于解决事务并发安全问题。上面提到的脏读、不可重复读和幻读三个典型问题都是在事务并发的前提下发生的,不同的是三者的问题关注点略有不同。脏读关注的是事务读取了另一个事务未提交的数据;不可重复读关注的是同一事务中对同一个数据项多次读取的结果互不相同;幻读更侧重于数据记录的插入/删除问题,比如同一事务中对符合同一条件的数据记录的多次查询的结果互不相同。更进一步地说,不可重复读关注的是数据的更新带来的问题,幻读关注的是数据的增删带来的问题。

三. 数据库的事务隔离级别

不同数据库的事务隔离级别不尽相同。比如我们在上一节提到,MySQL数据库支持下面的四种隔离级别,并且默认为 Repeatable read 级别;而在Oracle数据库中,只支持Serializable 级别和 Read committed 这两种级别,并且默认为 Read committed 级别。MySQL数据库为我们提供了四种隔离级别,分别为:

Serializable (串行化):最高级别,可避免脏读、不可重复读、幻读的发生;
Repeatable read (可重复读):可避免脏读、不可重复读的发生;
Read committed (读已提交):可避免脏读的发生;
Read uncommitted (读未提交):最低级别,任何情况都无法保证。

mongoDB不支持事务

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值