普遍的数据库在运行中遇到高并发读写的问题,导致数据库思索或压力增大而无法访问,随即数据库的处理机制也就是所谓的并发控制,是保证并发执行事务在一定隔离级别上正确执行的机制。需要指出的是,并发控制由数据库的调度器负责,事务本身不知道。如下图所示,Scheduler将多个事务的读写请求排列成合法的序列,以便依次执行:在此过程中,对于可能破坏数据正确性的冲突事务,调度器可以选择以下两种处理方法:Delay:将某项事务推迟到合法时间。Abort:直接放弃提交事务,恢复事务可能带来的影响。可见Abort比Delay带来了更高的成本,下面我们同并发控制机制在不同情况下的处理方法。
分类。
从横向和纵向两个维度对常见的并发控制机制进行分类:一是乐观程度。不同的实现机制基于不同的冲突概率假设。悲观的方式认为,只要两个事务访问相同的数据库对象,就会发生冲突,所以要尽快阻止;乐观的方式认为冲突的概率不大,会延迟处理冲突的时间。如上图横坐标所示,乐观程度从左向右提高:基于Lock:最悲观的实现需要在操作开始前,甚至事务开始前,锁定要访问的数据库对象,对冲突操作Delay;
以Timestamp为基础:乐观实现,每项事务在开始时都会得到全局增长的时间戳,期望按开始时间戳依次执行,在操作数据库对象时检查冲突,选择Delay或Abort;以Validation为基础:更加乐观的实现,仅在Commit之前进行Validate,Abort交易冲突。可见,不同乐观程度机制的本质区别在于,检查或预测冲突的时机,Lock在事务开始时,Timestamp在运行时,Validation在最后的Commit之前。与悲观的方式相比,乐观的机制能够获得更高的并发性,而且一旦发生冲突,Abort事务也会比Delay花费更多。
2.单版VS多版。
在相同的乐观程度下,还有多版本的实现。所谓多版本,就是每次需要修改数据库对象时,生成一个新的数据版本,每个对象的多个版本共存。阅读请求可以直接访问相应版本的数据,避免读写事务和只读事务之间的堵塞。当然,多个版本也会带来不同版本的维护成本。如果需要垃圾回收机制,可以释放任何东西都看不到的版本。需要指出的是,这些并发控制机制不与具体的隔离水平相结合,通过冲突判断的不同规则可以实现不同强度的隔离水平,基于Serializable具体介绍各机制的实现方式。
基于Lock实现的Scheduler需要在事务访问数据前添加必要的锁保护。为了提高并发性,会根据实际访问情况分配不同模式的锁,常见的有读写锁、更新锁等。最简单的是,需要长时间持有锁,直到事务结束。为了尽可能在保证正确性的基础上提高并行性,数据库中常用的锁定方法称为两阶段锁(2PL)Growing阶段可以申请锁定,Shrinking阶段只能释放,即第一次锁定释放后不能再有锁定要求。需要注意的是,2PL不能解决锁定问题,所以需要有锁定检测和处理机制,通常选择锁定事务进行Abort。
Scheduler还需要与LockTable相匹配,如下图所示,它是一个可能获得LockTable信息的示意,每个被访问的数据库对象都会在LockTable中有相应的表项,其中记录了当前最高的持有锁模式,是否有事务在Delay,以及是否有事务在Delay,以及是否有事务在Delay,以及是否有事务在Delay,以及是否有任何事务在Delay,以及是否有任何事务在Delay,以及是否有任何事务在Delay,以及是否有任何事务在Delay,以及是否有任何事务在Commit或Abort之后,Scheduler会根据不同的策略唤醒等待队列中的Delay事务。
还没有评论,来说两句吧...