目录
事务模型
在学习事务之前,先了解一下事务的模型。
事务必须处于以下状态之一:
· 活跃(active) 该状态为事务执行的初始状态。
· 部分提交(partially committed) 当事务中最后一条语句被执行后进入该状态。
· 失效(failed) 当正常执行不能继续时候进入该状态。
· 中止(aborted) 事务已回滚并且数据库已被恢复到它在事务开始之前的状态。
· 提交(committed) 事务成功完成后进入该状态。
部分提交状态虽然已经标识了事务完成全部语句执行,但由于内存到硬盘的传输可能存在硬件故障,因此部分提交状态仍然可能转为中止状态。
进入中止状态后系统有两种选择:
1.重启事务 此时通常为硬件错误而重启事务。
2.杀死事务 通常为语句逻辑错误而杀死事务,此时事务必须进行重新编写后再进行执行。
事务的并发
事务并发执行有两个优点:
1. 提高吞吐量和资源利用率 一个事务由多个步骤组成。一些涉及I/O操作;还有一些涉及CPU处理。
因此并行执行就是让让多个事务中的I/O操作、CPU处理并行执行。这提高了处理器、磁盘的利用率。
2.减少等待时间 系统中可能运行着各种各样的事务,有的耗时长,有的耗时很短。并行执行能让长短
不同的事务共享CPU和磁盘。这减少了系统处理这些事务的平均等待时间。
· 用下面的例子来说明数据库是如何实现事务的并发:
假设账户A、账户B分别拥有1000、2000
事务T1将账户A的50元转到账户B,而事务T2将账户A的1/10转到账户B。
如果按照串行的执行顺序,那么T1、T2的执行顺序如下两图。
数据库系统通过调整两个事务的语句执行顺序来实现事务并发处理。
下图就是对图1-1进行语句调整后的执行顺序
但显然语句和语句之间是不能随意调整顺序的。
如下图1-4的执行顺序,它的执行结果和图1-1是不一致的。
事务的隔离性
对于某些应用程序来说可能并不需要事务有很高并发的并发度。
针对于不同的需求,SQL标准定义了更加详细的隔离性级别:
1.可重复读:只允许读取已经提交的数据,并进一步要求在一个事务两次读取一个数据项期间,其他事务不能更新该数据项。
在MySQL中,可以通过设置参数来配置隔离级别。将隔离级别设置为可重复读,可以使用以下命令:
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
2.已提交读:只允许读取已经提交的数据,但并不要求可重复读。通俗来说就是一个事务两次读取一个数据项期间,另外的事务可以更新该数据项。
为了方便理解,考虑下面的例子:
假设有一个商品销售网站在出售一款商品A,用户选择了该商品A,此时商品A还是有货的状态,但是当用户结账时候,该商品A已经售罄了。
在MySQL中,可以通过设置参数来配置隔离级别。将隔离级别设置为可重复读,可以使用以下命令:
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
3.未提交读:允许读取其他事务未提交的数据。这是SQL标准中最低限度级别。
在MySQL中,可以通过设置参数来配置隔离级别。将隔离级别设置为可重复读,可以使用以下命令:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
事务隔离性的实现
1.基于锁的实现
基于锁的实现当中,为了确保隔离性的关键点就是对数据项以互斥的方式进行访问;也就是说,当一个事务访问某个数据项时,其他事务都不能修改该数据项。
给一个数据项加锁通常有两种方式:
1.共享锁。如果一个事务T1获得了数据项Q的共享锁,则T1可以读数据项Q,但不能写数据项Q。
2.排他锁。如果一个事务T1获得了数据项Q的共享锁,则T1既可以读又可以写数据项Q。
要访问一个数据项,事务T1必须先向并控制管理器申请锁,如果锁已经被另外的事务申请了,那么T1只能等待,直到其他事务所持有的锁释放。
下图就是一个上锁的银行示例,令账号A与账号B同时被事务T3与T4访问。其中事务T3从账号B转账50到账号A,事务T4显示账号A与B上的总金额。
2.基于时间戳的实现
对于数据库中的每一个事务,我们把唯一的、固定的时间戳和事务关联起来,此时时间戳记为TS(T)。该时间戳是在事务T开始执行之前由数据库系统所赋予的。若一个事务T1进入系统后另一个事务T2进入系统,此时TS(T1)< TS(T2),根据这个判断结果系统就会保证事务T1先于T2完成执行。
注:时间戳可以是
1.系统时钟
2.逻辑计数器