DMDSC实现原理
DMDSC是一个共享存储的数据库集群系统。多个数据库实例同时访问、修改同一个数据库,因此必然带来了全局并发问题。DMDSC集群基于单节点数据库管理系统之上,改造了Buffer缓冲区、事务系统、封锁系统和日志系统等,来适应共享存储集群节点间的全局并发访问控制要求。同时,引入缓存交换技术,提升数据在节点间的传递效率。
一、事务管理
多版本并发控制(MVCC)可以确保数据库的读操作与写操作不会相互阻塞,大幅度提升数据库的并发度以及使用体验,大多数主流商用数据库管理系统都实现了MVCC。DM的多版本并发控制实现策略是:数据页中只保留物理记录的最新版本数据,通过回滚记录维护数据的历史版本,通过活动事务视图(V$DSC_TRX_VIEW)判断事务可见性,确定获取哪一个版本的数据。
每一条物理记录中包含了两个字段:TID和RPTR。TID保存修改记录的事务号,RPTR保存回滚段中上一个版本回滚记录的物理地址。插入、删除和更新物理记录时,RPTR指向操作生成的回滚记录的物理地址。
回滚记录与物理记录一样,也包含了TID和RPTR这两个字段。TID保存产生回滚记录时物理记录上的TID值(也就是上一个版本的事务号),RPTR保存回滚段中上一个版本回滚记录的物理地址。
每一条记录(物理记录或回滚记录)代表一个版本。如下图所示。
各版本之间的关系
找到对当前事务可见的特定版本数据,进行可见性判断,是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),在事务启动、收集活动事务信息时通知全局事务视图,并获取相应的信息。
DMDSC中,与事务相关的视图如下所示:
序号 |
视图名 |
视图说明 |
1 |
V$DSC_GTV_TINFO |
显示TINFO控制结构的信息。 |
2 |
V$DSC_GTV_ACTIVE_TRX |
显示全局活动事务信息。 |
3 |
V$DSC_LOCK |
显示全局活动的事务锁信息。 |
4 |
V$DSC_TRX |
显示所有活动事务的信息。通过该视图可以查看所有系统中所有的事务以及相关信息,如锁信息等。 |
5 |
V$DSC_TRXWAIT |
显示事务等待信息。 |
6 |
V$DSC_TRX_VIEW |
显示当前事务可见的所有活动事务视图信息。 |
二、封锁管理
数据库管理系统一般采用行锁进行并发访问控制,避免多个用户同时修改相同数据;通过表锁、字典锁控制DDL和DML操作的并发访问,保证对象定义的有效性和数据访问的正确性。DM则采用了独特的封锁机制,使用TID锁和对象锁进行并发访问控制,有效减少封锁冲突、提升系统并发性能。
TID锁以事务号为封锁对象,每个事务启动时,自动以独占(X)方式对当前事务号进行封锁,由于事务号是全局唯一的,因此这把TID锁不存在冲突,总是可以封锁成功。同时,在事务管理中,介绍了物理记录上包含一个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授权后,才能进行后续操作。
DMDSC中,与锁相关的视图如下所示:
序号 |
视图名 |
视图说明 |
1 |
V$DSC_LOCK |
显示全局活动的事务锁信息。 |
2 |
V$DSC_TRXWAIT |
显示事务等待信息。 |
三、闩管理
闩(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封锁,避免节点内访问冲突。
DMDSC中,与闩相关的视图如下所示:
序号 |
视图名 |
视图说明 |
1 |
V$DSC_GBS_POOL |
显示GBS控制结构的信息。 |
2 |
V$DSC_GBS_POOLS_DETAIL |
显示分片的GBS_POOL详细信息。 |
3 |
V$DSC_GBS_CTL |
显示GBS控制块信息。 |
4 |
V$DSC_GBS_CTL_DETAIL |
显示GBS控制块详细信息。 |
5 |
V$DSC_GBS_CTL_LRU_FIRST |
显示GBS控制块LRU链表首页信息。 |
6 |
V$DSC_GBS_CTL_LRU_FIRST_DETAIL |
显示GBS控制块LRU链表首页详细信息。 |
7 |
V$DSC_GBS_CTL_LRU_LA |
显示GBS控制块LRU链表尾页信息。 |
8 |
V$DSC_GBS_CTL_LRU_LAST_DETAIL |
显示GBS控制块LRU链表尾页详细信息。 |
9 |
V$DSC_GBS_REQUEST_CTL |
显示等待GBS控制块的请求信息。 |
10 |
V$DSC_LBS_POOL |
显示LBS控制结构的信息。 |
11 |
V$DSC_LBS_POOLS_DETAIL |
显示分片的LBS_POOL详细信息。 |
12 |
V$DSC_LBS_ |
显示LBS控制块信息。 |
13 |
V$DSC_LBS_CTL_LRU_FIRST |
显示LBS的LRU_FIRST控制块信息。 |
14 |
V$DSC_LBS_CTL_LRU_LAST |
显示LBS的LRU_LAST控制块信息。 |
15 |
V$DSC_LBS_CTL_DETAIL |
显示LBS控制块详细信息。 |
16 |
V$DSC_LBS_CTL_LRU_FIRST_DETAIL |
显示LBS的LRU_FIRST控制块详细信息。 |
17 |
V$DSC_LBS_CTL_LRU_LAST_DETAIL |
显示LBS的LRU_LAST控制块详细信息。 |
四、缓存交换
根据目前的硬件发展状况来看,网络的传输速度比磁盘的读、写速度更快,因此,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请求,从磁盘读取数据。
4. IO完成后,修改LBS控制结构,记录数据页上的LSN信息
场景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数据
场景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请求,数据页并没有在节点间传递。
修改之后,数据页P1的LSN修改为11000。如下图所示:
场景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值信息,记录EP0获得的授权模式是S + X LATCH(此时,数据分布节点仍然是EP0/EP1),并授权EP1的LBS
5.节点EP1的LBS收到授权信息后,记录获得的授权模式是S + X LATCH,并根据数据分布情况,向节点EP0发起数据页P1的读请求
6.节点EP1修改数据页P1,LSN修改为12000
修改之后,数据页P1的LSN修改为12000。如下图所示:
这个过程中,数据页P1的最新数据从EP0传递到了EP1,但并没有产生磁盘IO。
五、重做日志管理
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结构图
DMDSC集群中,各个节点拥有独立的日志文件,Redo日志的LSN值也是顺序递增的,Redo日志只会写入当前数据库实例的联机日志文件,与集群系统中的其他数据库实例没有关系。考虑到所有节点都可以修改数据,同一个数据页可能由不同节点先后修改,为了体现修改的先后顺序,确保故障恢复时能够按照操作的顺序将数据正确恢复。DMDSC集群要求对同一个数据页的修改,产生的LSN值是全局递增的,各个节点对同一数据页的修改在日志系统中是严格有序的。但是,针对不同数据页的修改并不要求LSN是全局递增的,也就是说只有多个节点修改相同数据页时,才会产生全局LSN同步问题。并且LSN全局同步,是在缓存交换时附带完成的,并不会增加系统的额外开销。
与单节点系统相比,DMDSC的日志系统存在以下差异:
1.本地Redo日志系统中,LSN值保证是递增的,后提交物理事务的LSN值一定更大;但顺序提交的两个物理事务产生的LSN值,不能保证一定是连续的
2.全局Redo日志系统中,LSN值不再严格保证唯一性。不同节点可能存在LSN值相等的重做日志记录
3.故障重启时,控制节点需要重做所有节点的Redo日志,重做过程中会根据LSN排序,从小到大依次重做
4.联机Redo日志文件需要保存在共享存储中
DMDSC中,与重做日志相关的视图如下所示:
序号 |
视图名 |
视图说明 |
1 |
V$RLOG |
显示日志的总体信息。通过该视图可以了解系统当前日志事务号LSN的情况、归档日志情况、检查点的执行情况等。 |
2 |
V$RLOG_PKG |
显示日志包信息。通过该视图可以查询日志系统中当前日志包的使用情况,如包的长度、最大LSN、最小LSN等。通过该视图还可以查询当前实例日志系统中等待刷盘的链表上的日志包信息。 |
3 |
V$RLOG_PKG_STAT |
显示当前实例日志系统中日志包使用的统计信息。 |
4 |
V$RLOGFILE |
显示日志文件的具体信息。包括文件号、完整路径、文件的状态、文件大小等等。 |
5 |
V$ARCHIVED_LOG |
显示当前实例的所有归档日志文件信息。此动态视图与 |
6 |
V$LOGMNR_LOGS |
显示当前会话添加的需要分析的归档日志文件。 |
六、回滚记录管理
DMDSC集群的多版本并发控制(MVCC)实现策略是,通过回滚记录获取数据的历史版本,通过活动事务视图判断事务可见性、确定获取指定版本数据。因此,回滚记录也必须进行全局维护,有可能在节点间进行传递。与单节点一样,DMDSC集群中只有一个回滚表空间,回滚记录保存在回滚页中,回滚页与保存用户记录的数据页一样,由Buffer系统管理,并通过缓存交换机制实现全局数据共享。
为了减少并发冲突,提高系统性能,DMDSC集群中为每个节点分配了一个单独的回滚段(Segment),虽然这些回滚段位于同一个回滚表空间中,但是各个节点的回滚页申请、释放,并不会产生全局冲突。
与重做日志一样,DMDSC集群故障重启时,控制节点会扫描所有节点的回滚段,收集未提交事务进行回滚,收集已提交事务进行Purge操作。
DMDSC中,与回滚记录相关的参数如下如下所示:
序号 |
视图名 |
视图说明 |
1 |
UNDO_RETENTION |
动态系统级参数。事务提交后回滚页保持时间,单位为秒。有效值范围(0~ 86400)注:类型为DOUBLE,可支持毫秒。 |
2 |
ENABLE_FLASHBACK |
动态系统级参数。是否启用闪回查询,0:不启用;1:启用。 |
更多内容链接:https://edu.dameng.com,https://eco.dameng.com/