吞噬大数据存储领域新机制——NoSQL模式解析

在过去几年,一种新兴的大型数据存储机制正吞噬大数据存储市场。这种存储解决方案与传统的RDBMS有显著的区别,它们被称之为NoSQL。

在NoSQL世界中有以下关键的成员,包括

●GoogleBigTable、HBase、Hypertable

●AmazonDynamo、Voldemort、Cassendra、Riak

●Redis

●CouchDB、MongoDB

而这些解决方案又有一些共同的特点

●基于键-值存储

●系统运行在海量的普通机器上

●数据在经过分区和复制后分布在集群中

●放宽对数据一致性的要求(因为CAP定理)。

选择NoSQL的重要标准就是要看CAP(Consistency、Availability和PartitionTolerance),也就是我们所说的一致性、可用性和分区容忍性。但CAP原则要求在分布式系统只能选择一致性、可用性和分区容忍性其中的两项。

本文旨在提取这些解决方案背后的共同的技术,以便更深入的了解应用程序设计的意义。本文并不会对这些解决方案作比较,也不会建议使用某一款产品。

API模型

底层的数据模型可以被看作为一个大的Hashtable(键/值存储)

API访问基本形式:

get(key):提取给定键的对应值

put(key,value):新建或更新给定键的对应值

delete(key):删除键及其关联值

在服务器环境利用更高级的API执行用户自定义的函数

execute(key,operation,parameters):调用操作给定键对应的值,值具有特殊数据(例如List、Set、Map等)

mapreduce(keyList,mapFunc,reduceFunc):对范围内的键调用MapReduce

机器布局

底层基础设施由大量(成百上千)廉价的、普通的、不可靠的机器通过网络组成。每台机器为一个单独的物理节点(PN)。在每个PN软件配置相同,但CPU、内存、硬盘等会有所不同。每个PN根据不同的硬件配置运行不同数目的虚拟节点。

数据分区

由于整个Hashtable分布在众多VN之中,我们需要一种能够将每个键映射到对应VN的方法。一种行之有效的方法是:partition=keymod(total_VNs)。这种方案的缺点是当我们改变VN的数量时,现有键的所有权将发生巨大的变化,这导致全部的数据需要重新再分配。所以多数大型存储使用被称为“consistenthashing”的技术将键所有权变更的影响最小化。

在“consistenthashing”方法中,键空间是有限的,且分布在一个环上。虚拟节点的ID也从相同的键空间中分配。对于任意键,如果从该键沿着环顺时针移动,遇到了第一个虚拟节点就是它的所属节点。当某一节点崩溃时,他所属的所有键都将顺时针的移动到相邻的节点。因此,重新分配键的情况只发生在崩溃节点的相邻节点中,而所有其他的节点仍保持原有的键值。

数据复制

为了从单个并不可靠的资源来提供更高的可靠性,迫使我们需要复制数据分区。

复制不仅提高了数据的可靠性,同时将工作负载分布到多个副本还有助于提升性能。

只读请求可以分发到任何副本,而更新的要求却具有挑战性。因为需要任职协调各个副本的更新。

成员变动

请注意,虚拟节点可以随时加入和离开而不影响环的运作网络。

当心节点加入到网络

1.新加入的节点将示意自身的存在,并将其ID通知其他重要的节点。

2.所有相邻(左边或右边)节点将调整键的所有权以及副本成员的信息,这将通过同步完成。

3.新加入的节点开始从其相邻的节点并行、异步的批量复制数据。

4.副本成员的变更异步传播到其他节点

当现有节点脱离网络时(例如崩溃)

1.崩溃的节点不再回应Gossip消息,因此相邻的节点也会得知此情况。

2.相邻节点更新成员信息,同时异步复制数据。

客户端的一致性

一旦我们拥有相同数据的多个副本,就必须考虑如何同步他们,这样在客户端看来数据才能使一致的。

1.严格的一致性:从语义上看相当只存在一个数据副本,任何更新看上去都是实时发生的。

2.读取已写内容的一致性:允许客户端立刻看到自身所做的更新,但无法看到其他客户端的更新

3.会话一致性:对于客户端在同一会话作用域发出的请求,提供读取已写内容一致性。

4.单调读一致性:保证时间的单调性,保证客户端在未来的请求中只读区比当前更新的数据。

5.最终一致性:提供最低限度的保证。在更新过程中客户端将看到不一致的视图。当并发访问同一数据几率非常小的时候,此模型效果可以得到保证。

同时取决采用何种一致性模型需要安排两种机制

●客户端请求如何让分发到副本

●副本如何传播以及应用更新

围绕着如何实现这两方面。出现了许多模型,各有不同的权衡取舍。

矢量时钟

矢量时钟是一种时间戳机制,透过矢量时钟我们可以推到更新之前的因果关系。首先,每个副本都持有矢量时钟。假设副本i的时钟是Vi。Vi[i]是副本根据特定规则更新矢量时钟之后的逻辑时钟。

●当副本i执行了一则内部操作,副本i的时钟加1。

●当副本i向副本j发送一则消息,副本i首先把自己的时钟Vi[i]加1,并将自己的矢量时钟Vi附加到消息中发送出去。

●当副本j收到来自副本i的消息,首先自增其时钟Vj[j],然后合并其时钟及消息所附的时钟Vm。即Vj[k]=max(Vj[k],Vm[k])。

于是可以定义偏序关系,Vi>Vj,当且仅当对于所有的k,Vi[k]>=Vj[k]。根据这样的偏序关系,我们就可以推导出更新之间的因果关系。其背后原理如下

●内部操作的效果可在同一节点上立即看到。

●接到消息之后,接收节点得知发送节点在消息发送之时的情况。情况不仅包括发送节点上发生的事情,还包括发送节点所知的所有其他节点上发生的事情。

●Vi[i]反映了节点i上发生最后一次内部操作的时间。Vi[k]=6意味着副本i已经知道副本k在他的逻辑时钟6时刻的情况。

MapReduce的执行过程

分布式存储架构同样适合分布式的处理,例如对一个键列表执行Map/Reduce操作情况。

系统将Map和Reduce函数推送给全部的节点。Map函数分布到键所属的各个副本上处理,然后Map函数的输出被转交给Reduce函数去执行聚合操作。

对删除的处理

在多主复制系统中,用矢量时钟时间戳去判定因果序,需要非常小心处理“删除”的情况。以免丢失掉删除对象关联的时间戳信息,否则根本无法推到何时执行删除。因此,通常需要将删除当作一种特殊的更新来处理,把对象标记为删除,但仍然保留元数据、时间戳信息。当经过足够长的时间,并确信所有节点都已经对该对象标记为删除之后,才能通过垃圾回收已删除对象的空间

相关推荐