第三章 数据存取

  当前的很多大数据处理工作,一次计算产生几十个GB、或者几十个TB的数据已是正常现象,驱动数百、数千、甚至上万个计算机节点并行运行也已经不足为奇。但是在数据处理的后面,对于这种在网络间传输、数量巨大、且发生频率日益增加的数据处理,需要大数据系统具备极高的稳定性和可靠性才能保证完成计算任务。这是一项极其复杂的工作,需要兼顾好数据处理的每一个环节,而在这些环节中,最底层的一环:数据存取,又基本决定了大数据处理的整体效率。

  在这一章里,我们将从数据的一些本质特征谈起,从多个角度去阐述数据存取设计,以及如何优化它们。

3.1 数据块

  在实际的数据应用中,一个单位的数据尺寸往往有很大的随机性。小的可能是几十、几百个字节,大的可能达到数十,数百兆。当一台计算机的数据存储量达到TB规模,每天处理的数据量超过TB规模的时候,即使操作系统的文件系统支持这种单位的存储,也将使磁盘运行不堪重负,况且因此产生的磁盘碎片也是一种极大的浪费。

  针对这种情况,我们设计了一套新的数据存储流程,来保障高效率的数据处理。首先,将内存做为数据进入硬盘前的过渡,在内存开辟出一块固定尺寸的空间,此后的每一批数据,都被以流式的串行追加方式写入。这样即使当时有多个写入者,因为内存处理效率高和串行写入的原因,在写入过程中几乎没有延迟或者很小,也不会产生写入冲突。当内存空间的数据量达到规定阀值的时候,系统将内存空间关闭,然后执行一系列的数据优化措施,包括对数据的压缩和重组,最后将这块数据以文件形式写入磁盘。进入磁盘的文件,被称为“数据块”。

  当数据在内存驻留时,我们将它称为数据块的“CACHE”状态。数据写入磁盘后,我们称它为数据块的“CHUNK”状态。系统为内存数据空间设置的标准阀值是64M,这个参数或者可以由用户定义,最大不能超过4G。对于超大尺寸的内存数据空间,系统将视磁盘文件系统和可用内存空间而定,如果不能支持,将自动调整到合适的尺寸。

  为了能够区分内存和磁盘上的每一个数据块,系统会在每个数据块生成时,为它设置一个64位的无符号长整数,做为唯一标识它的编号。编号由Top运行节点提供,保证集群中唯一,不会重复。数据写入磁盘后,这个编号也成为数据块的文件名。

  依据上述对Data节点的定义,数据块只会保存在Data节点上,并且依从Data节点的主从关系。即所有主节点上的数据块都是主块(PRIME CHUNK),从节点保存从块(SLAVE CHUNK)。数据块的主从角色也会根据所属Data节点级别发生变化。一个集群上,同质数据块只允许拥有一个主块,其它都是从块。写数据的传入,由Call节点负责实施,向相关的Data主节点均匀推送,这样可以使这些Data主节点,在各自拥有的数据量上保持一个相对均衡的状态。

  系统不会在其它节点上缓存Data节点数据,这个设计是参考了大量实例后做的决定。经统计,单位时间内的网络计算,一个指令被重复执行的概率极低,这就连带影响到数据的重复命中率,使得缓存数据没有意义,并且缓存数据会占用大量宝贵的内存、硬盘空间,显得得不偿失。

  数据块的采用,很好地消除了磁盘碎片的现象,也减轻数据输入磁盘时的写处理压力。按照数据块标准的64M计算,数据写入磁盘的时间不会超过1秒。检索数据时,将按照优化规则从磁盘读取数据,这样也降低了数据输出过程的读处理压力。

3.2 存储模型

  存储模型是数据在磁盘上的物理组织结构。在许多介绍数据库的书籍里,存储模型又被称为内模型。它在很大程度上决定了数据的适用领域,是衡量数据存取性能的重要指标之一。

  我们在数据块的基础上进行了行存储模型(NSM)和列存储模型(DSM)的设计。因为两种存储模型的组织结构完全不同,以下将结合图3.1和数据运作流程,来阐述这两种存储模型的特点及优劣。

  见图3.1,这是一个网络音乐文件表,由6个属性组成。左侧是行存储模型,每一行由不同属性的列值组成,数据是从左到右、从上到下的排列,形成行与行连接的布局。右侧是列存储模型,同属性的列值被组织在一起,成为列的集合,数据是从上向下、从左到右的排列,形成列集合与列集合连接的布局。

  行/列存储模型都是建立在数据块的基础上。CACHE状态时,数据的读/写处理都在内存中进行,虽然两种存储模型的组织结构不尽相同,但是因为内存处理效率颇高,这个问题在速度面前就显示得微不足道。放到实际环境中检验,通过追踪两个存储模型的数据处理流程,发现它们的处理效率的确没有差别,所以两种存储模型虽然结构不同,但是在CACHE状态可以完全忽略。

  差异主要体现在数据块的CHUNK状态。进行CHUNK状态后,数据处理将在磁盘上执行。行存储是以行为单位,若整行读取,那么行存储效率很高;如果读取多行,最好在数据写入前将被检索的数据排列在一起,这样只需要对磁盘做一次定位和读取。同样的,列存储是以列集合为单位,它适合对单列连续数据的读取,如果读取多列数据,就需要扫描不同的磁盘位置,这会降低磁盘检索效率。

  数据块CHUNK状态的写处理,只会发生删除和更新操作。因为更新被分解为删除和追加,所以实质仍然是删除操作。删除操作不会将数据从磁盘中清除,只在数据的某个位置做一个无效标记。如果是批量删除,就需要分别做多个无效标记,这种操作对磁盘性能影响很大。

  但是在实际应用时不是这样。根据磁盘(温彻斯特硬盘)工作特性,一个完整的读/写处理,分为磁头定位、等待、数据传输三个阶段。从目前磁盘性能的发展趋势来看,带宽速率的提升优于磁头定位,况且现在计算机的内存容量已经足够大,缓存一些数据也绰绰有余。根据这种情况,实际的读/写处理,是将需要的数据一次性调入内存,在内存中完成处理后再输出。这种处理方式,非常有助于提高磁盘读写效率。

  在其它方面,列存储模型的数据是可以压缩的,压缩的好处是能够节省磁盘和内存的空间。比如当某一列有10个999的整数时,就不必把10个999依次排列,而是在999前面加一个10,就表达了10个999的含义。如果有增加或者删除999的操作时,只需要对10这个位置的参数进行修改,而不用修改999本身。行存储模型则没有这方面的能力。另外我们在列存储模型中采用了索引合并技术,这项技术除了节省磁盘和内存空间,还省略了关联操作,简化了存储层面的数据计算。行存储模型如果使用索引,则需要用户说明具体的列,并且在行数据集合之外开辟一块索引数据空间,处理前进行关联才能生效。根据我们对许多应用数据的统计,两组数据完全相同的存储模型,它们的空间占比,列存储模型是行存储模型的28% - 53%之间。

  综上所述,行/列存储模型在CACHE状态的处理性能持平。在CHUNK状态,行存储模型适合整行读取,列存储模型适合单列读取。CHUNK状态的写处理,因为数据在内存进行,它们处理性能仍然基本一致。

 

 

图3.1 行存储模型和列存储模型

3.3 行级锁

  从数据的逻辑角度来看,“行”是能够表达一组完整信息的最小单位。为了保证数据处理的一致性,防止多个操作者竞用数据可能引起的数据混乱,我们在“行”这个层级给数据规置了锁定处理。行级锁是一个互斥锁,一个单位时间内只能执行一个单写或者多读操作。因为它的粒度足够细,只在一行记录上进行操作,不会触及其它行,实际上速度非常快,对数据块的读写几乎没有影响。目前行级锁已经在行、列两个存储模型上实现。

3.4 元数据

  为了快速的数据定位和数据计算,元数据做为数据操作者和被操作对象之间的中间媒质,来配合完成数据处理工作。元数据本质上是实体资源的抽象表示,用于描述节点在某一个时间的形态。在Laxcus系统里,元数据又分为节点元数据和数据元数据。前者由网络地址和运行参数组成,后者将数据块的内容格式化成定长的数值,并且按照要求的规则排列和组合。

  所有元数据都在节点运行过程中产生,随着节点运行发生变化和进行更新。元数据产生的数据量非常小,通常只有几百到几千个字节之间。这个特点使它非常适合在网络间快速传递和在内存中驻留。不同类型的节点对元数据各有不同,它们会根据的自己需要,通过管理节点或者直接通信的方式,去收集汇总这些信息,然后在本地进行筛选、分组、排列,存储在内存中,为数据处理提供必需的计算依据。运行环境中的元数据都是实时的,误差被控制在秒级,由一个资源管理模块去负责收集、管理、分配这些信息。这个模块在Laxcus集群架构里起着承上启下的作用,它有一个专门的名称:Laxcus实时映像系统(Laxcus Realtime Map System)。

3.5 内存计算

  数据存储在磁盘上。数据受到磁盘本身的物理特性限制,其读写速率要远远低于内存和CPU,拖慢了整个计算过程。尤其当面对热点数据块的读写,或者需要读取大量数据做数据计算时,这个影响尤其明显。为了提高计算效率,一个简单的办法就是把数据调入内存,跨过硬盘这道瓶颈,让数据在内存和CPU之间来运行,从而减少磁盘对数据的影响。

  我们提供了两个加载数据块的方案:1.当内存空间比较充裕时,由系统判断,把热点数据块调入内存。2.由用户发出命令,指定某些数据,把它们加载到内存里。加载数据的过程中,运行系统会检查计算机的可用内存容量,在接近规定限制值前停止,不会发生内存溢出的现象。 

  如果这个加载过程是由系统引发的,这个一个临时性加载,热点数据块会受到持续监视。当低于调用阀值,或者内存开始紧张时,或者使用频率更高的热点数据块出现时,会把它从内存中移除。

  用户也可以卸载数据块,这个操作同样是通过命令进行。

  数据在内存的时候,不影响它的写操作。如果是添加、删除、更新这样的情况发生了,会同步修改它在内存和磁盘上的记录,这个过程仍然是串行的。

  实际上,内存数据更适合执行大规模数据检索。尤其在今天很多的CPU都已经是64位,寻址范围突破4G限制的情况下。只要有足够数量的内存,使集群成为一个临时的数据仓库,让数据跨过磁盘,完全在网络、内存、CPU之间运行,这是目前提高数据计算效率最有效的办法。

3.6 快照和备份

  每一个Cache状态的主数据块,在Data主节点上生成后,会通过网络定向通知其它几个关联节点,产生一个相同编号的Cache数据块。此后这个主数据块每一次的写操作,都会通过网络向它们传递它最新的记录。这种以复本形式存在的Cache状态数据块,被称为“快照”。

  每一个主数据块,从Cache状态转入Chunk状态后,主节点将立即启动,通过网络分发这个数据块的数据复本。这些被传播到不同节点的数据块,被称为“备份”。

  备份数据块传递完成后,主Data节点会通知关联的Data节点,将Cache状态的“快照”删除。此后的运行过程中,如果发生写操作,Chunk状态的主数据块仍会执行与快照一样的处理。

  快照和备份的分配,将根据集群的网段原则进行选择。这是一个类似LINUX TRACEROUTE命令的处理过程,通过向不同Data节点发送ICMP包,探测当前节点与目标节点的跳点数,判断网段之间的距离,按照由远及近的顺序进行分配。 

  系统规定同质数据块的默认存量是3,即有1个主块,2个属于快照或者备份的从块。主块允许执行读/写处理,从块只能执行读处理,和接受主块的覆写操作。这个存量参数也可以由用户定义,但如果实际运行环境的节点数量不足时,将根据实际情况自行调整。

  快照和备份使同质数据块之间保持了原始级的数据一致性,同时还实现了分解读处理压力、负载平衡、冗灾恢复的目的。如果当某个数据块读操作压力过大时,Data节点会做出判断,把这个数据块进行扩散,以缓解当前节点的压力。

3.7 完整性检查

  Data节点启动时,会对磁盘上的每个数据块进行扫描,检查它的数据完整性。完整性检查将具体到数据块的每一列。如果在扫描过程中发现错误,将转入暂停状态,通过网络找到一段正确的数据复本,来覆盖错误的数据。扫描数据块的工作在内存中进行,完成后释放。扫描采用CRC32校验和算法,这个计算过程非常快,在32位Pentium4 2G计算机上,一个64M数据块的扫描时间不超过1秒钟。通过完整性检查,可以即时判断出每个数据块可能存在的错误,为此后正确的数据处理提供了保证。

3.8 提高数据处理效率的一些措施

  分布计算业务普遍具有数据量大、耗时长、计算复杂的特点,在运行过程中会涉及到大批计算机节点和不同的处理环节。如果在执行这些工作前,有针对性地为它们产生某些数据,使它们能够减少磁盘读写频率,或者省略掉运行过程中的一些处理环节,这会对改善数据处理效率有很大帮助。

  在磁盘存取层面,这样的预处理工作包括:把可能被重复使用的中间数据提前生成。针对删除、更新操作造成的磁盘数据碎片现象,做定期碎片整理工作。为了改善集群数据分布不均、单点数据量过大的问题,按需求调整集群数据分布等。

  这些预处理工作被投入运行环境之后,数据处理效率有了明显提高。为了加快数据的生成速度,它们都被放到内存中执行。例如优化一个标准的64M数据块,在Pentium4 2.0 G芯片上,生成时间大约在1.2秒左右。另外,这些数据处理工作都是数据、计算双密集的,对内存、CPU有很高的占用比率。考虑到这个原因,它们应该避免开业务繁忙的时段,放在系统空闲的时间执行,比如夜间的某个时段。这个时间的业务处理量会明显减少,有助于平衡系统资源使用效率,减少预处理工作对系统正常业务造成的不利影响。

3.9 主块冲突

  一个编号的主数据块任何时间只能有一个。当前两个相同编号的主数据块在集群上出现时,主块冲突就产生了。主块冲突通常发生在故障Data主节点重新启动之后,在进行完整性检查的过程中。

  解决主块冲突由Data主节点自行协调处理,解决冲突的办法是文件的最后修改时间,以时间最新的那个主块为准。旧的主块会从磁盘上删除,从而达到防止主块冲突的目的。

3.10 数据负载检测

  Data节点在运行过程中,同一个时间可能有多个命令在执行,并且这些命令从磁盘上提取的数据量往往也是未知的,极有可能发生超载现象。面对这种情况,完全杜绝超载现象已不可能,能够做到的就是及时发现超载现象并且加以限制。

  在一台计算机的硬件层面,发生超载的源头有两个:CPU、磁盘。CPU超载原因是持续进行着大量的数据计算工作,磁盘超载是读写频率过高所致。CPU超载是持续进行着大量的数据计算工作,而久久得不到缓解。磁盘超载是读写频率过高所致。减少它们超载的办法是限制数据计算量和磁盘IO量。Invoke/Produce通过自适应机制实时追踪检查超载现象。一旦确认后,它将启动“锁”操作,限制计算任务的工作,降低对硬件设备的调用频率。必要时也会通知任务发起方,减少对本节点的调用频率。

  对数据超载的检查还会追踪到每个数据块。如果Invoke/Produce发现某个数据块在一个时段的调用频率超过阀值,会检查本机的内存,在容量许可的情况下,将它加载到内存里运行。或者去网络上检查数据块的分布状况,把它分发给空闲的Data节点,用分散数据块调用的办法,来达到降低负载的目的。

posted on 2016-04-12 19:18  laxcus  阅读(289)  评论(0编辑  收藏  举报