DMDSC 实现原理

DMDSC是一个共享存储的数据库集群系统。多个数据库实例同时访问、修改同一个数据库,因此必然带来了全局并发问题。DMDSC集群基于单节点数据库管理系统之上,改造了Buffer缓冲区、事务系统、封锁系统和日志系统等,来适应共享存储集群节点间的全局并发访问控制要求。同时,引入缓存交换技术,提升数据在节点间的传递效率。

4.1 事务管理

多版本并发控制(MVCC)可以确保数据库的读操作与写操作不会相互阻塞,大幅度提升数据库的并发度以及使用体验,大多数主流商用数据库管理系统都实现了MVCC。DM的多版本并发控制实现策略是:数据页中只保留物理记录的最新版本数据,通过回滚记录维护数据的历史版本,通过活动事务视图(V$DSC_TRX_VIEW)判断事务可见性,确定获取哪一个版本的数据。

每一条物理记录中包含了两个字段:TID和RPTR。TID保存修改记录的事务号,RPTR保存回滚段中上一个版本回滚记录的物理地址。插入、删除和更新物理记录时,RPTR指向操作生成的回滚记录的物理地址。

回滚记录与物理记录一样,也包含了TID和RPTR这两个字段。TID保存产生回滚记录时物理记录上的TID值(也就是上一个版本的事务号),RPTR保存回滚段中上一个版本回滚记录的物理地址。

每一条记录(物理记录或回滚记录)代表一个版本。如下图所示:

各版本之间的关系

图4.1 各版本之间的关系

如何找到对当前事务可见的特定版本数据,进行可见性判断,是DM实现多版本并发控制的关键。根据事务隔离级别的不同,在事务启动时(串行化),或者语句执行时(读提交),收集这一时刻所有活动事务,并记录系统中即将产生的事务号NEXT_TID。DM多版本并发控制可见性原则:

  1. 物理记录TID等于当前事务号,说明是本事务修改的物理记录,物理记录可见
  2. 物理记录TID不在活动事务表中,并且TID小于NEXT_TID,物理记录可见
  3. 物理记录的TID包含在活动事务表中,或者TID>=NEXT_TID,物理记录不可见

为了在DMDSC集群中实现与单节点相同的多版本并发控制(MVCC)策略,每个事务需要知道所有节点当前活动的事务信息,根据事务隔离级的不同,在事务启动时(串行化),或者语句执行时(读提交),收集这一时刻所有节点上的活动事务,以及系统中即将产生的事务号NEXT_TID,记录到事务的活动事务视图中。DMDSC集群将事务信息全局化,由控制节点统一管理集群中所有节点的全局事务视图(Global Transaction View,简称GTV);与之对应的是每个节点维护一个本地事务视图(Local Transaction View,简称LTV),在事务启动、收集活动事务信息时通知全局事务视图,并获取相应的信息。

4.2 封锁管理

数据库管理系统一般采用行锁进行并发访问控制,避免多个用户同时修改相同数据;通过表锁、字典锁控制DDL和DML操作的并发访问,保证对象定义的有效性和数据访问的正确性。DM则采用了独特的封锁机制,使用TID锁和对象锁进行并发访问控制,有效减少封锁冲突、提升系统并发性能。

TID锁以事务号为封锁对象,每个事务启动时,自动以独占(X)方式对当前事务号进行封锁,由于事务号是全局唯一的,因此这把TID锁不存在冲突,总是可以封锁成功。同时,在4.1事务管理中,介绍了物理记录上包含一个TID字段,记录了修改数据的事务号。执行INSERT、DELETE、UPDATE操作修改物理记录时,设置事务号到TID字段的动作,就相当于隐式地对物理记录上了一把X方式的TID锁。因此,通过事务启动时创建的TID锁,以及写入物理记录的TID值,DM中所有修改物理记录的操作都不再需要额外的行锁,避免了大量行锁对系统资源的消耗,有效减少封锁冲突。特别是在DMDSC集群中,需要进行全局封锁,封锁的代价比单节点更高,通过TID锁可以有效减少封锁引发的性能损失。

对象锁则通过对象ID进行封锁,将对数据字典的封锁和表锁合并为对象锁,以达到减少封锁冲突、提升系统并发性能的目的。

与事务管理类似,DMDSC集群将封锁管理拆分为全局封锁服务(Global Locking Services,简称GLS)和本地封锁服务(Local Locking Services,简称LLS)两部分。整个系统中,只有控制节点拥有一个GLS。控制节点的GLS统一处理集群中所有节点的封锁请求、维护全局封锁信息、进行死锁检测,确保事务并发访问的正确性。每个节点都有一个LLS。各节点的LLS负责与GLS协调、通讯,完成事务的封锁请求,DMDSC集群中所有封锁请求都需要通过LLS向GLS发起,并在获得GLS授权后,才能进行后续操作。

4.3 闩管理

闩(Latch)是数据库管理系统的一种内部数据结构,通常用来协调、管理Buffer缓冲区、字典缓存和数据库文件等资源的并发访问。与锁(Lock)在事务生命周期中一直保持不同,闩(Latch)通常只保持极短的一段时间,比如修改Buffer中数据页内容后,马上会释放。闩(Latch)的封锁类型也比较简单,就是共享(Share)和独占(Exclusive)两种类型。

为了适用DMDSC集群,我们同样将闩划分为全局闩服务(Global Latch Services)和本地闩服务(Local Latch Services)两个部分。但是,为了与全局封锁服务GLS和本地封锁服务LLS的名字简称区分开来,我们以使用最为频繁的Buffer来命名全局闩服务。因此,全局闩服务也称为全局缓冲区服务(Global Buffer Services),简称GBS;本地闩服务也称为本地缓冲区服务(Local Buffer Services),简称LBS。

整个系统中,每一个节点上都部署一个GBS和一个LBS。GBS服务协调节点间的Latch封锁请求、以及Latch权限回收。GBS与GTV/GLS由控制节点统一管理不同,GBS不是集中式管理,而是由DMDSC集群中的所有节点共同管理,Buffer对象会根据数据页号(Page No)对数据页进行划分,分给某一个节点的GBS服务处理。LBS服务与LLS/LTV一样,部署在每一个节点,LBS服务根据用户请求,向GBS发起Latch封锁,或者根据GBS请求,回收本地的Latch封锁。

为了避免两个、或多个节点同时修改同一个数据页,导致数据损坏,或者数据页修改过程中,别的节点读取到无效内容,DMDSC集群中数据页的封锁流程产生一定变化,与单节点相比,增加了全局Latch封锁、释放两个步骤。并且,在获取全局Latch授权后,仍然需要进行正常的本地Latch封锁,避免节点内访问冲突。

4.4 缓存交换

根据目前的硬件发展状况来看,网络的传输速度比磁盘的读、写速度更快,因此,DMDSC集群引入了缓存交换(Buffer Swap)技术,节点间的数据页尽可能通过网络传递,避免通过磁盘的写入、再读出方式在节点间传递数据,从而减少数据库的IO等待时间,提升系统的响应速度。

缓存交换的实现基础是GBS/LBS服务,在GBS/LBS中维护了Buffer数据页的相关信息。包括:1. 闩的封锁权限(LATCH);2. 哪些站点访问过此数据页(Access MAP);3. 最新数据保存在哪一个节点(Fresh EP)中;4. 以及最新数据页的LSN值(Fresh LSN)等信息。这些信息作为LBS封锁、GBS授权和GBS权限回收请求的附加信息进行传递,因此并不会带来额外的通讯开销。

下面,以两节点DMDSC集群(EP0/EP1)访问数据页P1为例子。初始页P1位于共享存储上,P1的GBS控制结构位于节点EP1上。初始页P1还没有被任何一个节点访问过,初始页P1的LSN为10000。通过几种常见场景分析,逐步深入,解析缓存交换的原理。

  • 场景1

节点EP0访问数据页P1。

  1. 节点EP0的本地LBS向EP1的GBS请求数据页P1的S LATCH权限
  2. 节点EP1的GBS修改P1控制结构,记录访问节点EP0的封锁模式为S LATCH(数据分布节点为EP0),并响应EP0的LBS请求
  3. 节点EP0的LBS获得GBS授权后,记录获得的授权模式是S_LATCH,P1数据不在其他节点的Buffer中,发起本地IO请求,从磁盘读取数据。IO完成后,修改LBS控制结构,记录数据页上的LSN信息

本地IO

图4.2 本地IO
  • 场景2

节点EP1访问数据页P1。

  1. 节点EP1本地LBS向EP1的GBS请求数据页P1的S LATCH权限
  2. 节点EP1的GBS修改控制结构,记录访问节点EP1的封锁模式为S LATCH(数据分布节点为EP0/EP1),并响应EP1的LBS请求
  3. 节点EP1的LBS获得GBS授权后,记录获得的授权模式是S LATCH,根据数据分布情况,EP1向EP0发起P1的读请求,通过内部网络从EP0获取数据,而不是重新从磁盘读取P1数据

远程IO

图4.3 远程IO
  • 场景3

节点EP0修改数据页P1。

  1. 节点EP0本地LBS向EP1的GBS请求数据页P1的X LATCH权限(附加LSN信息)
  2. 节点EP1的GBS修改控制结构的LSN值,从EP1的LBS回收P1的权限
  3. 修改访问节点EP0的封锁模式为S + X LATCH,并响应EP0的LBS请求
  4. 节点EP0的LBS获得GBS授权后,记录获得的授权模式是S + X LATCH
  5. 节点EP0修改数据页P1,LSN修改为11000

这个过程中,只有全局Latch请求,数据页并没有在节点间传递。

GBS管理

图4.4 GBS管理

修改之后,数据页P1的LSN修改为11000。如下所示:

数据修改

图4.5 数据修改
  • 场景4

节点EP1修改数据页P1。

1.节点EP1本地LBS向EP1的GBS请求数据页P1的X LATCH权限

2.节点EP1的GBS发现P1被EP0以S + X方式封锁,向EP0发起回收P1权限的请求

3.节点EP0释放P1的全局LATCH,响应GBS,并且在响应消息中附加了最新的PAGE LSN值

4.节点EP1的GBS收到EP0的响应后,修改GBS控制结构,记录最新数据保存在EP0,最新的LSN值信息,记录EP1获得的授权模式是S + X LATCH(此时,数据分布节点仍然是EP0/EP1),并授权EP1的LBS

5.节点EP1的LBS收到授权信息后,记录获得的授权模式是S + X LATCH,并根据数据分布情况,向节点EP0发起数据页P1的读请求

6.节点EP1修改数据页P1,LSN修改为12000

GBS管理

图4.6 GBS管理

修改之后,数据页P1的LSN修改为12000。如下所示:

数据修改

图4.7 数据修改

这个过程中,数据页P1的最新数据从EP0传递到了EP1,但并没有产生磁盘IO。

4.5 重做日志管理

Redo日志包含了所有物理数据页的修改内容,Insert/delete/update等DML操作、Create Table等DDL操作,最终都会转化为对物理数据页的修改,这些修改都会反映到Redo日志中。一般说来一条SQL语句,在系统内部会转化为多个相互独立的物理事务来完成,物理事务提交时产生Redo日志,并最终写入联机Redo日志文件中。

一个物理事务包含一个或者多个Redo记录(Redo Record,简称RREC),每条Redo记录都对应一个修改物理数据页的动作。根据记录内容的不同,RREC可以分为两类:物理RREC和逻辑RREC。物理RREC记录的是数据页的变化情况,内容包括:操作类型、修改数据页地址、页内偏移、数据页上的修改内容,如果是变长类型的Redo记录,在RREC记录头之后还会有一个两字节的长度信息。逻辑RREC记录的是一些数据库逻辑操作步骤,主要包括:事务启动、事务提交、事务回滚、字典封锁、事务封锁、B树封锁、字典淘汰等,一般只在配置为Primary模式时才产生逻辑RREC。

PTX和RREC结构图

图4.8 PTX和RREC结构图

DMDSC集群中,各个节点拥有独立的日志文件,Redo日志的LSN值也是顺序递增的,Redo日志只会写入当前数据库实例的联机日志文件,与集群系统中的其他数据库实例没有关系。考虑到所有节点都可以修改数据,同一个数据页可能由不同节点先后修改,为了体现修改的先后顺序,确保故障恢复时能够按照操作的顺序将数据正确恢复。DMDSC集群要求对同一个数据页的修改,产生的LSN值是全局递增的,各个节点对同一数据页的修改在日志系统中是严格有序的。但是,针对不同数据页的修改并不要求LSN是全局递增的,也就是说只有多个节点修改相同数据页时,才会产生全局LSN同步问题。并且LSN全局同步,是在缓存交换时附带完成的,并不会增加系统的额外开销。

与单节点系统相比,DMDSC的日志系统存在以下差异:

1.本地Redo日志系统中,LSN值保证是递增的,后提交物理事务的LSN值一定更大;但顺序提交的两个物理事务产生的LSN值,不能保证一定是连续的

2.全局Redo日志系统中,LSN值不再严格保证唯一性。不同节点可能存在LSN值相等的重做日志记录

3.故障重启时,控制节点需要重做所有节点的Redo日志,重做过程中会根据LSN排序,从小到大依次重做

4.联机Redo日志文件需要保存在共享存储中

4.6 回滚记录管理

DMDSC集群的多版本并发控制(MVCC)实现策略是,通过回滚记录获取数据的历史版本,通过活动事务视图判断事务可见性、确定获取指定版本数据。因此,回滚记录也必须进行全局维护,有可能在节点间进行传递。与单节点一样,DMDSC集群中只有一个回滚表空间,回滚记录保存在回滚页中,回滚页与保存用户记录的数据页一样,由Buffer系统管理,并通过缓存交换机制实现全局数据共享。

为了减少并发冲突,提高系统性能,DMDSC集群中为每个节点分配了一个单独的回滚段(Segment),虽然这些回滚段位于同一个回滚表空间中,但是各个节点的回滚页申请、释放,并不会产生全局冲突。

与重做日志一样,DMDSC集群故障重启时,控制节点会扫描所有节点的回滚段,收集未提交事务进行回滚,收集已提交事务进行Purge操作。

微信扫码
分享文档
扫一扫
联系客服