目前的数据处理系统包含有数据库管理程序。借助于这些程序能够方便地存取各个数据库表格,每个表由多个记录组成。关系数据库程序能够存取若干个不同的数据库表,其中一个数据库表的元素包含在另一数据库表中。关系数据库程序允许用户利用几个数据库表所共有的某一个特定的字段或元素对这些不同的数据库表中的数据进行搜索、存取和更改。例如,一个数据库表可能含有雇员名和雇员序号,另一个数据库表可能有雇员名和雇员薪水,第三个数据库表可包含雇员名和雇员住地。关系数据库程序可允许用户存取雇员名、雇员序号、雇员薪水以及雇员住址并对这些表格作些改变,而不必一一存取每个数据库表。
这些数据库管理程序的一个重要方面是能够对单个数据库中的诸记录提供快速而有效的访问能力。
最近的数据处理系统能够支持多用户,使每个用户能够同时并发地存取数据。
数据库管理程序通常使用一种索引文件来迅速而有效地存取数据库表中的记录。这些索引文件通常是按B型树结构构成的。谈论B型树的参考资料是“Efficient Locking For Concurrent
Operation on B-Tree(B型树上的并发操作的有效互锁)”,这篇文章是由Lehman和Yao两人于1981年12月发表在ACM Transaction on Database Systen杂志第六卷第4期650页至670页上的。讲述B型树结构的其它参考资料有“The Ubiquitous B-Tree”(到处可见的B型树),由Comer发表在Computing Surveys第11卷第二期,1979年6月,121-137页;再一篇文章是“Concurrent Operation on B-Tree With Over Taking”(利用追补法对B型树的并发操作),由Sagiv发表在ACM杂志关于数据库系统原理的SIGACT-SIGMOD研讨会的会议录中,1985年3月,28-37页。
作为B型树结构构造出来的索引文件包含有一个根节点和许多由根节点分义出来的节点级。包含在这些节点中的信息是指向下一级节点或指向数据库中记录的指针,这些指针又进一步包括可用于访问数据库中键记录的被称为键记录信息的内容,记录的各个键按顺序放置在所有节点上。例如,对于雇员名,可有一个按字为顺序的索引树。其中根节点包含引用键数据,这些引用键可供下一级节点直接或间接地用来访问有关的记录。引用键含有索引字段,也就是雇员名字为拼写的信息。因此,根节点中的有序的键将逐一指向下一级的名节点。换句话说,根节点下的第一个节点可以间接或直接地访问以A、B、C开头的所有雇员名,与这第一个节点并排的下一节点可包含雇员名是以D-M字为开头的雇员记录,这一级上的最后一个节点可包含雇员名是N-Z开始的记录。当搜索整个索引文件树时,最终总可以达
到底部节点,其内容是指向存贮器中单个记录的记录键。
对数据库表实施并发存取的问题之一出在多个事务处理企图同时存取同一个记录的时候。特别是当一个用户希望改变一个记录而另一用户又试图使用这一记录,那么就出现竞争状态。对此问题的一种解法办法是对B型树中各索引所对应的记录或其一部分实施互斥访问或互锁,以保证在一用户正在访问某记录时不准改变此索引节点或记录。在IBM技术创新公告(IBM Technical Disclosure Bulletin)第25卷第7B号,1982年12月,3725-3729页上有一篇题为“Index Locking amd Splitting”(索引的互锁与分离)以及在这同一杂志的第19卷第10期,1977年3月,3887-3889页上有一篇题为“B型树上的并发操作的互锁协议”都讲述了互锁问题。
互锁办法的缺点是当一个用户访问时用一把锁禁止了其它用户的访问。
本发明的目标是提供一种更为有效的通过索引树存取数据库中记录的方法,同时还保障了对此数据库记录的更加有效的并发存取。
索引节点中的信息在被几个用户访问过程中可能被修改。随着有记录被插入数据库表中或从该表中删去,索引节点中将含有被删去或插入的键记录信息。在记录插入操作期间,因为一个节点的存贮量有限,所以一个节点中的信息可能被移到另一节点。这种插入事务处理将试图把一个节点中的键记录信息移到另一节点来完成插入任务。在并发的事务处理访问期间上述这种信息移动会由于其索引信息被改变而造成并发事务处理访问中的错误条件,本发明的目标是在改变索引
信息期间使几个事务处理能够并发地访问该索引而不产生错误条件。
遵照本发明给出了在一组记录键中根据通过一索引树的一个键记录或其一部分取出该键记录数据的一种方法。该索引树为不同的事务处理提供并发存取记录键的能力,索引树有一根节点和至少一级与根节点连接的子节点,每个子节点有一键记录引用(即指针-译注),指向相邻的下一级中的一个或多个节点,每个节点还有能直接存取键数据的底部节点。此方法包括七个步骤:(1)利用所述的键记录部分从根节点开始横穿诸节点直到底部某个节点;(2)对于其它的并发的事务处理,限制其对正在被穿越的节点和之前被访问过的节点除了读以外的所有访问;(3)在所说的底部节点中标识出所述的键记录;(4)对于所说的键记录限制对它们进行读以外的所有访问;(5)撤消对穿越的各个节点的所有访问限制;(6)取出键记录数据(7)在这个记录数据被取出后撤消对这个键记录的访问限制。
此外还提出了插入和删除记录键的方法以及在并发存取期间允许同时改变索引树结构的方法。
在附加的权利要求书中叙述了确信是属于本发明之特征的新的特性。但是结合附图参照下文的详细说明,将会很好地理解本发明本身及权利要求中未曾提到的特性和优点。本文的附图有:
本发明涉及的是数据库程序对记录的存取问题。下面的例子大刀阔斧地简化了数据库的存取问题,其目的是为了使读者更容易理解包含在例子中的本发明。读者应当明白,包含在数据库表中的信息项目和内容实际上比所说明的要复杂得多。
图1所说明的表(10)包括雇员姓名和编号。每个雇员记录包含一个单一的雇员号。表(10)中的姓名按字母顺序排列。但是内行人懂得,在计算机内存中,诸记录的存贮方式并不是这样的。
图2说明雇员的姓名/编号记录在计算机存贮器(12)中的存贮情况。每个记录(14,16,18,20,24和26)均有若干部分组成。在记录14中,先后包括地址部分(28)、雇员名(30)和雇员号(32)。地址部分(28)是该雇员名和雇员号(30,32)在计算机内存中的实际位置。图2所示的表12说明姓名的存贮并不是按字母顺序的,甚至不是相邻的。
数据库程序通常为用户提供一种能使他快速地存取存贮器中指定记录的索引树。这种索引树的例子如图3所示。为了简化表示,该索引树只给出两级。索引40包含有一个根节点和与它连接的三个节点50,52和54。根节点42称为“索引标识”,它详细说明了该索引中的信息类型。在这个例子中,其索引是由雇员姓名按字母递升
顺序构成的索引。每个雇员姓名包括一个键。本例中的键是雇员姓名的第一个字母,这些键被用于标识下一级的节点,以便在搜索一个特定的雇员姓名记录时使用。在根结点42中,各个键由字母“D”(44),“F”(46)和“空”48组成。D这个键(44)指引用户从根结点42(父节点)到节点50(子节点)。节点50含有可供直接存取雇员Andrews、Baker和Chester的键记录信息。但是在根节点中只用了一个键。这个键涉及到最高层的信息(或最高键的记录),这个最高层的信息目前在节点50中或许不存在或者已经存在。类似地,字母“F”(46)指导用户转到节点52,在这个节点中包含有雇员名Edgar和Edward。最后“空”(48)这个键使用户转到节点54。“空”这个键表示节点54是最后一个相邻的叶子节点。内行的人应当理解,索引40也可以包含多级节点。但为简单起见,在此只给出两级索引40。
图4中56、58和60三列分别表示节点50、52和54的内容在计算机内存中可能出现的形式。列56表示包含在节点50中的信息在计算机内存中如何存贮,列56包括一组指针62和数据存贮区64,指针66指向含有雇员姓名Andrew和雇员号的信息。类似地,指针68指向雇员名Baker及有关信息。因此,当一个事务处理存取一个节点时,它首先考察这个节点信息的指针部分以确定这一键记录信息存在何处。这一指针的信息可用来直接存取包含所需信息的存贮单元。在包含有中间级的多级索引结构中,这一节点存贮单元将包含指向按键记录信息排列的下一个相邻位置的节点的地址的指针。这些指针将按照它们有关的键信息排序。但是一个连续的节
点,例如节点50,可包含有几个雇员名,所以只能用父节点中的单个键来引用它。所引用的键将是该节点中所允许的最高键的记录信息。在含有中间节点的索引树中,前头(或父)节点中的键将是本节点中最高记录的键,这个键被本节点中所有的键所引用。
图5说明通过索引来存取记录信息的流程图。应当理解,在数据库管理程序中,由图5到8所示的软件程序是在访问事务处理期间被调用的。根据所举的例子,该事务处理试图取出一个记录来确定其雇员编号,或者试图执行一些其它操作,诸如记录的插入和删除操作。
图4和3还画出了从一个树叶节点指向下一个相邻树叶节点的指针51和53。72和74这些指针将指导访问型的事务处理去存取下一个相邻位置的树叶节点。在这个例子中,点60中Null这一项76表示节点60是最后一个节点。
在图5中,访问程序从步骤100开始并继续到步骤102,在这里,索引的根节点被S型锁锁住并开始存取。这个索引标识符节点标明,该索引的类型并为访问本例中的一个记录提供初始方向。索引标识节点将按雇员姓名的字母递升顺序来标识该索引。在103步,按照父节点中的信息标识出要被存取的这个子节点。在104步,确定要被执行的那个操作是否是取数操作。如果不是取数操作,换句话说,如果是插入记录(或者是插入一个键记录)或删除记录(或删除键记录)操作,该程序便前进到106步,以确定父节点底下的节点是不是叶子节点或底部节点。如是,该程序进到110步,并且在这个子节点上获得一个X型锁。X型锁是排他锁,它阻止对此结点作任
何其它访问。换言之,使用了X锁的那个事务处理阻止所有其它事务处理来访问这个节点。
现在回到104步,如果本操作是一个取数操作或者在106步得知这个子节点不是叶子节点,那么该程序前进到108步,在这个子节点上获得一把S型锁。S型锁允许其它并发性的用户作些有限制的访问。所谓有限制的访问指的是只允许其它用户读这个节点,而不准许作诸如删除或修改等等其它操作。现在程序前进到112步,在这一步,该程序开始存取这个子节点并确定正在被存取的那个记录的键是否高于这个子节点中的键,如是,该程序就进到114步来判别这一树形的索引结构是否被锁住。如果未被锁住,该程序就进到118步,其中的操作是父节点和这个子节点上的锁均被解开而整棵树被锁住,接着该程序从118步返回到102步,以便在允许对这棵树上锁的情况下重新开始该操作。在重新试作上述操作时可以作些优化,以便减少要被存取的节点数。
在本例中,对整棵树的X型锁用来告诉所有别的访问操作:树形结构正在改变。如果在试图对这个树上锁时,正在做一棵树的上X型锁的存取,敲凑庵制笸急匦氲鹊较瓤嫉哪歉龇梦式崾笤俳小?
对一棵树的S型锁告诉其它所有访问者:本树不在作结构改变,允许其它访问者并发地使用这个索引树。但是在这个S型锁松开之前不能作别的改变操作。不论是否有S型或X型锁横切一棵树的操作总可发生,横切树的操作可包括键记录的删除和插入。
在114步,如果这个树上着锁,或者在112步当这个键不大于该子节点中的最高键,那么该程序进到116步来确定该子节点是
否是叶子,若不是,下一步就是115步来松开父节点上的锁之后回到103步。但是,这个子节点如果是叶子,程序就进到120步,以便松开父节点的锁,然后再到122、124和126步以便确定该操作是取、插入还是删除操作。在这个例子中,如果上述三种操作哪个都不是,那么本程序就在130步返回到用户。实际上这种返回将包括一个出错指示,它表明这个访问处理程序不能识别所要执行的操作。
如果要执行的操作是取数操作,那么该程序进入到如图6所示的200步。在这一步中,该程序找出正在被搜索的所需要的键或者起码是相邻的那个最高键。在202那一步,该程序接着请求对此键记录有条件地上锁,在本例中,有条件的上锁是请求对各记录键实施上锁管理的程序来执行的。所谓“有条件”指的是:如果这个锁不能立即被答应,那么将对提出请求的访问者给出一个回答以指出这样的一种锁不能被答应。在204步中使用这一回答。如果这个锁至今还不被答应,该程序就转到206步来松开这个子节点的锁,然后再转到208步,对此键记录请求无条件上锁。在208这一步,访问者一直等到这样一种锁被许可后才往下进行。一旦上锁条件满足后,该程序通过转接符213转到图5中所示的102步,以便重新开始搜索。让我们回到204步,如果已经允许上锁,该程序便进到209步以便送回这个键记录的数据。在210步,子节点的锁被松开。在211步松开键记录的锁。内行的人将十分清楚,211这一步将在这个事务处理完成时或者在略早一些时候发生。
图7用来说明插入操作。内行人知道,必要时,在插入操作开始
前,对要被插入的键记录需要上X型锁。这一操作将把一个记录插入到计算机内存中并在索引中增加一个插入键,以便更新该索引中的有关节点,从而使其它事务处理能够使用新插入的记录。在300这一步它首先判断这个键能否和底部或叶子节点相配。如是,该程序转到304步去找高于要被插入的那个键的下一相邻的键。如果下一个键不在本节点中,再去访问下一个相邻位置的叶子节点。如果不存在下一相邻位置的叶子节点。本次访问将会得到一个空指示符,从而在308步,该程序针对下一键记录请求一个有条件的X型锁。接着在312步确定这个锁是否允许。如被允许,在318步把这个新的键插入到第一个底部节点,这就是在300步到达的节点。在324步,下一个记录和键记录的锁被松开。此外,在329步把所有锁着的锁都松开,接着在325步上程序返回。内行人显然会明白,在事务处理结束后,对于被插入的那个键记录的锁将被放开。现在回到312步,如果那把锁不答应,该程序就进行到316步去松开那个子节点上的锁,然后再转到322步请求对这下一个键记录上无条件的X型锁。然后该程序通过转接符328回到102步(图5)。
让我们回到300步,如果这个键与该叶子节点不相配,该程序就转到302步去请求对整棵树上有条件的X型锁。在306步确定这个有条件的锁是否被许可。若不,该程序进入310步去松开这个子节点的锁,然后到314步,请求一个对于整个树的无条件的X型锁。在允许对整棵树上无条件的X型锁之后或者在306步允许上锁,该程序就继续到320步执行节点分裂算法。此算法给出一个新节点,这个节点含有从前面的节点分离出来的部分信息。节点分裂算法
还可能给出外加的中间节点,这个外加的节点将可能被更新。父节点也被修改,使它包含最高键记录和指向新节点的指针。图9中说明了一种节点分裂算法。而且在Addison-Wesley出版公司于1983年出版的名为“数据结构和算法”这本书的170-179页中讲述了节点分裂算法,该书的作者是Aho、Hopcroft和Ullman。节点分裂算法完成后,该程序转到326步去解开整棵树的锁。然后该程序经过转接符327回到102步(图5)。
图8举例说明了删除操作。内行人很清楚,要被删除的那个键记录将被挂上X型锁,这当然是在必要时在作删除操作之前做的。在400步,该程序寻找下一个相邻的高于要被删除的键的键。在402步,该程序请求对这下一个键记录挂有条件的X型锁,然后转404步确定这个锁是否被允许。若不允许,这个程序就转到406步去解下相应子节点的锁,接着在410步请求对这下一键记录挂无条件的锁。然后该程序经由转接符431回到102步(图5)。
让我们回到404步,如果这把锁被许可,那么在408步,这个键就被删除。然后程序转到412步去确定这下一个节点是否是空。若不空,该程序在421步解下这个节点的锁,在413步解开这下一键记录的锁之后在414步返回。但是,如若该节点为空,该程序就进入416步请求对树作有条件的X型锁操作。然后在418步确定这个锁是否被允许,若不,则在420步去解开这个子节点的锁。然后在400步请求对树上无条件的X型锁,一旦这个锁被允许,来自422或418的程序前进到424步去执行节点拆除算法,这个算法将从索引树中移去这个空节点和从上一节点指向这个空
节点的引用。图10是这一拆除算法的图解说明。在426步,这棵树的锁被解开。在429步,被删除的键记录和下一键记录的锁被解开。内行的人应当会明白,429步在该事务处理结束时发生。
图9举例说明了一种节点分裂算法。在这个算法中,加到索引树中的新节点用来存贮新插入的键记录。而且索引树中的所有在新节点之前的节点或父节点均可能被修改以反映出这个新加的节点和其中包含的信息。在500步,得到一个新节点。在502对这个新节点挂上X型锁。在504步,把被分裂的那个节点中的一部分键移到新结点。显然,在被分裂的节点中应当有一个指针指向这个新节点。此外,如果需要,在新节点中也应该有一个指针指明对这个新节点而言的下一个相邻位置上的节点。在506步,被分裂的节点和新节点都被解锁。在507步,相对于被分裂节点的父节点被挂上X型锁。在508步,这个父节点被更新。在10步,解开这个父节点的锁。内行人应当明白,父节点的更新实际上可能涉及这个父节点的分裂和这棵索引树上的若干个相继位置上的父节点的更新。
图10举例说明了一种节点拆除算法。这个节点拆除算法的用途是从索引树中移去一个空节点,以便贮备存贮空间并提高对键的存取效率。在600步,使邻接这个空节点一个节点挂上X型锁。在602步,这个邻接节点的键被移到空节点。在604步,更新接受节点(即原来的空节点-译注)中的指针和其中的键,以便用新加载的键对这些结点定位。在606步,它的下一个节点,即当前的空节点被拆除。在608节,解除这个子节点上的各个锁,解除刚被移走键的那个节点的锁以及那个相邻节点(目前已经被拆除)上的锁。然后在
610步对各个父节点挂上X型锁。在612步,修改各个父节点,目的是拆除指向被拆除节点的指针和调整与从被拆除的节点中得到键的那个节点相关的键。在614步,解除各个父节点上的锁。正如在图9中所讲过的一样,各个父节点的更新可能会引起一种递归处理,这对内行的人来说是不难理解的。