LAXCUS 大数据集群操作系统:一个分布式分时共享 E 级系统软件(四)
第三章数据存取
当前的很多大数据处理工作,一次计算产生几十个 GB、或者几十个 TB 的数据已是正常现象,驱动数百、数千、甚至上万个计算机节点并行运行也不足为奇。但是在数据处理的后面,对于这种在网络间传输、数量巨大、且发生频率日益增加的数据处理,需要大数据系统具备极高的稳定性和可靠性才能保证完成计算任务。这是一项极其复杂的工作,需要兼顾好数据处理的每一个环节,而在这些环节中,最底层的一环:数据存取,又基本决定了大数据处理的整体效率。
在这一章里,我们将从数据的一些本质特征谈起,从多个角度去阐述数据存取设计,以及如何优化它们。
3.1 数据块
在实际的数据业务中,一个单位的数据尺寸往往有很大的随机性。小的可能是几十、几百个字节,大的可能达到数十数百兆。当一台计算机的数据存储量达到 TB 规模,每天处理的数据量超过 TB 规模的时候,即使操作系统的文件系统支持这种单位的存储,也将使磁盘运行不堪重负,况且因此产生的磁盘碎片也是一种极大的浪费。
针对这种情况,Laxcus 采用这样一套新数据存取流程,来保障高效率的数据处理。首先,将内存做为数据进入硬盘前的过渡,在内存开辟出一块固定尺寸的空间,此后的每一批数据,都以流式的串行追加方式写入。这样即使当时有多个写入者,因为内存处理效率高和串行写入的原因,在写入过程中几乎没有延迟或者很小,也不会产生写入冲突。当内存空间的数据量达到规定阀值的时候,系统将内存空间关闭,然后执行一系列的数据优化措施,包括对数据的压缩和重组,最后将这块数据以文件形式写入磁盘。进入磁盘的文件,被称为“数据块”。
当数据在内存驻留时,我们将它称为数据块的“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 以大规模的读操作为主,兼顾少量的写操作
根据我们的调查,在很多商业应用场景中,由于固态硬盘(SSD)使用成本居高不下,承担数据存储工作的仍然是传统的机械硬盘(温彻斯特硬盘)。调查中同时也发现,很多大数据处理过程,由于硬盘的 IO 效率远滞后于 CPU 和内存,75%-90%的时间被消耗在硬盘存取上,即使是固态硬盘,虽然 IO 效率比机械硬盘提高一个量级,但是仍然远低于 CPU 和内存的处理能力。这种硬件之间的不匹配,导致硬盘成为大数据处理过程中的最主要瓶颈。所以,改善硬盘的处理效率,对提高大数据处理效率有立竿见影的效果,但是机械硬盘工作的特点,又使它与 CPU、内存这些电子部件在运行效率上存在着巨大的差异。在这种条件下,尽可能多地根据硬盘自身的特点,发挥出它的最大效能,成为解决问题的重要办法。
同时,我们对用户的数据应用追踪中也发现,大数据处理过程中,96%发生在检索操作上,3%是添加数据,删除和更新合计只占不到 1%的比例。这个现象促使我们对数据存储产生了不同以往的定位和思路,将数据存储设计的重点围绕着检索展开,并据此制定了以下的执行策略:首先,为保证大数量高频度的检索操作,结合到计算机内的 CPU、内存、硬盘各主要工作部件的性能,在保证数据的持续吞吐性能上,流式处理效率最高。并行的数据写入在进入存储层面时,汇流为串行模式。检索操作的最终目标是硬盘,硬盘检索受制于硬盘物理特性的影响,在数据计算过程中,严重拖滞了整体性能的发挥,为提高数据处理性能,需要在检索前对数据进行优化,如关联和聚凑,同时提供一批优化算法给用户,使用户可以按照自己的意愿去组织和检索数据。删除不改变数据本身,只对数据做无效记录。数据更新分解为删除和添加两步操作,目的在于简化和内聚数据处理流程,同时避免发生多次硬盘读写现象。
上述处理虽然改善了存取性能,但是不可能从根本改变硬盘慢的特点。若要使性能获得根本性的提升,必须跳过硬盘这个瓶颈,所以在 2.x 版本中增加了一套新的数据处理方案:让内存代替硬盘,数据在网络、内存、CPU 之间流动,以接近 CPU 的速度运行。这种内存处理方案解决了硬盘存取慢的问题,使数据处理性能获得巨大的提升。根据我们的测试评估结果,这个提升幅度在 2 个量级左右。在实际应用中,用户如果有实时性的数据处理需求,且有足够的内存做保证时,内存处理方案无疑是最佳的选择。
3.6 内存计算
数据存储在磁盘上。数据受到磁盘本身的物理特性限制,其读写速率要远远低于内存和 CPU,拖慢了整个计算过程。尤其当面对热点数据块的读写,或者需要读取大量数据做数据计算时,这个影响尤其明显。为了提高计算效率,一个简单的办法就是把数据调入内存,跨过硬盘这道瓶颈,让数据在内存和 CPU 之间来运行,从而减少磁盘对数据的影响。
我们提供了两个加载数据块的方案:1.当内存空间比较充裕时,由系统判断,把热点数据块调入内存。2.由用户从 Front 节点发出命令,指定某些数据,把它们加载到内存里。加载数据的过程中,运行系统会检查计算机的可用内存容量,在接近规定限制值前停止,不会发生内存溢出的现象。
如果这个加载过程是由系统引发的,这是一个临时性加载,热点数据块会受到持续监视。当低于调用阀值,或者内存开始紧张时,或者使用频率更高的热点数据块出现时,会把它从内存中移除。
用户也可以卸载数据块,同样是通过命令从 Front 节点发出。
数据在内存的时候,不影响它的写操作。如果是添加、删除、更新这样的情况发生了,会同步修改它在内存和磁盘上的记录,这个过程仍然是串行的。
实际上,内存数据更适合执行大规模数据检索。尤其在今天很多的 CPU 都已经是 64 位,寻址范围突破 4G 限制的情况下。只要有足够数量的内存,使集群成为一个临时的数据仓库,让数据跨过磁盘,完全在网络、内存、CPU 之间运行,这是目前提高数据计算效率最有效的办法。
3.7 快照和备份
每一个 Cache 状态的主数据块,在 Data 主节点上生成后,会通过网络定向通知其它几个关联节点,产生一个相同编号的 Cache 数据块。此后这个主数据块每一次的写操作,都会通过网络向它们传递它最新的记录。这种以复本形式存在的 Cache 状态数据块,被称为“快照”。
每一个主数据块,从 Cache 状态转入 Chunk 状态后,主节点将立即启动,通过网络分发这个数据块的数据复本。这些被传播到不同节点的数据块,被称为“备份”。
备份数据块传递完成后,主 Data 节点会通知关联的 Data 节点,将 Cache 状态的“快照”删除。此后的运行过程中,如果发生写操作,Chunk 状态的主数据块仍会执行与快照一样的处理。
快照和备份的分配,将根据集群的网段原则进行选择。这是一个类似 LINUX TRACEROUTE 命令的处理过程,通过向不同 Data 节点发送 ICMP 包,探测当前节点与目标节点的跳点数,判断网段之间的距离,按照由远及近的顺序进行分配。
系统默认规定同质数据块的存量是 3,即有 1 个主块,2 个属于快照或者备份的从块。主块允许执行读/写处理,从块只能执行读处理,和接受主块的覆写操作。这个存量参数也可以由用户定义,但如果实际运行环境的节点数量不足时,将根据实际情况自行调整。
快照和备份使同质数据块之间保持了原始级的数据一致性,同时还实现了分解读处理压力、负载平衡、冗灾恢复的目的。如果当某个数据块读操作压力过大时,Data 节点会做出判断,把这个数据块进行扩散,以缓解当前节点的压力。
3.8 完整性检查
Data 节点启动时,会对磁盘上的每个数据块进行扫描,检查它的数据完整性。完整性检查将具体到数据块的每一列。如果在扫描过程中发现错误,将转入暂停状态,通过网络找到一段正确的数据复本,来覆盖错误的数据。扫描数据块的工作在内存中进行,完成后释放。扫描采用 CRC32 校验和算法,这个计算过程非常快,在 32 位 Pentium4 2G 计算机上,一个 64M 数据块的扫描时间不超过 1 秒钟。通过完整性检查,可以即时判断出每个数据块可能存在的错误,为此后正确的数据处理提供了保证。
3.9 提高数据处理效率的一些措施
分布计算业务普遍具有数据量大、耗时长、计算复杂的特点,在运行过程中会涉及到大批计算机节点和不同的处理环节。如果在执行这些工作前,有针对性地为它们产生某些数据,使它们能够减少磁盘读写频率,或者省略掉运行过程中的一些处理环节,这会对改善数据处理效率有很大帮助。
在磁盘存取层面,这样的预处理工作包括:把可能被重复使用的中间数据提前生成。针对删除、更新操作造成的磁盘数据碎片现象,做定期碎片整理工作。为了改善集群数据分布不均、单点数据量过大的问题,按需求调整集群数据分布等。
这些预处理工作被投入运行环境之后,数据处理效率有了明显提高。为了加快数据的生成速度,它们都被放到内存中执行。例如优化一个标准的 64M 数据块,在 Pentium4 2.0 G 芯片上,生成时间大约在 1.2 秒左右。另外,这些数据处理工作都是数据、计算双密集的,对内存、CPU 有很高的占用比率。考虑到这个原因,它们应该避免开业务繁忙的时段,放在系统空闲的时间执行,比如夜间的某个时段。这个时间的业务处理量会明显减少,有助于平衡系统资源使用效率,减少预处理工作对系统正常业务造成的不利影响。
3.10 主块冲突
任何一个编号的主数据块在任何时间只能有一个,当前两个相同编号的主数据块在集群上出现时,主块冲突就产生了。主块冲突通常发生在故障 Data 主节点重新启动之后,在进行完整性检查的过程中。
解决主块冲突由 Data 主节点自行协调处理,解决冲突的办法是判断文件的最后修改时间,以时间最新的那个主块为准。旧的主块会从磁盘上删除,新块被保留,从而达到防止主块冲突的目的。
3.11 数据负载检测
Data 节点在运行过程中,同一个时间可能有多个命令在执行,并且这些命令从磁盘上提取的数据量往往也是未知的,极有可能发生超载现象。面对这种情况,完全杜绝超载现象已不可能,能够做到的就是及时发现超载现象并且加以限制。
在一台计算机的硬件层面,发生超载的源头有两个:CPU、磁盘。CPU 超载原因是持续进行着大量的数据计算工作,磁盘超载是读写频率过高所致。CPU 超载是持续进行着大量的数据计算工作,而久久得不到缓解。磁盘超载是读写频率过高所致。减少它们超载的办法是限制数据计算量和磁盘 IO 量。Invoke/Produce 通过自适应机制实时追踪检查超载现象。一旦确认后,它将启动“锁”操作,限制计算任务的工作,降低对硬件设备的调用频率。必要时也会通知任务发起方,减少对本节点的调用频率。
对数据超载的检查还会追踪到每个数据块。如果 Invoke/Produce 发现某个数据块在一个时段的调用频率超过阀值,会检查本机的内存,在容量许可的情况下,将它加载到内存里运行。或者去网络上检查数据块的分布状况,把它分发给空闲的 Data 节点,用分散数据块调用的办法,来达到降低负载的目的。
版权声明: 本文为 InfoQ 作者【陈泽云】的原创文章。
原文链接:【http://xie.infoq.cn/article/0822562ed8d5844ef2ed43ce4】。
本文遵守【CC BY-NC】协议,转载请保留原文出处及本版权声明。
评论