1、事务管理
多版本并发控制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),在事务启动、收集活动事务信息时通知全局事务视图,并获取响应的信息。
2、封锁管理
数据库管理系统一般采用行锁进行并发访问控制,避免多个用户同时修改相同数据;通过表锁、字典锁控制DDL和DML操作的并发访问,保证对象定义的有效性和数据访问的正确性。DM采用了独特的封锁机制,使用TID锁和对象锁进行并发访问控制,有效减少封锁冲突、提升系统并发性能。
1、TID锁
TID锁以事务号为封锁对象,每个事务启动时,自动以独占X方式对当前事务号进行封锁,由于事务号是全局唯一的,因此这把TID锁不存在冲突,总是可以封锁成功。同时,在事务管理中,介绍了物理记录上包含一个TID字段,记录了修改数据的事务号。执行INSERT\DELETE\UPDATE操作修改物理记录时,设置事务号到TID字段的动作,就相当于隐式地对物理记录上了一把X方式的TID锁。因此,通过事务启动时创建的TID锁,以及写入物理记录的TID值,DM中所有修改物理记录的操作都不在需要额外的行锁,避免了大量行锁对系统资源的消耗,有效减少封锁冲突。特别是在DMDSC集群中,需要进行全局封锁,封锁的代价比单节点更高,通过TID锁可以有效减少行锁引发的性能损失。
2、对象锁
对象锁则通过对象ID进行封锁,将对数据字典的封锁和表锁合并为对象锁,已达到减少封锁冲突、提升系统并发性能目的。
与事务管理类似,DMDSC集群将封锁管理拆为全局封锁服务(Global Locking Services,简称GLS)和本地封锁服务(Local Locking Services,简称LLS)两部分。整个系统中,只有控制节点拥有一个GLS。控制节点的GLS统一处理集群中所有节点的封锁请求,维护全局封锁信息、进行死锁检测,确保事务并发访问的正确性。每个节点都有 一个LLS。各个节点的LLS负责与GLS协调、通讯,完成事务的封锁请求,DMDSC集群中所有封锁请求都需要通过LLS向GLS发起,并在获得GLS授权后,才能进行后续操作。
3、闩管理
闩(Latch)是数据库管理系统的一种内部数据结构,通常用来协调、管理Buffer缓冲区、字典缓存和数据库文件等资源的并发访问。与锁在事务生命周期中一直保存不同,闩通常指保持极短的一段时间,比如修改Buffer中数据库内容后,马上会释放。闩的封锁类型也比较简单,就是共享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、缓存交换
根据目前的硬件发展状况来看,网络的传输速度比磁盘的读写速度更快,因此,DMDSC集群引入了缓存交换(Buffer Swap)技术,节点间的数据页尽可能通过网络传递,避免通过磁盘写入、在读出方式在节点间传递数据,从而减少数据库的IO等待时间,提升系统的响应速度。
缓存交换的实现基础是GBS/LBS服务,在GBS/LBS中维护了Buffer数据页的相关信息。包括:
1、闩的封锁权限(Latch);
2、哪些站点访问过此数据页(Access MAP);
3、最近数据保存在哪一个节点(Fresh EP)中;
以及最近数据页的LSN值等信息;
这些信息作为LBS封锁,GBS授权和GBS权限回收请求的附加信息进行传递,因此并不会带来额外的通信开销。
5、举例
下面,以两个节点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信息。
场景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以X+S 方式封锁,向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.