同时适应磁盘与固态硬盘读写特性的海量数据存储方法与流程

文档序号:12063290阅读:275来源:国知局
本发明属于海量数据存储领域,特别涉及存储树,该方法可同时适应磁盘与固态硬盘读写特性。
背景技术
::现有的硬盘上常用的索引树有B-tree、LSM-tree、buffer-tree等。其中B-tree是传统的经典树,但因为其在随机写的场景中不可避免的随机写磁盘,当存储海量数据时性能较低,所以存储海量数据时常常使用其变体,如BigTable中对B-tree的变体和LSM-tree的结合使用。对于海量数据的存储,常使用LSM-tree或buffer-tree(又称为fractal-tree)作为索引树,这两者的共同特点是将待写入的记录推迟写,待积累到一定量时再批量处理。这样可以较好的解决B-tree的随机写场景中引起的随机写磁盘的问题,使得写吞吐量得到了较大的提升。在随机读的场景中,因为LSM-tree和buffer-tree的层数较多且树中的块大小比B-tree的中的块大小大很多,所以读放大较大使得随机读性能有明显降低。为了解决这个问题,bigtable/leveldb等项目在实现LSM-tree时,在每个节点保存了布隆过滤器信息,这样可以很好的降低LSM-tree的读放大,较好的解决随机读性能低的问题。但不管是B-tree还是LSM-tree/buffer-tree,这些树的写放大都较大。由于磁盘吞吐量的限制,较大的写放大限制了这些索引树随机写性能的进一步实质性的提升,而且严重损害固态硬盘的寿命。较大的写放大侵占了大部分的磁盘的吞吐量进而使得在读写混合的场景中,随机写影响随机读对磁盘性能的利用而使得随机读性能也会有一定程度的下降。技术实现要素:本发明所要解决的问题是:传统树的较大的写放大使得随机写效率低下的问题,在固态硬盘盘中较大的写放大也严重的影响着固态硬盘的寿命。较大的写放大侵占了大部分的机械磁盘或固态硬盘的吞吐量进而使得在读写混合的场景中,随机写影响随机读对机械磁盘或固态硬盘性能的利用而使得随机读性能也会有一定程度的下降。由此设计了称为Log-StructuredAppend-Tree(日志结构的追加树,简称LSA-tree)的树。本发明提供一种同时适应磁盘与固态硬盘读写特性的海量数据存储方法,将一个块中的记录的完全排序改为部分部序,再在每个块的尾部加上布隆过滤器,实现方式如下,内存中包括可变内存缓存、不可变内存缓存和树的元数据信息,磁盘中的数据采用LSA-tree结构组织,设该树分为n层,第i层中至少ti个块最多ti+1个块,1≤i≤n-1,参数t为相邻两层块数阈值的倍数,最后一层小于等于tn个块;每个块有一个键的范围,当每个块存储的数据量达到相应阈值时,将块内的数据刷入下一层中在范围上有覆盖重叠关系的块中,将要刷的数据直接追加到相应的块内时,某一块的数据由若干个排序序列组成,而不是通过归并排序的方式实现块内完全排序;树中每一个块保存着布隆过滤器;而且,后台线程对LSA-tree树中的块的操作分为三类,包括下刷、分裂和合并;所有操作都只对非最后一层的块发起处理;将当前层的某一块与下层的一个或多个块在键上的覆盖重叠关系称为父子关系,当前层的该块称为父亲块,下一层的一个或多个块称为孩子块;下刷操作是将块内的数据下移至下一层中,但该块的范围仍保留,该块所在层的块的数目不发生变化;下刷操作的触发条件为,该块存储的数据量达到存储阈值且该块的孩子块数目小于2t;触发后,需要在下面两个执行条件均满足后进行下刷,条件1,下层的块的数目小于ti+1+1且i+1<n,或者小于tn且i+1=n;条件2,若下层为非最后一层,孩子块需都未达到存储阈值;分裂操作是将块拆分成两个,以使新生成的两个块的孩子块数目相等;分裂操作的触发条件为,该块存储的数据量达到存储阈值且该块的孩子块数目大于2t;该操作需满足的执行条件为,该块所在层的块的数目小于ti+1;合并操作是将块内的数据下移至下一层中,在下刷后该块的范围被删除,以使得该块所在层的块的数目减1;合并操作的触发条件为,该块所在层的块数目等于ti+1;该操作需要满足下面两个执行条件,条件1,下层的块的数目小于ti+1+1且i+1<n,或者小于tn且i+1=n;条件2,若下层为非最后一层,孩子块需都未达到存储阈值;而且,当用户线程插入记录时,有以下三种情况,1)若可变内存缓存未达到容量阈值,将记录追加进用户日志,再将记录插入可变内存缓存;2)若可变内存缓存达到容量阈值且不可变内存缓存不存在,先将其重命名为不可变内存缓存,再新建一个可变内存缓存插入记录;3)若可变内存缓存达到容量阈值且不可变内存缓存存在,等待后台线程将不可变内存缓存写入磁盘后销毁,用户线程再按照2)进行处理;而且,基于LSA-tree树,后台线程将不可变内存缓存写入磁盘包括以下步骤,步骤1.1,若最后一层的块的数目等于tn,则令n=n+1,并新建一层,新建的层为新的最后一层;步骤1.2,选取要处理的任务,每个任务包括选择要处理的块和该块上将要执行的操作,将不可变内存缓存也看作一种特殊的块;本选取操作设有三种优先级,从高到低依次如下,优先级1:不可变内存缓存的下刷操作,若不满足下刷的执行条件,则继续判断优先级2条件;优先级2:对于非最后一层,从上层开始判断是否存在块数等于ti+1且下层块数小于ti+1+1且i+1<n的层,或者是否存在下层块数小于tn且i+1=n的层;若存在,则选择该层中的某一块进行合并操作以减少该层的块的数目;然后在候选集合中选取最优的块,对选择出的最优块执行合并操作;若不存在这样的层,则继续判断优先级3条件;优先级3:从上层到下层依次判断是否存在存储的数据量达到存储阈值的块,若存在则选取遍历过程所遇到的第一个块;若该块的孩子块的数目小于2t,则将对该块执行下刷操作;若该块的孩子块数目大于等于2t,则将对该执行分裂操作;若选取的块将进行的是下刷操作,但因为该块存在已经到达存储阈值的孩子块而使得该操作的执行条件不满足,则改为选择该孩子块进行下刷或分裂操作,依次类推进行递归查找,直至最终选择到第一个满足下刷或分裂执行条件的块;若最终未选择到任何目标块和操作,则在用户继续插入数据时,从步骤1.1开始重新执行;步骤1.3,根据获得的任务执行实际的磁盘操作,包括下刷操作、合并操作或分裂操作;步骤1.4,申请一个排它锁,申请成功后,将执行的实际的磁盘操作所修改的树的结构信息写入树元数据变化日志,并根据此信息更新内存中的树的元信息;步骤1.5,若处理的是不可变内存缓存的下移操作,销毁不可变内存缓存;若有用户线程正睡眠,则唤醒用户线程;将本线程所获取的所有锁解锁,本线程从步骤1.1继续开始执行。而且,当用户需读取数据时,执行的步骤如下:步骤2.1,读可变内存缓存,若读取到所需要的记录即返回;步骤2.2,读不可变内存缓存,若读取到所需要的记录即返回;步骤2.3,依次读第1层到第n层,找到即返回,若到最后一层没找到,则说明数据库中不存在对应记录。而且,步骤1.3中,若任务为下刷操作,分为3种情况,情况1,若待下刷的块不存在孩子块,则直接进入步骤1.4修改该块的元信息以实现下移;当前层的被下移的块的范围保留;情况2,若待下刷的块存在孩子块且下一层为最后一层,对于落在最后一层中的某一块的范围内的记录,直接修改该块;对于落在最后一层所有块的范围之外的记录,选择距离与待插入记录的键的距离最小的孩子块进行修改,并修改孩子块的范围;修改最后一层孩子块的具体操作为,若该块存储的数据未达到阈值,则进行追加操作;若达到,则将待写入的数据与原有的数据进行归并排序生成若干个新的块;情况3,若待下刷的块存在孩子块且下一层为非最后一层,对于落在下一层某一块的范围内的记录,直接将数据追加到该块;对于落在所有块的范围之外的记录,选择与待插入记录的键的距离最小的孩子块进行追加,并改变孩子块的键范围;当前层的被下移的块的范围保留。而且,若任务为合并操作,和下刷操作采用同样方式将块内的数据下移至下一层中,下刷后该块的范围被删除,以使得该块所在层的块的数目减1。而且,块中存储的数据有索引数据、布隆过滤器和用户记录,索引数据和布隆过滤器存储在块的末尾,用户记录存储在块的前端。而且,当块中间的空闲空洞(hole,逻辑上空闲的地址空间,但是没有实际机械磁盘或固态硬盘的地址与其绑定)存储不下本次要写的所有数据但存储得下索引数据和布隆过滤器时,将索引数据和布隆过滤器存储在块的后端,将用户记录追加到块的尾部;而且,当块中间的空闲空洞存储不下本次要写的数据的索引数据和布隆过滤器时,将要写的数据和原有的数据归并排序,生成一个新的块;或者,通过将索引数据、布隆过滤器和用户记录都追加到块的尾部,替代进行归并排序。依照本发明,在不牺牲任何其他性能的情况下,使得写放大大大降低,大大增加了随机写效率。在读写混合的场景中,随机读性能也有所增强。对固态硬盘寿命起到了更好的保护和延长,具有重要的市场价值。附图说明图1为本发明实施例为本存储方法中使用的基本架构图,主要为LSA-tree的结构示意图。图2为本发明实施例在执行磁盘操作时,将块中数据下刷到最后一层的逻辑示意图。图3为本发明实施例在执行磁盘操作时,将块中数据下刷到非最后一层的逻辑示意图。图4为本发明实施例中所设计的块的磁盘布局的示意图。图5为本发明实施例中所设计的块的可选的磁盘布局的示意图。具体实施方法本发明要解决的核心问题是:传统树的较大的写放大使得写性能或读写混合的性能低下。在固态硬盘盘中较大的写放大也严重的影响着固态硬盘的寿命。本发明通过将一个块中的记录的完全排序改为部分部序,再在每个块的尾部加上布隆过滤器使得该方案对读性能的影响降到最低的方法以解决上述问题。图1是本发明实施例所提供存储方法的基本架构图,分为内存部分和磁盘部分。内存中包括可变内存缓存和不可变内存缓存各一个,以及树的元数据信息。树的元数据信息描述了树中每个块的元信息。块的元信息包括块的范围,所属的层,块中间的空闲空洞的大小,被追加的次数等。这些块的元信息通过所属的层分组,每组中块的元信息通过将元信息中保存的块的范围进行比较,而使得每组的元信息排好序。磁盘中的数据采用LSA-tree结构组织。内存中的块采用全排序结构,分为可变内存缓存和不可变内存缓存两种,前者是未达到块存储容量阈值的块,用户的记录可直接插入;后者大小达到阈值,只能被读取不能再被改变。当用户线程插入记录时,有三种情况:1)若可变内存缓存未达到容量阈值,将记录追加进用户日志,再将记录插入可变内存缓存,返回;2)若可变内存缓存达到容量阈值且不可变内存缓存不存在,先将其重命名为不可变内存缓存,再新建一个“可变内存缓存”插入记录,返回;3)若可变内存缓存达到容量阈值且不可变内存缓存存在,等待后台线程将不可变内存缓存写入磁盘后销毁(此过程在下文中将详细描述),用户线程再按照2)进行处理。磁盘中的数据采用LSA-tree的结构进行组织。该树分为n层,每一层由多个块组成,每层块的数量以指数级递增。第i(1≤i≤n-1)层的块数为ti或ti+1,最后一层(第n层)块的个数小于等于tn(t为大于等于2的正整数,例如10)。如图1中从高到低记为:L1层有t1个块,L2层有t2个块,…,Ln-1层有tn-1个块,Ln层有x个块(x大于0小于等于tn)。参数t为相邻两层块数阈值的倍数,具体实施时本领域技术人员可根据需要预设层数n、参数t、例如n=7,t=10。每个块有一个键的范围,当每个块存储的数据量达到相应阈值时,将块内的数据刷入下一层在键范围上有覆盖重叠关系的块中。大多数情况下,该过程将要刷的数据直接追加到相应的块(这样得到的块内数据是由若干个排序序列组成),而不是通过归并排序的方式实现,从而避免过大的写放大。当树中的块大小的阈值达到十兆级别,如64MB,即使拆分成若干份写到下一层的块中,写到每一块的平均数据量也达到数兆,可很好的利用的磁盘与固态硬盘的顺序写性能。树中每一个块保存着布隆过滤器,用户读取记录时不需要读取块中的每个序列,而只需要读取占少量空间的布隆过滤器来判断查询的记录是否在块中某个序列中,以使用户的读操作性能与全块排序相比几乎不受影响。后台线程对树中的块的操作分为三类:下刷、分裂和合并。所有操作都只对非最后一层的块发起处理,设为第i层Li(1≤i≤n-1)。为方便描述,将当前层的某一块与下层的一个或多个块在键上的覆盖重叠关系称为父子关系,当前层的该块称为父亲块,下一层的一个或多个块称为孩子块。下刷操作是将块内的数据下移至下一层中,但该块的范围仍保留,该块所在层的块的数目不发生变化。下刷操作的触发条件为:该块存储的数据量达到存储阈值且该块的孩子块数目小于2t。该操作需要满足下面两个执行条件才能进行:条件1,下层的块的数目小于ti+1+1(i+1<n,下一层为非最后一层)或tn(i+1=n,即下一层为最后一层Ln);条件2,若下层为非最后一层(i+1<n),孩子块需都未达到存储阈值。下刷操作详细操作步骤参见步骤1.3。分裂操作是将块拆分成两个,以使新生成的两个块的孩子块数目相等。分裂操作的触发条件为:该块存储的数据量达到存储阈值且该块的孩子块数目大于2t。该操作需满足的执行条件为:该块所在层的块的数目小于ti+1。详细操作步骤参见步骤1.3。合并操作与下刷操作类似,将块内的数据下移至下一层中,唯一的不同是在下刷后该块的范围被删除,以使得该块所在层的块的数目减1。合并操作的触发条件为:该块所在层的块数目等于ti+1。该操作需要满足下面两个执行条件才能进行:条件1,下层的块的数目小于ti+1+1(i+1<n,下一层为非最后一层)或tn(i+1=n,即下一层为最后一层Ln);条件2,若下层为非最后一层,孩子块需都未达到存储阈值。使操作不满足执行条件的层或块称为阻塞层或阻塞块,阻塞了该操作的进行。没有逻辑依赖关系的数据块上的操作可以并行。磁盘中的块和内存中可变内存缓存和不可变内存缓存都有一个排他锁与其一一绑定。当某种操作修改块时,需要对要修改的块依次加排他锁,以防止某一块被多个线程同时修改,造成数据错误。实施例中,不可变内存缓存写入LSA-Tree的具体流程(即后台线程执行的操作流程)如下:步骤1.1,若最后一层的块的数目等于tn,则令n=n+1,并新建一层,新建的层为新的最后一层。进入步骤1.2。步骤1.2,选取要处理的任务,每个任务包括选择要处理的块(这里将“不可变内存缓存”也看作一种特殊的块)和该块上将要执行的操作。本选取操作设有三种优先级(这三种优先级保证了树的每一层的块数满足上文中对每层块数的要求,并且使得树可以高效存储不可变内存缓存所下刷的数据),从高到低的依次为:优先级1:不可变内存缓存的下刷操作,若不满足下刷的执行条件,则继续判断优先级2条件。优先级2:对于非最后一层Li(1≤i≤n-1),从上层开始判断是否存在块数等于ti+1且下层块数小于ti+1+1(i+1<n,下一层为非最后一层)或小于tn(下一层为最后一层Ln)的层。若存在,则选择该层中的某一块进行合并操作以减少该层的块的数目。选择的策略为:将满足孩子块的数目小于等于t的所有块加入候选集合(由树的每层块数的约束易知集合中必定存在至少一个这样的块)。然后在候选集合中选取最优的块,选取策略为:该块存储的数据量除以与孩子块数目的值越大越好,以及该块与相邻块的范围合并后所生成的新的范围的孩子块的数目越小越好。对选择出的最优块执行合并操作。若不存在这样的层,则继续判断优先级3条件。优先级3:从上层到下层依次判断是否存在存储的数据量达到存储阈值的块,若存在则选取遍历过程所遇到的第一个块(阻塞“不可变内存缓存”的块优先)。若该块的孩子块的数目小于2t,则将对该块执行下刷操作;若该块的孩子块数目大于等于2t,则将对该执行分裂操作。若选取的块将进行的是下刷操作,但因为该块存在已经到达存储阈值的孩子块而使得该操作的执行条件不满足,则改为选择该孩子块进行下刷或分裂操作,依次类推进行递归查找,直至最终选择到第一个满足下刷或分裂执行条件的块。若最终未选择到任何目标块和操作,则从步骤1.1开始重新执行。在选择了要处理的块和该块上将要执行的操作后,再依次对将要修改的块加锁,若所有锁都加锁成功后,便成功获取了任务,若对任一块加锁失败,则对所加的所有锁解锁,并从步骤1.1开始重新执行。步骤1.1保证了从步骤1.2开始之后的步骤执行时最后一层的块个数必定小于tn。这样步骤1.2中的优先级2中若存在一个或多个块数等于ti+1的层,必可以选取一个同时满足块数等于ti+1且下层块数小于ti+1+1(i+1<n,下一层为非最后一层)或小于tn(下一层为最后一层Ln)的层进行合并操作。步骤1.2中的优先级2的设立目的是保证优先级3中定存在满足执行条件的任务(不会因为所有层的块数都等于ti+1而使得优先级3中的所有任务被阻塞),使得树一定可以正常的运作。步骤1.2中的优先级3设立的目的是,处理树中种达到存储阈值的块,既可以是阻塞上层任务的块使得树可以继续存储不可变内存缓存下刷的数据,也可以是未为阻塞上层任务的块而优化性能。步骤1.3,根据获得的任务执行实际的磁盘操作,具体操作如下(逻辑独立而不互斥的任务可以并行执行):1)若为下刷操作,分为3种情况:情况1:若待下刷的块不存在孩子块,则直接进入步骤1.4修改该块的元数据(元信息)以实现下移。当前层的被下移的块的范围保留。情况2:若待下刷的块存在孩子块且下一层为最后一层。对于落在最后一层中的某一块的范围内的记录,直接修改该块;对于落在最后一层所有块的范围之外的记录,选择距离与待插入记录的键的距离最小的孩子块进行修改。对于后者需要修改孩子块的范围。修改最后一层孩子块的具体操作为:若该块存储的数据未达到阈值,则进行追加操作;若达到,则将待写入的数据与原有的数据进行归并排序生成若干个新的块(使新生成的块总数比原来的块总数最多多一个)。当前层的被下移的块的范围保留。参见图2,存在被追加的孩子块,也存在被归并排序的孩子块。情况3:若待下刷的块存在孩子块且下一层为非最后一层。下刷数据时,对于落在下一层某一块的范围内的记录,直接将数据追加到该块;对于落在所有块的范围之外的记录,选择与待插入记录的键的距离最小的孩子块进行追加,并改变孩子块的键范围。当前层的被下移的块的范围保留。参见图3,只存在被追加的孩子块。和现有技术相比,这种操作几乎完全避免了归并排序,而用追加操作代替,因此大大减小了写放大,提高了写性能。2)若为合并操作,具体操作流程与下刷操作类似,即按照上述下刷操作同样方式将块内的数据下移至下一层中,唯一的不同是在下刷后该块的范围被删除,以使得该块所在层的块的数目减1。3)若为分裂操作,则该块分裂成两个新的块,分裂后新生成的两个块拥有的孩子块数目相等。步骤1.4,申请一个排它锁,以保证某一时刻内只有一个后台线程可以进行本步骤;申请成功后,将执行的实际的磁盘操作所修改的树的结构信息写“树元数据变化日志”,并根据此信息更新内存中的树的元信息。步骤1.5,若处理的是“不可变内存缓存”的下移操作,销毁“不可变内存缓存”,若有用户线程正睡眠,则唤醒用户线程;将本线程所获取的所有锁解锁。本线程从步骤1.1继续开始执行。当用户需读取数据时,执行的步骤如下:步骤2.1,读“可变内存缓存”,若读取到所需要的记录即返回;步骤2.2,读“不可变内存缓存”,若读取到所需要的记录即返回;步骤2.3,依次读层L1->Ln,找到即返回,若到最后一层没找到,则说明数据库中不存在对应记录。在读取过程中,通过MVCC(多版本并发控制)使得磁盘的读取过程中无需持有任何锁。图4是本方法实现块在磁盘上的组织方式,如图4(左)所示,块中存储的数据有索引数据、布隆过滤器和用户记录;前两者存储在块的末尾,用户记录存储在块的头部;在存储数据时,可能出现三种情况:1)当块中间的空闲空洞存储得下存储本次要写的所有数据,采取如图4(左)所示的存储方式,第1次写到第n次写的用户记录依次存储在块的前端,第1次写到第n次写的索引数据和布隆过滤器依次存储在块的后端;2)当块中间的空闲空洞存储不下本次要写的所有数据但可存储得下索引数据和布隆过滤器时,采取如图4(右)所示的存储方式,将第n+1次写的索引数据和布隆过滤器依次存储在块的后端后,将第n+1次写的用户记录追加到块的尾部;3)当中间的空闲空洞存储不下索引数据和布隆过滤器时,则将要写的数据和原有的数据归并排序生成一个新的块。在实现时,通过将块中存储的数据达到95%时视为达到存储阈值几乎可以完全避免此种情况的发生。进一步地,本发明提出,更优选地,可以替代归并排序,通过图5的方式实现而不用上述归并方法,即将索引数据、布隆过滤器和用户记录都追加到块的尾部。实现时,块可以用文件的方式实现,每个文件的阈值为64MB,但可超过64MB,如当以图4(右)的方式存储时。注意,在读取块中的记录时,先读时间上靠后追加的序列,若找到所需记录,其他未读取的序列便无需读取,可直接返回。以上所述的方法仅为“通过将一个块中的记录的完全排序方式改为部分排序方式(通过多个排序序列组成)以大大减少写放大,再在每个块的索引信息中加上布隆过滤器使得该方案对读性能的影响降到最低”思想的一个实例。凡在本发明的精神与原则之内,所做的任何修改,改进等,均应包含在本发明的保护范围之内,如在buffer-tree中的块中应用同样逻辑也在本发明的保护范围之内。当前第1页1 2 3 当前第1页1 2 3 
当前第1页1 2 3 
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1