最后更新:2020-04-21 13:22:16 手机定位技术交流文章
1.数据库并发控制的作用
1.1事务概念
在引入并发控制之前,您首先需要了解事务。数据库提供几个基本操作,如添加、删除、修改和检查。用户可以灵活组合这些操作来实现复杂的语义。在许多情况下,用户希望一组操作作为一个整体生效,这是一个事务。事务是数据库状态变化的基本单位,包含一个或多个操作(例如,多个SQL语句)。经典的转账交易包括三个操作:(1)检查账户a的余额是否充足。(2)如足够,从甲方扣除100元..(3)增加乙账户100元。
事务有一个基本特征:这组操作要么一起生效,要么都不生效。如果在事务执行过程中出现错误,必须撤销所有已经执行的操作。这是事务的原子性。如果在失败发生后一些有效的事务不能被撤回,数据库将进入一个不一致的状态,这与真实世界的事实相反。比如账户a扣100元转账失败,账户b还没有增加金额。如果账户a的扣款操作没有被撤销,世界将莫名其妙地损失100元。原子性可以通过日志记录(更改前的值)来实现,一些数据库在本地缓存事务操作。如果失败,缓存中的操作将被直接丢弃。
一旦事务被提交,其结果就不能改变。即使发生系统中断,重新启动后数据库的状态也与中断前相同,这就是事务的持久性。只要数据存储在非易失性存储介质上,停机就不会导致数据丢失。因此,数据库可以采用以下方法来确保持久性:(1)所有更改都保证在事务完成之前存储在磁盘上。或者(2)在提交完成之前,事务的变更信息以日志的形式存储在磁盘中,重启过程根据日志恢复数据库系统的内存状态。一般来说,数据库会选择方法(2),留给读者思考的理由。
为了提高资源利用率、事务执行效率和减少响应时间,数据库允许并发执行事务。但是,当多个事务同时在同一个对象上操作时,不可避免地会有冲突。事务的中间状态可能暴露给其他事务,导致一些事务根据其他事务的中间状态将错误的值写入数据库。需要提供一种机制来确保事务执行不受并发事务的影响,使用户感觉好像只有他们自己发起的事务正在被执行,这就是隔离。隔离允许用户关注单个事务的逻辑,而不考虑并发执行的影响。数据库通过并发控制机制保证隔离。因为隔离需要高事务执行顺序,所以许多数据库提供不同的选项,用户可以牺牲一些隔离来提高系统性能。这些不同的选项是事务隔离级别。
该数据库反映了现实世界,其中有许多限制,如:无论如何转移帐户,总金额不会改变和其他实际的限制;年龄不能是负面的,性别只能有完整性约束,如男性、女性和跨性别选择。事务执行不能打破这些约束,并确保事务从一个正确的状态转移到另一个正确的状态,这就是一致性。与前三个属性不同,数据库实现完全保证了一致性,这取决于数据库实现(原子性、持久性和隔离也是为了确保一致性),还取决于应用程序端编写的事务逻辑。
1.2如何确保事务并发控制中的隔离
为了确保隔离,一种方法是顺序执行所有事务,这样事务就不会相互干扰。然而,串行执行的效率非常低。为了提高吞吐量和减少响应时间,数据库通常允许多个事务同时执行。因此,并发控制模块需要确保事务并发执行的效果与事务串行执行的效果完全相同,以满足隔离的要求。
为了描述并发控制如何确保隔离,我们简化了事务模型。一个事务由一个或多个操作组成,所有这些操作最终都可以分为一系列读取和写入。一批同时发生的事务,即所有读取和写入的执行顺序,被定义为一个计划,例如:
T1和T2同时执行。时间表(连续时间表是T1。读(一),T2。读取(B),T1。写(A),T1。读(二),T2。写(一)。如果并发事务执行的调度效果等同于串行执行的调度效果,则可以满足可串行化。一个调度不断地改变读写操作的顺序,并且总是变成一个可序列化的调度,但是一些改变可能导致不同的事务执行结果。在计划中,如果两个相邻的操作改变位置,导致事务结果改变,则这两个操作是冲突的。冲突需要满足以下条件:
1.这两个操作来自不同的事务2。至少一个是写操作3。操作对象是相同的
因此,常见的冲突包括:读写冲突。事务A首先读取一行数据,事务B稍后修改该行数据,事务B首先修改一行数据,事务A稍后读取该行记录。事务a读取不同的结果。这种冲突可能会导致不可重复的异常读取和异常的肮脏读取。
读写冲突。这与阅读和写作之间的冲突是一样的原因。这种冲突可能会导致不正常的读数。写作冲突。两个操作相继写入一个对象,后一个操作的结果决定了写入的最终结果。这种冲突可能会导致更新失去视觉。
只要数据库保证并发事务的调度保持冲突操作的执行顺序不变,并且只交换不冲突的操作,它就可以被称为串行调度,并且它们可以被认为是等价的。这种等价的判断方法被称为冲突等价:两个调度的冲突操作顺序相同。
例如,在下图所示的示例中,T1写(A)与T3读(A)冲突,并且T1发生在T3之前。t1读(B)与T2写(B)冲突,T2在T1之前,因此左边的事务执行时间表相当于T2、T1和T3的串行执行时间表(右边)。左边的执行序列满足冲突可序列化性。


分析了另一个反例:T1读(A)与T2写(A)冲突,T1在T2之前,T2写(A)与T2写(A)冲突,T2在T1之前。下图所示的时间表不能等同于任何串行时间表。这是一个不符合冲突可序列化执行顺序的异常,将导致更新丢失。

一般来说,可序列化性是一个相对严格的要求。为了提高数据库系统的并发性能,许多用户愿意降低隔离要求来寻求更好的性能。数据库系统通常实现多个隔离级别,供用户灵活选择。有关事务隔离级别,请参考本文。
并发控制的要求很明确,如何实现?下面,根据冲突检测的乐观性,将逐一介绍并发控制的常用实现方法。
2.基于两阶段锁的并发控制
2.1 2PL
因为有必要确保操作以正确的顺序执行,所以最容易想到的方法是锁定和保护访问对象。数据库系统的锁管理器模块专门负责锁定和释放被访问对象的锁,确保只有持有锁的事务才能操作相应的对象。锁可以分为两类:S锁和X锁,其中S锁是用于读请求的共享锁,X锁是用于写请求的排他锁。它们的兼容性如下:在同一个对象上操作时,只有两个读请求是相互兼容的,并且可以同时执行。由于锁冲突,读取和写入操作都将串行执行。

2PL(两阶段锁定)是数据库中最常见的基于锁定的并发控制协议。顾名思义,它由两个阶段组成:
阶段1:增长,事务从锁管理器请求它需要的所有锁(存在锁失败的可能性)。
第二阶段:收缩,事务释放在增长阶段获得的锁,并且不允许请求新的锁。
为什么锁定和解锁应该分为两个阶段?2PL并发控制的目的是实现可串行化。如果并发控制没有预先应用于所有需要的锁,但允许在释放锁后再次应用锁,那么可能会发生其他事务在事务内的两个操作中修改相同对象之间的此对象(如下图所示),从而无法实现冲突的可序列化并导致不一致的现象(下面的示例是丢失更新)。

2PL可以保证冲突的可序列化性,因为事务必须拥有执行所需的所有锁。例如,正在进行的事务A与事务B冲突。事务B要么已完成执行,要么仍在等待。因此,当BA或AB被串行执行时,那些冲突操作的执行顺序与冲突操作的执行顺序一致。
因此,只要使用2PL,数据库能否确保一致性和隔离性?让我们看看这个例子:

上述执行顺序与2PL一致,但T2读取的是未提交的数据。如果T1此时回滚,将触发级联回滚(T1的更改不会被任何事务看到)。因此,数据库通常使用增强版本的2PL,这与2PL略有不同:在收缩阶段,锁只能在事务结束后释放,从而完全防止事务的未提交数据被读取。
2.2死锁处理
并发事务锁定和解锁不能避免问题死锁:事务1持有B锁,如A锁,而事务2持有A锁,如B锁。目前,死锁问题有两种解决方案:
死锁检测:数据库系统根据等待图记录事务的等待关系,其中点代表事务,有向边代表等待另一个事务解锁的事务。当等待图有一个环时,意味着死锁已经发生。系统后台将定期检测等待图。如果找到一个环,有必要选择一个适当的事务中止。

死锁预防:当一个事务请求一个已经持有的锁时,数据库系统会终止其中一个事务以防止死锁(通常,事务持续的时间越长,保留的优先级越高)。这种预期的方法不需要等待图,但是增加了事务被终止的速率。
2.3方向锁
如果只有行锁,事务需要获取1亿个行锁来更新1亿条记录,这将占用大量的内存资源。我们知道锁用于保护数据库中的访问对象。根据它们的大小,这些对象可以是属性、元组、页面和表。相应的锁可以分为行锁、页锁和表锁(没有人实现属性锁;对于OLTP数据库,最小的操作单元是行)。对于事务,当然最好获得最小数量的锁,例如更新1亿条记录,也许添加一个表锁就足够了。
更高级别的锁(如表锁)可以有效地减少资源占用,显著减少锁检查的数量,但严重限制并发性。较低级别的锁(如行锁)有利于并发执行,但是当有许多事务请求对象时,需要进行大量的锁检查。为了解决高级锁限制并发的问题,数据库系统引入了意图锁的概念:
意图共享(IS):表示表中的一个或多个对象受S锁保护,例如,表加IS,并且表中至少有一行受S锁保护。
意向性排他(IX):表示其中的一个或多个对象受X锁保护。例如,如果一个表添加了IX,表中至少有一行受X-Lock保护。
共享+意图独占(六):表示至少有一个内部对象受X锁保护,并且其自身受S锁保护。例如,如果某个操作需要进行全表扫描并更改了表中的几行,则可以向表中添加六行。读者可以思考为什么没有十九或XIS
意大利锁与普通锁的兼容关系如下:

意大利锁的优点是,当IX被添加到表中时,它意味着表中有正在被修改的行。
(1)当此时对表发起DDL操作时,需要请求表的X锁,因此当表持有X锁时,它将直接等待,而不是逐个检查表中的行是否持有行锁,从而有效地降低了检查开销。
(2)此时,还有其他读写事务。由于该表是用IX而不是X添加的,所以对该行的读写请求不会被阻止(先将IX添加到表中,然后将S/X添加到记录中)。如果事务不涉及带有X锁的行,它可以正常执行,增加了系统的并发性。
3.基于时序的并发控制
时间戳被分配给每个事务,以确定事务的执行顺序。当事务1的时间戳小于事务2时,数据库系统应确保事务1在事务2之前执行。时间戳分布包括:(1)物理时钟;(2)逻辑时钟;(2)混合时钟。
3.1基本测试/输出
基于读写操作的并发控制,读写不需要被锁定。每行记录标记上次修改和读取它的事务的时间戳。当事务的时间戳小于记录的时间戳(不能读取“未来”数据)时,需要在中止后再次执行。假设记录X标有读写两个时间戳:WTS (x)和实时交易系统(X),交易的时间戳为实时交易系统,可见性判断如下:
读取:tts < WTS(X):该对象对该事务不可见。中止事务,用新的时间戳重新启动。TTS >。WTS(十):这个对象对事务是可见的,更新RTS(十)=最大值(TTS,RTS(十))。为了满足可重复读取,事务复制x的值。为了防止读取脏数据,可以在记录上做一个特殊标记,读取请求需要等待事务提交后才能读取。
写作:tts < WTS(十)| | TTS & ltRts (x):中止事务,重新启动。TTS >。WTS(十)和;& amp专题小组。实时交易系统(十):交易更新十,WTS(十)=实时交易系统。
TTS > Rts(X)的原因是为了防止以下情况:读取请求的时间戳是Rts,X已被读取,时间戳被设置为RTS(X)=rts。如果TTS < Rts(X),并且更新成功,则Rts读取请求再次读取以查看新的更改,这违反了可重复读取,因此这是为了避免读写冲突。最后的读写时间存储在记录中,这可以确保冲突的可串行化。
这种方法还可以避免写偏斜,例如:初始状态,X和Y记录,X=-3,Y=5,X+Y > 0,RTS(X)=RTS(Y)=WTS(X)=WTS(Y)=0。事务T1的时间戳是TTS1=1,事务T2的时间戳是TTS2=2。

它的缺陷包括:长事务很容易饿死,因为长事务的时间戳太小,并且在执行一段时间后读取更新数据的概率很高,导致中止。
读操作也会产生写操作(写即时战略)。
4.基于验证的并发控制(OCC)
在执行过程中,每个事务都维护自己的写操作(在事务执行过程中,基本传输/输出将数据写入数据库)和相应的实时传输/WTS。提交时,它判断其更改是否与数据库中的现有数据冲突。如果没有,它会写入数据库。OCC分为三个阶段:
阅读和;写入阶段:即读写阶段,事务维护读取的结果和要提交的更改,以及写入记录的RTS和WTS。
验证阶段:检查事务是否与数据库中的数据冲突。
写阶段:无冲突写,有冲突中止,重启。
阅读和。写阶段结束,进入验证阶段相当于完成事务处理准备,进入提交阶段,进入验证阶段的时间被选择作为排序记录行的时间戳。不需要事务开始时间的原因是事务执行时间可能很长,导致稍后开始的事务可能首先提交,这将增加事务冲突的可能性。时间戳较小的事务将被写入数据库,并且肯定会中止。
验证过程
假设当前只有两个事务T1和T2,并且同一数据行已被修改,T1时间戳< T2时间戳(即验证序列:T1 < T2,对于用户,T1首先出现在T2),则存在以下条件:
(1)T1处于验证阶段,T2仍处于读写阶段。此时,只要T1和T2的读写没有冲突,就可以提交。
如果WS(T1)∪; RS(T2)∪; WS(T2))=,则意味着T2和T1所写的记录之间没有冲突。验证通过,可以写入。
否则,T2和T1之间存在读写冲突,T1需要回滚。读写冲突:T2读取T1之前写的版本。T1提交后,可以读取T1编写的版本,不能重复读取。写-写冲突:T2可能从旧版本更新并再次写入,导致T1更新丢失。
(2)T1完成验证阶段并进入写入阶段,直到提交完成,这已经是不可逆的。T1进入写阶段之前T2的读和写肯定不会与T1的操作冲突(因为T1验证已经通过)。读写操作在T2之后继续,可能与T1提交的操作冲突,因此T2进入验证阶段:如果WS(T1)∪RS(T2)= T2不读取T1写入的记录,验证通过,T2可以写入。(为什么不验证WS(T2)?已经提交了WS(T1),其时间戳小于WS(T2)。WS(T2)的前一部分肯定不会与后一部分冲突,因为T1写的对象还没有被读取,写入没有问题,也不会覆盖WS(T1)的写入
否则,T2和T1之间会有读写冲突,T2需要回滚。读写冲突:T2读取T1之前写的版本。T1提交后,可以读取T1编写的版本,不能重复读取。写-写冲突:T2可能从旧版本更新并再次写入,导致T1更新丢失。
5.基于MVCC的并发控制
数据库维护记录的多个物理版本。当事务被写入时,新版本的写入数据被创建,并且读请求根据事务/语句开始时的快照信息获得当时存在的最新版本数据。它带来的最直接的好处是写操作不会阻止读操作,读操作也不会阻止写操作。由于这种冲突,读取请求永远不会失败(例如,单一版本的测试/输出)或等待(例如,单一版本的2PL)。对于数据库请求,读取请求通常多于写入请求。几乎所有主流数据库都采用了这种优化技术。
MVCC是读写请求的优化技术。它没有完全解决数据库并发问题。它需要与前面提到的并发控制技术相结合,以提供完整的并发控制能力。常见的并发控制技术包括MV-2PL、MV-T/O和MV-OCC,它们的特点如下表所示:

MVCC还需要考虑两个关键点:多版本数据的存储和冗余多版本数据的恢复。
多版本数据存储方法可以大致分为两种类型:(1)仅追加方法,其中新旧版本存储在相同的表空中,例如基于LSM树的存储引擎。(2)最新版本数据记录在主表空之间,前镜像记录在其他表空或数据段之间,例如,InnoDB的多版本信息记录在撤销日志中。多版本数据收集也称为垃圾收集。那些没有机会被任何读请求检索的旧版本记录应该被及时删除。
6.摘要
根据冲突处理的时间性(乐观性),引入了基于锁(事务开始前防止冲突)、T/O(事务执行时判断冲突)和验证(事务提交时验证冲突)的事务并发控制机制。不同的实现适合不同的工作负载和具有较少并发冲突的工作负载,这当然适合更乐观的并发控制方法。MVCC可以解决只读事务和读写事务之间的相互阻塞问题,提高事务的并发读取,被大多数主流数据库系统采用。
本文由 在线网速测试 整理编辑,转载请注明出处。