法律状态公告日
法律状态信息
法律状态
2022-06-21
专利权有效期届满 IPC(主分类):G06F12/08 专利号:ZL028116011 申请日:20020604 授权公告日:20080206
专利权的终止
2008-02-06
授权
授权
2007-09-05
专利申请权、专利权的转移专利申请权的转移 变更前: 变更后: 登记生效日:20070803 申请日:20020604
专利申请权、专利权的转移专利申请权的转移
2004-10-27
实质审查的生效
实质审查的生效
2004-08-18
公开
公开
技术领域
本发明一般涉及数据库管理系统。更具体地讲,本发明涉及用于数据库系统中驻留存储器索引结构的考虑了高速缓存(cache-conscious)的并行控制方案。
背景技术
由于服务器DRAM(动态随机存储器)组件价格的不断降低,主存储器数据库管理系统(MM DBMS)在许多应用中就成为了驻留磁盘数据库管理系统(DR DBMS)的一种经济可行的替代。不仅在读事务方面而且在更新事务方面,MM DBMS在性能上比DRDBMS潜在地高出几个数量级。
然而,MM DBMS相对于DR DBMS显著的性能优势的取得并不是自动实现的,而是需要MM DBMS专用的优化技术,尤其是高速缓存的有效利用。高速缓存,一种存取时间比主存储器快得多的特殊的存储设备,储存着频繁引用的数据项,从而提高总的存储存取性能。在主存储器索引和查询处理的设计中,MM DBMS中的高速缓存的效果是一个重要的考虑因素。
目前,存在所谓的考虑高速缓存的索引结构,例如CSS-tree(高速缓存灵敏搜索树)以及CSB+-tree(高速缓存灵敏B+树),它们是作为能减少高速缓存丢失而因此提高搜索效率而提出的替代索引结构。CSS-tree是在一个数组中一层一层地设置索引节点,通过计算数组偏移实现索引遍历(index traversal)。CSB+-tree每个节点仅保留B+-tree的一个子节点指针,它可以将树的扇出(fanout)几乎增加一倍,但这是以增加更新成本为代价的。
此外,还有其它索引结构,例如CR-tree。认识到对于MBR关键字比指针大得多的R-tree而言,删除指针不是很有效,CR-tree对MBR关键字进行了有效地压缩,以便在每个节点装入更多的条目。通过将局部-关键字信息储存在索引中而不管非无效关键字的大小,pkT-tree和pkB-tree有效地减小了缓存丢失。
虽然所有这些考虑高速缓存的索引结构采用了一些有效增加索引扇出、减小所谓的冷(cold)高速缓存丢失和容量高速缓存丢失的方法,但它们的索引结构设计并未考虑到并行控制因素。并行控制需要协调多用户或多处理器的多处理环境中同时执行的事务。对于运行涉及索引更新的现实的主存储器数据库应用,以及利用现有多处理器平台提高此类应用的性能,并行控制至关重要。
处理这一问题的一种方法是使用传统的、驻留磁盘系统中用到的并行控制方案,例如锁耦合(lock coupling)。在索引遍历期间,锁耦合锁存索引节点。通过在申请子节点上的锁存的同时,持有父节点上的一个锁存,从而进行从一个节点到其子节点的索引遍历。如果子节点不是目标节点,则所申请的锁存为共享模式。如果子节点是目标节点,则根据要对目标节点执行的操作,所申请的锁存可以是共享或独占模式。当事务希望从节点读取数据时,则发布共享锁。当事务希望在节点上写入数据项时,则发布独占锁。只要子节点闭锁,父节点的锁存就被释放。然而,锁存涉及存储器写入,在多处理环境中,容易发生相干高速缓存丢失。
有一种称为物理版本的技术,它允许无锁存的索引遍历。其思想是:创建一个用于更新的索引节点的新型版本,这样读取现有索引节点的事务就不会干扰那些更新索引节点的事务。这就可以实现无锁存的索引遍历,实现读取事务的高级并行。将更新过的版本加入索引需要获得树级或树和将要更新的节点的锁存。这种物理版本技术存在的主要问题在于需要很高的创建版本成本。随着更新率的提高,索引性能会急剧下降。对于多处理单元的数量而言,更新性能的可扩展性很差。
因此,需要一种高效的索引结构以及并行控制方案,能在多处理环境中优化数据库管理系统,消除高速缓存无效的高昂代价。
发明内容
本发明的一个目的在于提供一种高效的索引结构,用于在主存储器中具有数据库的数据库管理系统(DBMS)。
本发明的另一目的在于提供一种高效的索引结构,用于带有高速缓存的DBMS中,该高速缓存用于存储频繁存取的索引节点以提高存储器存取性能。
本发明的再一目的在于提供一种并行控制方案,用于多处理环境中具有高速缓存的MM DBMS中。
本发明的又一目的在于提供一种并行控制方案,以保证索引读取器不会干扰并行的索引更新器。
利用本发明的不用锁存任何节点可有效遍历索引的开放式、无锁存索引遍历(“OLFIT”)并行控制方案,可以实现上述目的及其它目的。在每个节点中,该OLFIT方案保留一个锁存、一个版本号、以及一个指向同一级的下个节点的链接。
索引遍历涉及一致节点更新操作以及从根节点开始的无锁存节点读取操作。为了保证无锁存条件下节点读操作的一致性,每次节点更新首先获得该节点的锁存,并在更新节点内容后将版本号加1。节点的读操作从将版本号读入寄存器开始,并以校验当前版本号是否与寄存器存储的版本一致结束。如果它们相同,则读操作是一致的。否则,重新进行节点读取操作,直至校验成功。本发明的并行控制方案可应用于B+-trees、CSB+-trees、及其变体。
附图说明
图1为一个涉及多处理器的多处理环境中的高速缓存无效的实例图。
图2为使用开放式、无锁存索引遍历(OLFIT)的本发明的并行控制方案的框图,其中数据库和索引结构驻留在主存储器中。
图3是本发明的另一实施例的框图,其中数据库和索引结构驻留在磁盘中而不是主存储器中。
图4是本发明的第三实施例的框图,其中多个线程独立地存取索引结构。
图5是根据本发明的B+-tree索引节点的结构图。
图6为节点分裂期间没有高键和链指针的节点分裂操作的实例图。
图7为节点分裂期间带有一个高键和一个链指针以保证正确的索引遍历的节点分裂操作的实例图。
图8A和8B为用于OLFIT的CSB+-Tree节点结构的实例图。
图9A和9B分别为B+-tree和完全CSB+-tree中OLFIT的八线程性能随节点大小变化的曲线。
图10A和10B分别为B+-tree和完全CSB+-tree中各种并行控制方案的搜索性能曲线。
图11A和11B分别为在B+-tree和完全CSB+-tree中各种并行控制方案的插入与删除的性能曲线。
图12A和12B分别为在B+-tree和完全CSB+-tree中各种并行控制方案的单线程性能随着更新率变化的曲线。
图13A和13B分别是在B+-tree和完全CSB+-tree中各种并行控制方案的八线程性能随更新率变化的曲线。
具体实施方式
I并行控制
相干高速缓存丢失
图1显示了在具有传统数据库管理系统(DBMS)的查询处理系统中,相干高速缓存丢失是如何发生的。DBMS是一个管理数据库结构以及控制数据库存取的程序集合。在主存储器100中,有一个包括节点n1到n7(101至107)的索引树,用于访问磁盘或主存储器中的数据库。为了简单起见,假设每个节点对应一个高速缓存块,且包含一个锁存。如同上文所述,锁存可以保证一次事务唯一地存取一个数据项。有四个处理器(108到111)通过高速缓存(112-115)访问主存储器100。
让我们考虑这样一种情况:在主存储器查询处理系统冷启动时,处理器p1 108遍历路径(n1 101,n2 102,n4 104),这样这些节点就被复制在p1108的高速缓存器c1 112中。在这过程中,保持和释放n1和n2上的锁存。现在,p2 109遍历路径(n1 101,n2 102,n5 105),然后这些节点被复制到c2 113,这样锁存这些节点的p2 109就使高速缓存在c1 112中的n1和n2无效。可以注意到:即使在高速缓存c1 112中有足够的空间,也会发生高速缓存无效的情况。如果p3 110遍历路径(n1 101,n3 103,n6 106),c2 113中的n1就变得无效。最后,如果p4 111遍历路径(n1 101,n3 103,n7 107),c3 114中的n1和n3就变得无效。
本发明的OLFIT方案
图2示出了根据本发明实现开放的无锁存索引遍历(OLFIT)方案的整个系统,它利用了数据库事务的“开放(optimistic)”特点,这样大部分数据库事务都不会发生冲突。
主存储器201储存数据库203和索引结构202(通常为树型结构),从而有效管理该数据库。为处理单元#1 204(或处理单元#2205)设置的高速缓存#1 206(或高速缓存#2 207),用于储存频繁存取的索引节点208(或209),以提高总的存储器存取时间性能。为了协调处理单元或线程,以维持处理的一致性而不会有太频繁的高速缓存项目无效,设置了并行控制单元210(优选在软件中实现)。具体地,并行控制单元210提供了索引节点的无锁存遍历所需的控制,这种控制基于开放式索引并行控制。
图3显示了本发明的另一实施例,其中数据库225和索引树224驻留在磁盘223中,而不是图1的主存储器201中。主存储器221储存着索引节点的子集(“缓存的索引节点”)222。为处理单元#1 226设置的高速缓存#1 228,用于储存频繁存取的索引节点(“高速缓存索引节点”)230。相似地,为处理单元#2 227设置的高速缓存#2 229,用于储存高速缓存索引节点231。并行控制单元232(优选在软件中执行)用于在多处理环境中控制高速缓存#1 228和高速缓存#2229。
图4示出了本发明的另一可选实施例,其中多处理通过能够对数据库执行独立存取的几个处理线程实现。一个线程就是程序中的控制的一个单个顺序流(sequential flow)。线程#1 245和线程#2 246运行在处理器244中,它们独立地访问主存储器241中的数据库243,主存储器241也存储引用数据库项目的索引树242。设置了高速缓存248,用于储存频繁存取的索引,具有用于线程#1和线程#2的每个线程检索节点的专用高速缓存条目。
OLFIT的节点操作
图5显示了本发明的OLFIT方案所使用的B+-tree索引节点的结构。节点内容信息和并行控制信息储存在每个索引节点中。节点内容包括用于存取数据库的键值和指向其它节点的指针305。并行控制信息包括控制并行存取节点的锁存301和代表节点内容更新状态的版本号302。节点内容还包括指定树索引中节点级别的级别303、指示节点中项数的大小304、高键306,以及链指针307。高键306和链指针307的使用将在下面解释。
OLFIT方案基于字的基本读取与基本写入操作,每个操作均为单一的不可再分的工作单元。目前产生的体系结构均支持字的基本读取与基本写入。在读取和更新节点的描述原语(primitive)中,假定以下操作都能够由基本操作实现:读取版本号、更新版本号、更新锁存、以及校验锁存是否已锁定。
Update Node(更新节点)原语
U1.以独占模式取得锁存。
U2.更新节点的内容。
U3.将版本号加1。
U4.释放锁存。
Update Node原语是“一致的”,它保证更新将某一节点从一个一致的状态改变到另一个一致的状态。为了做到这一点,节点的更新操作利用节点上的1-bit锁存将其隔离和串行化。独占模式中的锁存保证了当发生更新时不会有其它事务存取该节点。在更新节点内容(例如级别、大小、键&指针、高键,以及链指针)前,当在该节点上没有独占锁时,更新器将取得该节点上的锁存用于独占方式存取。锁存在更新后释放。在释放锁存前,更新器也要将版本号加1。与节点读取不同,节点更新总能成功而无需再试。步骤U3(即,将版本号加1)是为了使读取器能够读取无锁存的所述节点,并校验它们读取的节点是否处于一致的状态。
Read Node(读取节点)原语
R1.将版本号复制到寄存器X中。
R2.读取节点的内容。
R3.如果锁存被锁定该节点上,转到R1(在某一选择延迟后)。
R4.如果当前版本号与X中复制的值不同,转到R1(在某一选择延迟后)。
Read Node原语是“一致的”,在这种意义下,步骤R3和R4保证了读取器以一致的状态只读取不持有任何锁存的节点。只有在R2中读取的数据处于一致状态的情况下,读取器才能够通过R3和R4,否则读取器要从R1重新开始。在从R1重新开始前,Read Node原语可选择等待一段时间,直至解决了与并行的更新节点原语的冲突。Read Node原语所读取信息的一致性可以表述为一个定理,证明如下。
定理:与Update Node原语一起,Read Node原语保证在R2中读取的节点的内容不能处于不一致的状态。换言之,如果在R2中读取的节点的内容处于不一致的状态,R3中的条件或R4中的条件变为真,则读取器无法全部通过R3和R4。
证明:R3校验是否有任何并行的更新。如果R3中的条件为假而读取器能够通过R3,由于U1是持有锁存而U4是释放锁存,那么,或者任何并行更新的U4在R3之前一定已经完成;或者在R3之后一定开始了任何并行更新的U1。R4校验在R1与R4之间是否存在版本号的任何更新。如果R4中的条件为假而读取器能够通过R4,由于U3更新了版本号,所以任何并行更新的U3必须在R1之前或R4之后发生。在下述公式中,A→B表示A在B之前执行。根据Read Node和Update Node原语的定义,保持有两条语句R1→R2→R3→R4和U1→U2→U3→U4。均通过R3和R4的情况(当R3和R4中的条件都为假时)可表示及推导如下:
((U4→R3)∨(R3→U1))∧((U3→R1)∨(R4→U3))
=((U4→R3)∧(U3→R1))∨((R3→U1)∧(U3→R1))∨((U4→R3)∧(R4→U3)
∨((R3→U1)∧(R4→U3))
=((U4→R3)∧(U3→R1))∨FALSE∨FALSE∨((R3→U1)∧(R4→U3)
(U3→R1)∨(R3→U1)
因此,如果R3和R4中的条件均为假,要么任何并行更新的U3必须在R1之前,要么任何并行更新的U1必须在R3之后。由于更新U2中所述节点内容在U1与U3之间,且读取R2中节点内容在R1和R3之间,所以在R2中读取的某一节点内容不能处于非一致状态。Q.E.D.
注意:R3(校验锁存的步骤)和R4(校验当前版本号的步骤)可以在一条单个指令中执行。
为了保持坚固性(robustness,也可称之为鲁棒性),可以调整ReadNode原语以限制重试的次数。限定重试次数的ReadNode被描述为RobustReadNode。RobustReadNode原语使用一条ConversativeReadNode原语,当重试次数超过某一限制时,该原语会在读取节点前取得一个共享模式的锁存。共享模式锁存允许并行地进行多个读取事务,并且只要并行事务为只读,则它就不会产生冲突。在RobustReadNode原语中,RETRY_LIMIT为用户定义的重试次数的上界,而重试次数为一个局部变量(用于计算重试的次数)。为了简单起见,在RobustReadNode中,ReadNode的R3和R4由“或”操作连接。
ConservativeReadNode原语:
PR1.以共享模式取得一个锁存。
PR2.读取节点的内容。
PR3.释放锁存。
RobustReadNode原语:
RR1.将重试次数初始化为0。
RR2.将版本号复制到寄存器X中。
RR3.读取节点的内容。
RR4.如果锁存锁定于独占模式或者当前版本号与X中复制的版本号不同,
RR4-A.如果重试次数小于或等于RETRY_LIMIT,则重试次数加1并转到RR2。
RR4-B.否则,执行ConservativeReadNode。
与无冲突多数情况的ReadNode相比,RobustReadNode在RR1中只多使用了一条指令。
以下示出了Read Node和Update Node原语的执行。为了简便起见,只显示了ReadNode而不是RobustReadNode的实现。因而,本领域技术人员将意识到,可以容易地将ReadNode的实现修改为RobustReadNode的实现。procedure latch(word)
begin1.do{2.t:=turn-off-LSB(word);3.}while(compare-and-swap(word,t,turn-on-LSB(t))≠t);endprocedure unlatch(word)word:=word+1;endprocedure update_node(node)begin1.latch(node.ccinfo);2.//Update the contents of node3.unlatch(node.ccinfo);endprocedure read_node(node)begin1.do{2.t:=turn-off-LSB(node.ccinfo);3.//Read the contents of node4.}while(t≠node.ccinfo)end
如果使用目前这一代计算机体系结构普遍支持的“检验与设置”和“比较与交换”操作等基本的读取与写入操作中的一种已经执行锁存,则Read Node和Update Node原语的实现能够直接取自该操作的描述。
然而,在一个或两个高速缓存块数量级下,由于考虑到节点的大小,每个节点的锁存和版本号占用两个字成本太高,所以将这两个字合并到一个称作ccinfo的单字中。ccinfo的最低有效位(″LSB″)用于锁存,字中的其它比特用于版本号。这种合并能够进一步优化,而仅使用一个条件跳转用于校验ReadNode原语中的R3和R4的状态。
操作compare-and-swap(A,B,C)是比较A与B的值,如果相同,则用C的值取代A的一项基本操作。该操作返回A的初始值。Turn-on-LSB(字)与turn-off-LSB(字)分别为打开和关闭一个字的LSB的比特操作。
update_node过程可实现UpdateNode原语。过程latch的步骤2复制LSB关闭的ccinfo的值。过程latch的步骤3通过比较ccinfo的值和步骤2中复制的t值以校验ccinfo的LSB是否打开,并通过用LSB打开的t替代ccinfo的值,以自动地打开ccinfo的LSB。过程unlatch用于释放锁存,并通过增加的ccinfo而将其版本号加1。由于ccinfo的LSB在过程latch中被打开,所述过程unlatch中的加1关闭了锁存并同时将版本号加1。
Read_node过程可实现ReadNode原语。read_node的步骤4同时校验了ReadNode的R3与R4中的条件,因为如果t等于ccinfo,则当前ccinfo的LSB一定已被关闭,而从步骤2起其它比特一定已被更改。
OLFIT的树操作
在节点进行并行分裂与删除的同时,不用锁存,OLFIT可以保证根据搜索键,树遍历能到达正确的叶节点。无锁存遍历减少了相干高速缓存丢失,并且由于搜索、插入、以及删除操作包括树遍历,从而提高了这些树操作的性能。例如,包含搜索键的搜索操作从最左端叶节点开始遍历,然后向右遍历叶节点。插入操作开始遍历找到将包括新条目的叶节点,然后将条目插入该节点。删除操作开始遍历以找到包括要删除的条目的叶节点,然后从所述节点删除该条目。
节点分裂的处理
由于当向下遍历树索引时,OLFIT方案不使用锁耦合,并行更新器可以在遍历到达该子节点之前,分裂遍历的目标子节点。此外,由于读取节点时不持有锁存,并行更新器也可以分裂正在被读取的节点。
图6A和6B显示了使用传统的索引结构,节点分裂之前和之后的节点分裂实例。这些图显示了带有搜索键25的读取器正在从n2 402向下遍历的情形,而插入n10的并行更新器通过n6 406分裂为(n6 416和n10 420)的分裂繁殖,可以将图6A中的n2 402分裂为图6B中的(n2 412和n11 421)。如果所述遍历在分裂后通过n2,它就会选路到n5 415(而不是n6 416),而最后会到达一个错误的叶节点。
图7A和7B分别为利用本发明的索引结构的节点分裂前和分裂后的节点分裂实例,为了解决上述节点分裂出现的问题,本发明的索引结构中加入了一个指示节点中键值上界的高键和指向同一级别该节点的右兄弟(right sibling)节点的链指针。所有分裂均从左向右进行,并且每个节点都加入了一个高键和一个链指针。节点的高键表示该节点中键值的上界,而链指针为指向该节点同一级别上的右节点的指针。链指针提供了到达一个节点的另外一条路径,而高键确定了是否跟随该链指针。使用所述链指针,由于分裂从左向右进行,且每一节点都有自己的高键和指向右节点的链指针,所以从一个节点开始的所有节点分裂都可以从该节点到达,并且出现节点并行分裂时,也可以到达正确的子节点。
图7B显示了即使并行更新器将图7A中的n2 442分裂为(n2和n11),在该分裂后,通过n2 452的带有搜索键25的遍历,将由链指针选路到n11 461,然后被正确地选路至n6 456。
树遍历过程
以下示出了找出与搜索键对应的叶节点的树遍历过程。
procedure traverse(root_node,search_key)begin1.node:=root_node;2.while(node is not a leaf node){3.t:=turn-off-LSB(node.ccinfo);4.next_node:=find_next_node(node,seach_key);5.if(node.ccinfo=t)node:=next_node;6.}7.return node;end
在遍历过程中,find_next_node(node,search_key)为找出要遍历的下一节点的操作。如果search_key比所述节点的高键大,则此操作返回链指针;否则,将指针转向要向下遍历的适合子节点。
read_node过程被嵌入到了所述遍历过程。只有当next_node的值从一致状态中的某一节点计算得到,next_node的值才被分配到可变节点。
节点删除处理
由于并行更新器能够分裂正在被读取的节点,所以如果节点为空的话,更新器也能够删除正在被读取的节点。为处理这一情况,当某一节点为空时,更新器只删除指向该节点的链接,并将该节点寄存到无用节点收集器中。直到该节点确实被释放,空节点中的链指针才被删除。当没有能够读取该节点的索引操作时,无用节点收集器才真正释放所寄存的节点。为了判断是否有任何能读取该节点的索引操作,要使用一个带有物理版本的相同的无用节点收集,但所用开销大不相同,这是因为物理版本使用每次更新的无用节点收集器,但它只有在更新器从树索引删除一个空节点时才被用到。
使用附在每一线程上的全局时间戳以及专用时间戳。注意:假设线程执行事务。全局时间戳初始化为0,而专用时间戳初始化为∞。当开始索引操作时,每一线程均将全局时间戳的值复制到其无锁存的专用时间戳中;而当结构索引操作时,则将其专用时间戳重置为∞。在线程删除一个节点后,线程便将带有所述全局时间戳当前值的节点寄存到无用节点收集器中,并将持有与该全局时间戳相关的锁存的全局时间戳加1。无用节点收集器周期性地扫描所有线程的专用时间戳,并实际释放所寄存的时间戳小于专用时间戳当前最小值的节点。由于在删除节点无法到达而专用时间戳被设置为开始遍历前的全局时间戳的值之后,带有全局时间戳的值的删除节点才被寄存,所以任何索引操作均无法到达带有的时间戳小于专用时间戳当前最小值的寄存节点。
虽然已经说过Read Node和Update Node原语用于保证节点存取之间的一致性,也可以用一个使用这些原语的变量,来保证索引存取之间一致性。此类变量的实例如下,具体为,Update Index原语和Read Index原语。
Update Index(更新索引)原语
U1.取得一个独占模式锁存。
U2.更新索引。
U3.将版本号加1。
U4.释放锁存。
Read Index(读取索引)原语
R1.将版本号复制到寄存器X中。
R2.读取索引。
R3.如果锁存被锁定,转到R1(在选择延迟后)。
R4.如果当前版本号与X中复制的值不同,转到R1(在选择延迟后)。
事务周期锁定
本发明可以与记录上的处理周期锁定一起支持事务的可串行性。使用Find_First,Find_GE,Find_GT,Scan_Next,Insert,and Delete原语可以描述这种结合的一个实例。假定在索引中索引的项目以降序从左向右排序。Find_First为查找索引中最左端项目并初始化迭代器的操作。Find_GE为查找键值大于或等于索引中搜索键值并初始化迭代器的操作。Find_GT与Find GE相似,除了Find_GT查找键值大于搜索键值的项目。Scan_next为查找由Find_First,Find_GE,Find_GT,或Scan_Next找到的项目的下一项目。Insert(插入)是将一个项目添加入索引的操作,而Delete(删除)为从索引删除一个项目的操作。
以下为Find_GE的过程。
procedure find_GE(root_node,search_key)begin1.node:=traverse(root_node,search_key)2.while(node is not null){3.t:=turn-off-LSB(node.ccinfo);4.pos:=find_position_GE(node,seach_key);<!-- SIPO <DP n="16"> --><dp n="d16"/>5.if(pos>node.size){6.next_node:=node.link_ptr;7.if(t=node.ccinfo)node:=next_node;8.}9else{10.record:=get_record(node,pos);11.lock(record,SHARED_MODE);12.if(t=node.ccinfo)13. return iterator(search_key,node,pos,ccinfo,record);14.else unlock(record);15.}16.}17.return iterator(search_key,NULL,0,0,NULL);end
在Find_GE过程中,Find_position_GE(node,search_key)为返回其键值大于或等于节点中search_key的最左边记录的位置的操作。该操作返回一个比节点尺寸大的值。Get_record(node,position)是一项将指针返回到处在节点位置的记录的操作。Lock(record,mode)为一种以指定模式持有记录处理周期锁的操作,unlock(record)为释放记录处理周期锁的操作。
read_node过程被嵌入到Find_GE过程,用于向右遍历以及锁定记录。
虽然所述实例针对Find_GE,对于本领域有经验的人士来说,其它原语Find_First,Find_GT,Scan_next,Insert和Delete也可以以相同方式实现。
II.对CSB+-TREE的修改
图8A和8B显示了为OLFIT而对CSB+-Tree进行调整的节点和节点组。图8A显示了无叶节点500的节点结构。对于CSB+tree,具有同一父节点的节点都集中在称作节点组508的相邻空间内,节点组508包括仅储存指向子节点组的指针(而不是所以指向子节点的指针)的CSB+-tree的非叶节点500。由于这一原因,如果右兄弟节点的父节点与所述节点的父节点相同,则没有指向兄弟节点指针,也能够查找出所述节点的右节点。由于同一节点组中的右节点没有指针也能够定位,所以用于OLFIT的CSB+-tree节点仅储存高键507,而无任何链指针。如图8A所示,每个节点组仅储存一个指向右节点组的链指针,以定位其它节点组中的右兄弟节点。由于链指针512不属于所述节点组中的任何节点,所以链指针512具有其本身的锁存510以及版本号511。
类似地,图8B显示了叶节点的节点结构。此处,CSB+-tree代表完全的CSB+-tree,CSB+-tree的一种变体。对于本领域技术人员而言,很明显可以扩展到CSB+-tree的其它变体。
CSB+-tree的分裂操作与B+-tree的分裂操作不同。由于具有同一父节点的节点集中在一个节点组中,当某一节点分裂时,如果节点组不满的话,节点组中的所有右兄弟节点均向右移位为该分裂腾出空间。如果节点组满了,节点组则分成两个节点组。当一个节点分裂时没有产生节点组的分裂,则要分裂的节点、所有移位的右兄弟节点以及父节点都要在分裂前锁存起来。当节点组由于组内节点的分裂而被分裂,则要分裂的节点、移动到新节点组中的节点、指向右节点组的链指针以及父节点都要在分裂前锁存起来。
III.试验结果
通过实现本发明,证明了本发明具有优越的可扩展性。对B+-tree和完全CSB+-tree,OLFIT方案与四种其它的索引并行控制方案的性能做了比较:(1)带有节点锁存的锁耦合(″LC″);(2)带有树锁存的树级锁定(″TL″);(3)带有节点锁存的物理版本(″VN″);以及(4)带有树锁存的物理版本(″VT″)。对于100%搜索和单一线程实验,也测量了无任何并行控制(″NO″)的索引操作的性能。对于物理版本方案VN和VT,用于T-tree的版本方案被调整以用于B+-tree和完全CSB+-tree。
实验是在运行Solaris 2.7操作系统的带有八个CPU(UltraSparcII 400MHz)的Sun Enterprise 5500服务器上进行的。每个CPU具有一个高速缓存行大小为64字节的8MB L2高速缓存。在下述四种工作载荷条件下,运行每一种并行控制方案:(1)100%搜索操作;(2)100%插入操作;(3)50%插入和50%删除操作的混合操作;以及(4)具有变化更新率的搜索、插入和删除操作所组成的混合操作。
为了与要求用于更新操作的实际内存分配的物理版本方案的公平对比,使用了存储池以减小调用内存分配的系统开销。按照系统中处理器的数量创建了同样数量的存储池,并且给每个线程分配一个存储池以将内存分配争夺降到最小。
对于每个实验,使用了允许复制键值的非唯一索引,其中索引通过插入10兆均匀分布的4字节整数键和相关指针进行初始化。因为实验在32位寻址模式中运行,所以每个指针的大小为4字节。初始化后,B+-tree的高度为7,而完全CSB+-tree的高度为6。B+-tree的大小约为140MB,而完全CSB+-tree的大小约190MB。由于B+-tree和完全CSB+-tree利用插入操作进行初始化,所以它们充满程度约为70%。由于当索引通过插入操作建立时,128字节的节点具有最佳性能,所以索引节点的大小选择了128字节。
图9A和9B显示了随节点大小的变化,使用OLFIT方案的性能曲线。如图所示,128字节的节点大小具有最佳性能。当索引由插入操作建立时,B+-tree和完全CSB+-tree的充满程度约为70%。对于70%满的节点,仅存取128字节节点的前64字节块的概率很高。
表1为采用每个并行控制方案时,节点扇出的最大数量。TL和VT具有最大的扇出数量,这是因为该方案不需要节点的并行控制信息。LC和VN具有较小的扇出数量,这是因为方案在每个节点上都需要一个锁存。OL具有最小的扇出数量,这是因为该方案在每个节点上需要锁存、版本号、高键、以及链指针。
[表1]
单纯搜索性能
图10A和10B分别显示了将各种并行控制方案应用于B+-tree和完全CSB+-tree的单纯搜索性能。通过改变执行搜索操作的线程数来测量精确匹配搜索操作的吞吐量。x-轴代表执行搜索操作的线程数量,而y-轴代表集合吞吐量。
OLFIT(OL)方案和物理版本方案(VN,VT)的性能与无任何并行控制(NO)的情况相似。物理版本和无并行控制之间的间隙代表了为无用节点收集器设置专用时间戳的成本。当开始遍历时,OL、VN、和VT将全局时间戳的值复制到专用时间戳;而当完成遍历时,将专用时间戳的值重置为∞。
图10B中使用CSB+-Tree的OL和NO之间的间隙要比图10A中使用B+-tree的间隙宽,这是因为校验高键的成本不同。采用B+-tree,由于遍历右兄弟节点和向下遍历一个子节点是相同的,所以高键不需对非叶节点进行特殊处理。而使用CSB+-trees时,遍历右兄弟节点和向下遍历是不同的。子节点的位置是由指向子节点组的指针计算得出的,而右兄弟节点的位置是从当前节点的位置计算得出的。对高键的这种特殊处理需要稍稍多花费一点时间,而使得与物理版本方案有一段很小的间隙。
由于对树锁存的争用,以及由持有树锁存和释放树锁存还会多生产出两个一致锁存丢失,树级锁(TL)会随着线程数目的增大而恶化。锁耦合(LC)的性能最差。因为要锁存多个节点,锁耦合会生成许多一致高速缓存丢失,与无并行控制之间的间隙随线程数量而成接近线性地变宽。
单纯更新性能
图11A和11B分别显示了将各种并行控制方案应用于B+-tree和完全CSB+-tree的单纯更新性能。更新操作的吞吐量通过改变执行更新操作的线程数目来测量。x-轴代表执行更新操作的线程数量,而y-轴代表集合吞吐量。其中,一半操作为插入,另一半为删除。
图11A和11B表明只有OLFIT方案(OL)显示出了可扩展性能。由于版本的代价很高,特别是对于每次更新都必须变换整个节点组版本的CSB+-tree,物理版本方案(VT,VN)表现出较差的更新性能。性能结果表明:VN的更新性能略优于VT,这是因为最初为T-tree提出的VN方案在将使其适合于B+-tree的过程中作了改动。如果发生结构改动,VN持有一个树锁存。由于B+-tree的分裂不同于T-tree的旋转且结构修改不需要集中的树锁存,所以消除了VN对集中树锁存的需要。随着线程数量的增加,集中树锁存大大降低了更新性能。
虽然带有节点锁存(VN)和锁耦合(LC)的物理版本没有产生优良的性能,它们的性能会随线程的增加而缓慢增加。但是,由于存在集中树锁存的争用,随着线程数目的增加,带有树锁存(VT)和树级锁(TL)的物理版本的性能会下降。
不同更新率的性能
图12A和12B显示了不同更新率的条件下,利用各种并行控制方案,使用单线程的性能结果曲线。x轴代表执行更新操作的线程数量,而y轴代表集合吞吐量。
在不使用并行控制(NO)的情况下,OLFIT(OL)和树级锁方案(TL)的性能相似。由于锁闭的开销,锁耦合(LC)显示的性能要比OL和TL差。当更新率接近0时,两个物理版本方案(VN,VT)显示出了较好的性能,但随着更新率的增长,由于物理版本的高开销,它们的吞吐量都会急剧下降。如图10B所示,对于CSB+-tree,其同一父节点的各节点集中在一个连续空间中,这样版本创建在节点组的基础上,所以其版本开销甚至更大。
图13A和13B显示了在多处理环境中使用8个线程的性能结果。当更新率接近0时,物理版本方案(VN,VT)的性能与OLFIT方案(OL)的性能相当。而随着更新率的增加,OL的性能显著优于其它并行控制方案。
使用8个线程时,OL与其它方案的性能差距要比使用单线程时更大。差距变大的部分原因在于其它方案产生出大量相关高速缓存丢失,部分原因在于在TL和VT的情况中,集中的树锁存发生争用。注意:VT的性能略优于TL的性能,但随着更新率的进一步增加,其性能开始与TL的性能接近,并最终由于版本的高昂代价而与TL的性能相交。
随着更新率的增加,由于CSB+-tree具有更高的分裂代价,所以CSB+-tree的性能比B+-tree的性能下降得更剧烈。在CSB+-tree中,当节点分裂时,如果包括所述节点的节点组不满,则所述节点在同一节点组中的所有右兄弟节点均向右移动,而如果节点组满时,所述组中的一半节点被移到一个新节点组中。注意:当更新率超过20%,由于版本的高昂代价,VN和VT的性能甚至比TL更差。
总之,在8个CPU的多处理系统上,OLFIT几乎具有线性的可扩展性,其中读取的可扩展性与无并行控制以及通过索引节点的物理版本也提供无锁存遍历的现有技术的主存索引并发控制的性能相当。
本发明能够适用于其它的B+-tree变体,例如B-tree,B*-tree,以及pkB-tree。虽然显示的优选实施例是针对B+-tree的变体的,但本领域技术人员将理解:本发明的OLFIT方案可以很容易地进行调整而适用于其它索引结构,例如T-tree、一个节点中具有许多元素的平衡二叉树、多维索引的R-tree、CR-tree、R-tree的缓存意识版本,和GiST(广义搜索树),以及允许用户对任何类型数据的索引进行开发并允许用户将其定制以表示它们本身索引结构的可扩展数据结构。
本发明是参照优选实施例进行描述的,但这些实施例并不会限制本发明。本领域中的普通技术人员将理解,所述实施例的结构与形式可以进行许多修改,而不偏离本发明的精神和范围。
机译: 高速缓存存储器的高速缓存一致性控制器,用于在并行执行线程时保持数据的反依赖性
机译: 用于在可扩展的并行处理体系结构中执行高速缓存等待时间诊断的系统和方法,包括计算CPU空闲时间和计算高速缓存未命中次数
机译: 用于在可扩展的并行处理体系结构中执行高速缓存等待时间诊断的系统和方法,包括计算CPU空闲时间和计算高速缓存未命中次数