• 再读simpledb 之 事务管理的实现(1)


    在关系型数据库里面,每一项操作,都是要放在一个事务中完成。自顶向下看来,事务处在顶层的位置,事务统领了查询,统领了日志,统领了存储。

    按照类似于软件工程中 喷泉模型 的思路,用层层迭代的方式,来看待关系型数据库管理系统从小到大各个组件的扩张,最初解决了无语义的字节存储,然后附加上日志的底层实现,再加上事务(包含了日志的语义信息处理),就得到了一个最小的系统。之后的查询,优化等都可以在这个最小系统的基础上,不断丰富。

    image

    图1 最小系统

    在simpledb这个小型的教学数据库管理系统中,主要包含了两个组件:并发管理,恢复管理。实际上,还应该有一个锁管理。只是在这里只是用一个LockTable来实现。

    首先看Transaction的类图

    image

    图2 Transaction 类图

    1、并发管理

    每个事务都有自己的一个并发管理器,并发管理器跟踪当前事务持有的锁,在必要的时候会跟全局的锁表交换信息。可见,在simpledb的并发管理中,核心的是锁表,通过对锁表数据的操作,实现了并发管理。

    > LockTable

    锁表提供接口,实现的是对block的加锁、解锁,粒度是底层的数据块Block。

    锁表维护的是一个<Block,int>的字典。在处理访问数据块的冲突的时候,实现的是一个忙等待机制:在每个Block上维护了一个等待队列,当事务请求一个数据块并加锁的时候,与一个已存在的锁有冲突,则将该事务加入等待队列。只是,所有的block共享一个等待队列,同时,字段中block对应的int值记录了该block上等待的事务的个数。

    当blockc上最后一个锁释放后,等待队列中所有相关的事务同时释放,重新调度。之后,没有请求成功的事务,重新入队。

    image

    图3 锁表示意图

    Tx1,Tx2,Tx3,Tx4请求的是xLock,等待之前的sLock释放;Tx5请求的是xLock,等待之前的xLock释放

    Block上的信息通过字典来保存,事务的等待队列,通过操作系统的线程等待机制来实现,没有显示的数据结构。

    simpledb支持加锁有两种:共享锁 sLock,互斥锁xLock。sLock在字典中int值不断增加来表示,xLock通过在字典中int值置-1来表示。

    系统实现的是“读优先的”加锁机制,如下代码所示:

                      imageimage

    图4 加锁代码

    由代码可见,在加sLock的时候,只要目前持有锁的不是xLock,便可以继续往上加锁,直接访问Block;而在加xLock的时候,只要有锁,就必须等待。如果一个xLock与sLock同时竞争,显然sLock总会获得锁,而sLock总是要等待。

    为了避免“饥饿”现象的出现,在加锁的时候,增加了等待时常的控制:如果等待时间超过了阈值,事务便抛出异常,然后退出。

    解锁的时候,如果是sLock,直接将字典中block对应的标记值减1,;如果是xLock,将block从字典中删除,然后唤醒所有的等待事务。

    > ConcurrencyMgr

    用static标记了一个全局得到LockTable。所有事务共享这一个LockTable。

    额外维护了一个字典locks,<Block, string>,string值可以是S或X,标记该Block上加的是sLock还是xLock。

    加锁的时候,除了调用LockTable的接口外,还将<Block, string>对存入locks中,标记S或X。注意的是,在加xLock的时候,使用的是锁升级的策略:先价sLock,然后将sLock升级为xLock。

    另外,这里的加锁,没有考虑加锁的等待,细节放到了LockTable中。

                                                                  imageimage

    图5 ConcurrencyMgr中的加锁

    假设下:初始,locks为空。首先T1对block1申请了sLock,<block1, S>加入locks中;之后,T2对block1申请sLock,显然locks中已经存在了blk为key的内容,判断条件失败,不做任何的事情,但是,显然,T2加锁成功了。

    如果T3要在block2上加xLock,先判断是否有xLock:“locks.ContainsKey(blk) ? locks[blk].Equals("X") : false;” ;实际上既考虑了block2上有xLock的情况,也考虑block2上有sLock的情况。

    然后先加sLock,再加xLock。

    1)加sLock的阶段locks中添加了字段,同时,在locktbl.sLock执行过程中,由于事先block2上没有任何的锁,他会直接获得sLock,在locktbl.sLock中唯一做的事情,就是将locktbl中的字典locks'里block2对应的value加1,0-->1

    2)锁升级:对block2加sLock后,实际上block2上没有任何的等待队列。locktbl.xLock可以直接执行。注意:判断hasOtherSLocks的条件是vlaue>1,。而之前的sLock是value为1。故hasOtherSLock条件不成立,直接执行“locks[blk] = -1;”。这样,锁升级成功。

    image

    图6 ConcurrencyMgr加锁示意图

    如上所述,simpledb中的并发管理的实现原理通过加锁来实现,维护的锁表有效地管理了多个事务的并发执行。

    恢复管理后文待续。

  • 相关阅读:
    Audit(二)--清理Audit数据
    开启和关闭oracle数据库中的审计功能
    ORACLE AUDIT
    expdp导出卡住问题诊断
    Oracle 12c 新特性 --- 新增对数据泵操作的审计跟踪
    针对Oracle的审计方案
    深入理解Oracle的imp/exp 和各版本之间的规则
    Oracle Audit 功能的使用和说明
    Oracle的存储的三大物理文件
    操作系统核心原理-5.内存管理(下):段式内存管理
  • 原文地址:https://www.cnblogs.com/YFYkuner/p/2658580.html
Copyright © 2020-2023  润新知