3.2 数据存储技术
为保证高可用、高可靠和经济性,云计算采用分布式存储的方式来存储数据,采用冗余存储的方式来保证存储数据的可靠性,即为同一份数据存储多个副本。
云计算的数据存储技术主要有谷歌的非开源的GFS(Google File System)和Hadoop开发团队开发的GFS的开源实现HDFS(Hadoop Distributed File System)。大部分IT厂商,包括雅虎、英特尔的“云”计划采用的都是HDFS的数据存储技术。
云计算的数据存储技术未来的发展将集中在超大规模的数据存储、数据加密和安全性保证以及继续提高I/O速率等方面。
下面以Google文件系统GFS和Hadoop分布式文件系统(HDFS)为例进行具体阐述。
3.2.1 Google文件系统
3.2.1.1 GFS的定义和内涵
GFS是一个管理大型分布式数据密集型计算的可扩展的分布式文件系统。它使用廉价的商用硬件搭建系统并向大量用户提供容错的高性能的服务。云计算的数据存储技术未来的发展将集中在超大规模的数据存储、数据加密和安全性保,证以及继续提高I/O速率等方面。
GFS与传统分布式文件系统的区别如表3-1所示。
表3-1 GFS与传统分布式文件系统的区别
3.2.1.2 GFS的构成
GFS系统由一个Master和大量块服务器构成。Master存放文件系统的所有元数据,包括名字空间、存取控制、文件分块信息、文件块的位置信息等。GFS中的文件切分为64MB的块进行存储。
在GFS文件系统中,采用冗余存储的方式来保证数据的可靠性。每份数据在系统中保存3个以上的备份。为了保证数据的一致性,对于数据的所有修改需要在所有的备份上进行,并用版本号的方式来确保所有备份处于一致的状态。
客户端不通过Master读取数据,可避免大量读操作使Master成为系统瓶颈。客户端从Master获取目标数据块的位置信息后,直接和块服务器交互进行读操作。
GFS将写操作控制信号和数据流分开,如图3-2所示
图3-2 写操作控制信号和数据流
即客户端在获取Master的写授权后,将数据传输给所有的数据副本,在所有的数据副本都收到修改的数据后,客户端才发出写请求控制信号。在所有的数据副本更新完数据后,由主副本向客户端发出写操作完成控制信号。当然,云计算的数据存储技术并不仅仅只是GFS,其他IT厂商,包括微软、Hadoop开发团队也在开发相应的数据管理工具。本质上是一种分布式的数据存储技术,以及与之相关的虚拟化技术,对上层屏蔽具体的物理存储器的位置、信息等。快速的数据定位、数据安全性、数据可靠性以及底层设备内存储数据量的均衡等方面都需要继续研究完善。
由于搜索引擎需要处理海量的数据,所以Google的两位创始人Larry Page和Sergey Brin在创业初期设计一套名为“BigFiles”的文件系统,而GFS(全称为“Google File System”)这套分布式文件系统则是“BigFiles”的延续。
GFS主要分为两类节点。
(1)Master节点 主要存储与数据文件相关的元数据,而不是Chunk(数据块)。元数据包括一个能将64位标签映射到数据块的位置及其组成文件的表格,数据块副本位置和哪个进程正在读写特定的数据块等。还有Master节点会周期性地接收从每个Chunk节点来的更新(“Heart-beat”)以让元数据保持最新状态。
(2)Chunk节点 顾名思义,肯定用来存储Chunk,数据文件通过被分割为每个默认大小为64MB的Chunk的方式存储,而且每个Chunk有唯一一个64位标签,并且每个Chunk都会在整个分布式系统被复制多次,默认为3次。
图3-3就是GFS的架构图。
图3-3 GFS的架构图
3.2.1.3 GFS的特点
在设计上,GFS主要有8个特点。
(1)大文件和大数据块 数据文件的大小普遍在GB级别,而且其每个数据块默认大小为64MB,这样做的好处是减少了元数据的大小,能使Master节点非常方便地将元数据放置在内存中以提升访问效率。
(2)操作以添加为主 因为文件很少被删减或者覆盖,通常只是进行添加或者读取操作,这样能充分考虑到硬盘现行吞吐量大和随机读写慢的特点。
(3)支持容错 首先,虽然当时为了设计方便,采用了单Master的方案,但是整个系统会保证每个Master都有其相对应的复制品,以便于在Master节点出现问题时进行切换。其次,在Chunk层,GFS已经在设计上将节点失败视为常态,所以能非常好地处理Chunk节点失效的问题。
(4)高吞吐量 虽然其单个节点的性能无论是从吞吐量还是延迟都很普通,但因为其支持上千个节点,所以总的数据吞吐量是非常惊人的。
(5)保护数据 文件被分割成固定尺寸的数据块以便于保存,而且每个数据块都会被系统复制3份。
(6)扩展能力强 因为元数据偏小,使得一个Master节点能控制上千个存储数据的Chunk节点。
(7)支持压缩 对于那些稍旧的文件,可以通过对它进行压缩,来节省硬盘空间,并且压缩率非常惊人,有时甚至接近90%。
(8)用户空间 虽然在用户空间运行在运行效率方面稍差,但是更便于开发和测试,还能更好地利用Linux自带的一些POSIC API。
3.2.2 Hadoop分布式文件系统(HDFS)
3.2.2.1 HDFS功能
HDFS(Hadoop Distributed File System)是一个为普通硬件设计的分布式文件系统,是Hadoop分布式软件架构的基础部件。
HDFS在设计之初就做了如下假设。
①硬件错误是常态。
②流式数据访问为主,要求具备高吞吐量。
③存储的文件以大数据集为主。
④文件修改以尾部追加为主,一次写入多次读取。
基于以上几点,HDFS被设计为部署在大量廉价硬件上的,适用于大数据集应用程序的分布式文件系统,具有高容错、高吞吐率等优点。HDFS使用文件和目录的形式组织用户数据,支持文件系统的大多数操作,包括创建、删除、修改、复制目录和文件等。HDFS提供了一组Java API供程序使用,并支持对这组API的C语言封装。用户可通过命令接口DFSShell与数据进行交互,以容许流式访问文件系统的数据。HDFS还提供了一组管理命令,用于对HDFS集群进行管理,这些命令包括设置NameNode,添加、删除DataNode,监控文件系统使用情况等。
3.2.2.2 HDFS的基本概念
(1)数据块(block)
①HDFS(Hadoop Distributed File System)默认的最基本的存储单位是64MB的数据块。
②和普通文件系统相同的是,HDFS中的文件是被分成64MB一块的数据块存储的。
③不同于普通文件系统的是,HDFS中,如果一个文件小于一个数据块的大小,并不占用整个数据块存储空间。
(2)元数据节点(NameNode)和数据节点(DataNode)
①元数据节点用来管理文件系统的命名空间。其将所有的文件和文件夹的元数据保存在一个文件系统树中。这些信息也会在硬盘上保存成以下文件:命名空间镜像及修改日志,其还保存了一个文件包括哪些数据块、分布在哪些数据节点上。然而这些信息并不存储在硬盘上,而是在系统启动的时候从数据节点收集而成的。
②数据节点是文件系统中真正存储数据的地方。客户端或者元数据信息可以向数据节点请求写入或者读出数据块。其周期性地向元数据节点回报其存储的数据块信息。
③从元数据节点。从元数据节点并不是元数据节点出现问题时候的备用节点,它和元数据节点负责不同的事情。其主要功能就是周期性地将元数据节点的命名空间镜像文件和修改日志合并,以防日志文件过大。这点在下面会详细叙述。合并过后的命名空间镜像文件也在从元数据节点保存了一份,以防元数据节点失败的时候,可以恢复。
3.2.2.3 HDFS文件读操作流程
客户端(Client)用File System的open()函数打开文件Distributed FileSystem用RPC调用元数据节点,得到文件的数据块信息。对于每一个数据块,元数据节点返回保存数据块的数据节点的地址。Distributed FileSystem返回FSData InputStream给客户端,用来读取数据。客户端调用stream的read()函数开始读取数据。DFS InputStream连接保存此文件第一个数据块的最近的数据节点。Data从数据节点读到客户端(Client),当此数据块读取完毕时,DFS InputStream关闭和此数据节点的连接,然后连接此文件下一个数据块的最近的数据节点。当客户端读取完毕数据的时候,调用FSDataInputStream的close函数。在读取数据的过程中,如果客户端在与数据节点通信出现错误,则尝试连接包含此数据块的下一个数据节点。失败的数据节点将被记录,以后不再连接。HDFS文件读操作流程如图3-4所示。
图3-4 HDFS文件读操作流程
3.2.2.4 HDFS文件写操作流程
客户端调用create()来创建文件,Distributed FileSystem用RPC调用元数据节点,在文件系统的命名空间中创建一个新的文件。元数据节点首先确定文件原来不存在,并且客户端有创建文件的权限,然后创建新文件。Distributed FileSystem返回DFS OutputStream,客户端用于写数据。客户端开始写入数据,DFS OutputStream将数据分成块,写入Data queue。Data queue由Data Streamer读取,并通知元数据节点分配数据节点,用来存储数据块(每块默认复制3块)。分配的数据节点放在一个pipeline里。Data Streamer将数据块写入pipeline中的第一个数据节点。第一个数据节点将数据块发送给第二个数据节点,第二个数据节点将数据发送给第三个数据节点。DFS OutputStream为发出去的数据块保存了ack queue,等待pipeline中的数据节点告知数据已经写入成功。如果数据节点在写入的过程中失败,关闭pipeline,将ack queue中的数据块放入data queue的开始。当前的数据块在已经写入的数据节点中被元数据节点赋予新的标示,则错误节点重启后能够察觉其数据块是过时的,会被删除。失败的数据节点从pipeline中移除,另外的数据块则写入pipeline中的另外两个数据节点。元数据节点则被通知此数据块是复制块数不足,将来会再创建第三份备份。当客户端结束写入数据,则调用stream的close函数。此操作将所有的数据块写入pipeline中的数据节点,并等待ack queue返回成功。最后通知元数据节点写入完毕。HDFS文件写操作流程如图3-5所示。
图3-5 HDFS文件写操作流程
3.2.2.5 HDFS存储分析
为了实现文件的可靠存储,HDFS做出了如下设计。
(1)冗余存储 在HDFS中大文件被存储为一系列的数据块,每个数据块被复制成若干个副本,存储在不同的数据节点上以保证系统的容错性。
(2)错误恢复 每个数据节点都周期性地向名字节点发送心跳数据包,当网络出现故障或者数据节点出现故障时,心跳信息无法发出,名字节点由此判断故障出现,此时名字节点会标记最近没有心跳的数据节点宕机,并不再向它们转发任何新的I/O请求,当数据节点宕机导致数据块复制因子低于指定位时,名字节点会复制这些数据块。
(3)集群重配平 当某个数据节点的剩余磁盘空间小于极限值时,HDFS自动将一部分数据从此数据节点移动到另一个节点,同样,当系统对某个文件的访问很大时,HDFS会动态增加该文件的复制数,以平衡集群的访问。
(4)数据完整性检查 HDFS客户端从数据节点读取数据后,将对数据进行校验和检查。
(5)元数据磁盘失效 为应对名字节点失效导致的系统故障,HDFS对名字节点的关键数据,如文件系统镜像和编辑日志进行了多份备份,以便在名字节点宕机时快速恢复到其他机器。
可见,HDFS采用了多项技术支持文件的可靠存储,这些技术在一定程度上牺牲了磁盘空间和访问效率,但对于保证系统的可靠性而言,这种牺牲是值得的。
3.2.3 键值存储系统技术
键值存储系统的目的就是存储海量半结构化和非结构化数据,应对数据量和用户规模的不断扩展。对于传统的关系数据库存储系统来说,这种目标是可望而不可即的。键值存储系统的目标并不是最终取代关系数据库系统,而是弥补关系数据库系统的不足。
3.2.3.1 键值存储系统的特点
在互联网飞速发展的今天,键值存储系统和关系数据库系统将共存。虽然都是管理数据,但键值存储系统和关系数据库系统是完全不同的。
①关系数据库系统中,数据库包含表,表包含行和列,行由各个列的数据值组成,在一个表中的行都拥有相同的策略。而在键值存储系统中,并不包含策略和关系数据库那样的表。键值存储系统一般包含域或桶,各个域或桶中包含若干条数据记录。
②关系数据库拥有良好的数据模型定义,包含策略、表的关系、事物等机制。数据之间的关系式建立在数据本身基础上的,而不是上层应用的功能和需要。键值存储系统中,数据记录只是简单地通过一个标识来识别和获取,数据之间没有关系的概念。
③关系数据库的目的是提高数据共享能力和减少数据冗余,键值存储系统一般需要进行数据冗余保证可靠性。
④关系数据库适合于存储传统数据,如字符、数字的存储和查询。键值存储系统适合于海量的非关系型数据的存储和查询。
总而言之,键值存储系统和关系数据库系统从根本上是不同的,键值存储系统在需要可扩展性的系统中和需要进行海量非关系数据查询和处理的环境中拥有明显优势。当前,键值存储系统在以下几方面的效果优于关系数据库系统。
(1)键值存储系统是云计算模式的最佳搭档 云计算模式就是需要灵活地应对用户对可伸缩性的需求。键值存储系统的可伸缩性的特点正好满足了用户的需求。如果试图把规模庞大的系统伸缩需求交给数十个甚至上百台服务器去处理,那么键值存储系统应该是一个比较好的解决方案。
(2)键值存储系统提供了相对廉价的存储平台,并拥有巨大的扩充潜力 用户通常只需根据自己的规模进行相应的配置即可,当需求增长时配额也能随之增加。同时,键值存储系统一般运行在便宜的PC服务器集群上,避免了购买高性能服务器的昂贵开销。
与关系数据库相比,键值存储系统也在一些传统的数据处理上存在明显的不足。例如,关系数据库的约束性保证数据在最低层次拥有完整性,违反完整性约束的数据是不可能存在于关系数据库系统中的,而键值存储系统一般都不同程度地放宽了对一致性和完整性约束的要求。键值存储系统不存在这些约束,使得程序员不得不承担起确保数据完整性的重要责任。然而在实际过程中,程序员经常会犯错误,使得系统出现一些Bug,这很可能引起数据完整性问题。其次,各种键值存储系统之间并没有像关系数据库的标准查询语言一样的标准接口,所以兼容性问题也是键值存储系统需要面临的一个重要挑战。
3.2.3.2 键值存储系统的分类
由于互联网快速发展对非关系型数据处理的需要越来越强烈,业界和学术界对键值存储系统的研究投入很大,现在已经出现了多种开源系统和商业产品,表3-2列出了这些典型系统。这些系统的设计思路主要是满足自身应用所需要的功能和要求或者是模仿现有的一些系统进行开源实现。
表3-2 当前主要键值存储系统及其特点
最典型的系统是谷歌公司开发和实现的Bigtable系统和亚马逊公司开发和实现的Dynamo系统。其他的许多系统都以这两个系统作为蓝本进行研究和设计,并设计出了适合其自身需要的系统。表3-2中的这些键值存储系统从系统架构和数据模型上可以分为以下三种。
(1)类Bigtable系统 如Hypertable、Hbase等都是Bigtable的开源实现。这类系统在架构上都实现了文件存储和数据管理的分层模型。Bigtable包含一个文件存储系统GFS,并在GFS的基础上构建了Bigtable系统,Bigtable系统只是管理数据逻辑并不关心数据的具体存储。这类系统将数据存储和数据的描述、处理分在了两个逻辑层次,并且具有较为完备的数据模型,也是和传统关系数据库最接近的系统。同时,由于这类系统将文件的存储和数据的管理分层,这使得系统的容量具有较好的可扩展性。上层的数据管理系统实现较简单。
(2)类Dynamo系统 Dynomite、Project Voldemort等都采用了和Dynamo差不多的环架构,这种环架构与Chord等这些DHT的环有所不同,在这些系统中,各个节点之间基本上都是全相关的,所以不存在漫长的路由过程。此外,这类键值存储系统的数据模型比较简单,与类Bigtable系统相比,这类系统只是提供了最基本的数据访问方式。
(3)内存数据库系统 严格上讲,这类系统应该看成是缓存系统,主要提供了快速的查询响应,如Memcache DB。这类系统不能提供数据的持久存储功能。
从键值存储系统设计目标来看,又可以把这些系统分为三类。
(1)强调读写性能的键值存储 典型代表是Redis、Tokyo Cabinet等。这类系统的设计是以速度或快速响应时间为目标的。一般来讲,这类系统都有较高的系统吞吐率。但是这类系统的可扩展性、存储容量等存在一些限制,比较适合做缓存系统,其应用范围一般比较狭小。
(2)强调对海量非关系数据的存储能力的键值存储 Mongo DB、Couch DB是这种类型的典型代表。这类键值存储系统解决的是非关系海量数据的存储和比较良好的查询性能,目的并不是强调快速的读写性能。这类系统的读写性能不高,但是系统的存储容量的扩展性很好。
(3)强调高可扩展性和可用性并面向分布式计算领域的键值存储 如Cassandra、Project Voldemort、Bigtable。这类键值存储系统一般是具有很强可扩展性的分布式系统,具有很强的可扩展能力。例如,可以动态地增加、删除数据节点等。Cassandra常常被看成是一个开源版本的Bigtable,其具有Bigtable的数据模型和Dynamo的环架构。
从功能上来看,键值存储系统又可以被分为支持简单的键值查询功能的系统和具有复杂功能的系统。类Bigtable系统或者使用了与Bigtable类似的数据模型的系统一般都更接近传统的数据库系统,他们提供了更多的操作接口。类Dynamo系统只是提供了键到值的简单访问,提供的功能有限,将复杂的数据处理功能交给了上层应用程序。