数据库:掌握事务

事务在MySQL中相当于一个工作单元,使用事务可以确保同时发生的行为与数据的有效性不发生冲突,并且维护数据的完整性,确保数据的有效性。

事务概述

所谓事务,就是用户定义的一个数据库操作序列,这些操作要么全做,要么全不做,是一个不可分割的工作单位。事务是单个的工作单元,是数据库中不可再分的基本部分。

事务处理机制在程序开发过程中有非常重要的作用,它可以使整个系统更加安全。例如,在银行处理转账业务时,如果A账户中的金额刚被转出,而B账户还没来得及接收就发生停电;或A账户中的金额在转出过程中因出现错误未转出,但B账户却已完成转入工作,这会给银行和个人带来很大的经济损失。采用事务处理机制后,一旦在转账过程中发生意外,则整个转账业务将全部撤销,不做任何处理,从而确保数据的一致性和有效性。

MySQL系统具有事务处理功能,但是并不是所有的存储引擎都支持事务,如InnoDB和BDB支持,但MyISAM和MEMORY不支持。

事务的ACID特性

事务是由有限的数据库操作序列组成的,但并不是任意的数据库操作序列都能成为序列,为了保护数据的完整性,一般都要求事务具有以下4个特征:原子性、一致性、隔离性、持久性。

原子性(Atomic)

一个事务是一个不可分割的工作单位,事务在执行时,应该遵守“要么不做,要么全做”(Nothing or All)的原则,即不允许事务部分地完成,即使因为故障而使事务未能完成,它执行的部分结果也要被取消。保证原子性是数据系统本身的职责,由DBMS的事务管理子系统实现。

一致性(Consistency)

事务对数据库的作用是使数据库从一个一致状态转变到另一个一致状态。所谓数据库的一致状态,是指数据库中的数据满足完整性约束。例如,在银行企业中,“从账号A转移资金金额R到账号B”是一个典型的事务,这个事务包括两个操作,从账号A中减去资金额R和在账号B中增加资金额R,如果只执行其中的一个操作,则数据库处于不一致状态,账务会出现问题,也就是说,两个操作要么全做,要么全不做,否则就不能成为事务。可见事务的一致性与原子性是密切相关的。确保单个事务的一致性是编写事务的应用程序员的职责,在系统运行时,是由DBMS的完整性子系统实现的。

隔离性(Isolation)

如果多个事务并发执行,就应像各个事务独立执行一样,一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的。并发控制就是为了保证事务间的隔离性。隔离性是由DBMS的并发控制子系统实现的。

持久性(Durability)

最后,一个事务一旦提交,它对数据库中数据的改变就应该是持久的。如果提交一个事务以后计算机瘫痪,或数据库因故障而受到破坏,那么重新启动计算机后,DBMS也应该能够恢复,该事务的结果将依然是存在的。

事务的定义

一个事务可以是一组SQL语句、一条SQL语句或整个程序,一个应用程序可以包括多个事务。事务的开始与结束可以由用户显式控制。如果用户没有显式地定义事务,则由DBMS按照默认规则自动划分事务。

# 开始事务
START TRANSACTION|BEGIN WORK;

# 结束事务
COMMIT [WORK] [AND [NO] CHAIN] [[NO] RELEASE];
AND CHAIN: 在当前事务结束时,立刻启动一个新事务,并且新事务与刚结束的事务有相同的隔离等级。
RELEASE: 在终止当前事务后,会让服务器断开与当前客户端的连接。

# 撤销事务
ROLLBACK [WORK] [AND [NO] CHAIN] [[NO] RELEASE];

# 设置保存点
SAVEPOINT identifier;

# 回滚事务
ROLLBACK [WORK] TO SAVEPOINT identifier;

# 删除保存点
RELEASE SAVEPOINT identifier;

# 改变MySQL的自动提交模式
SET @@AUTOCOMMIT=0/1    # 0关闭、1打开

MySQL使用的是平面事务模型,因此嵌套的事务是不允许的。在第1个事务中使用START TRANSACTION命令后,当第2个事务开始时,系统会自动提交第1个事务。

在MySQL中,当一个会话开始时,系统变量@@AUTOCOMMIT的值为1,即自动提交功能是打开的,用户每执行一条SQL语句,该语句对数据库的修改就立即被提交成为持久性修改保存在磁盘上,一个事务也就结束了。

事务并发操作引起的问题

当同一数据库系统中有多个事务并发运行时,如果不加以适当控制,就可能产生数据的不一致性问题。数据库的并发操作导致的数据库的不一致性主要有3种:丢失更新、脏读和不可重复读。

例如:并发取款操作。假设存款金额R=1000元,甲事务T1取走存款100元,乙事务T2取走存款200元,如果正常操作,即甲事务T1执行完毕再执行乙事务T2,存款余额更新后应该是700元,但是如果按照如下顺序操作,则会有不同的结果。

甲事务T1读取存款余额R=1000元。
乙事务T2读取存款余额R=1000元。
甲事务T1取走存款100元,修改存款金额R=R-100=900,把R=900写回到数据库。
乙事务T2取走存款200元,修改存款金额R=R-200=800,把8=900写回到数据库。

结果两个事务共取走存款300元,而数据库中的存款却只少了200元。得到这种错误的结果是由于甲、乙两个事务并发操作引起的。

丢失更新(Lost Update)

当两个事务T1和T2读入同一数据做修改并发执行时,T2把T1或T1把T2的修改结果覆盖掉,造成了数据的丢失更新问题,导致数据不一致。

脏读(Dirty Reads)

脏读也称“污读”,即事务1更新了数据R,事务T2读取了更新后的数据R,事务T1由于某种原因被撤销,修改无效,数据R恢复原值。这样事务T2得到的数据与数据库的内容不一致,这种情况称为脏读。将这些未提交的随后又被撤销的更新数据称为脏数据

不可重复读(Non-repeatable Reads)

一个事务对同一行数据在不同的时刻进行读取,但是却得到了不同的结果。不可重复读包括以下情况:

  • 事务T1读取了数据R,事务T2读取并更新了数据R,当事务T1再读取数据R以进行核对时,得到的两次读取值不一致。

  • 事务在操作过程中查询两次,第2次查询的结果包含了第1次查询中未出现的数据或者缺少了第1次查询中出现的数据(这里并不要求两次查询的SQL语句相同)。这种现象就称为幻读(Phantom Reads)。这是因为在两次查询过程中有另外一个事务插入或删除了数据。

事务隔离级别

在并发操作带来的问题中,“丢失更新”是应该完全避免的。但防止更新丢失,并不能单靠数据事务控制器来解决,需要应用程序对要更新的数据加必要的锁来解决,因此防止丢失更新应该是应用程序的责任。脏读和“不可重复读”其实都是数据库的一致性问题,必须由数据库提供一定的事务隔离机制来解决。数据库实现事务隔离的方式,基本上可以分为以下两种:

  • 在读取数据前,对其加锁,阻止其他事务对数据进行修改。

  • 不加任何锁,通过一定机制生成一个数据请求时间点的一致性数据快照,并用这个快照来提供一定级别(语句级或事务级)的一致性读取。从用户的角度来看,好像是数据库可以提供同一数据的多个版本,因此这种技术叫作数据多版本并发控制(Multi Version Concurrency Control, MVCC或MCC),也经常称为多版本数据库。

为了解决“隔离”与“并发”的矛盾,ISO/ANSI SQL 92定义了4个事务隔离级别,每个级别的隔离程序不同,允许出现的副作用也不同,可以根据自己的业务逻辑要求来选择,通过选择不同的隔离级别来平衡“隔离”与“并发”的矛盾。

  • 未提交读(Read Uncommitted)。该级别允许脏读,但不允许丢失更新。如果一个事务已经开始写数据,另外一个事务则不允许同时进行写数据,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。

  • 已提交读(Read Committed)。该级别允许不可重复读,但不允许脏读。这可以通过“共享读锁”和“排他写锁”实现。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写将会禁止其他事务访问该行。

  • 可重复读(Repeated Read)。该级别禁止不可重复读和脏读,但是有时可能出现幻影数据。这可以通过“共享读锁”和“排他写锁”实现。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。

  • 可序列化(Serializable)。该级别提供严格的事务隔离。它要求事务序列化执行,即事务只能一个接着一个地执行,但不能并发执行,如果仅仅通过“行级锁定”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。

隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为已提交读,它能够避免脏读,而且具有较好的并发性能。尽管它会导致不可重复读、幻读和第2类丢失更新这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。

隔离级别读数据一致性脏读不可重复读幻读
未提交读最低级别,只能保证不读物理上损坏的数据
已提交读语句级
可重复读事务级
可序列化最高级别,事务级

(最近更新:2019年09月03日)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值