`
forchenyun
  • 浏览: 312258 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

海量数据存储之Key-Value存储简介

阅读更多

Key-value存储简介

具备高可靠性及可扩展性的海量数据存储对互联网公司来说是一个巨大的挑战,传统的数据库往往很难满足该需求,并且很多时候对于特定的系统绝大部分的检索都是基于主键的的查询,在这种情况下使用关系型数据库将使得效率低下,并且扩展也将成为未来很大的难题。在这样的情况下,使用Key-value存储将会是一个很好的选择。

它被广泛应用于缓存,搜索引擎等等领域。

         根据以上的描述,一个好的key-value存储需要满足哪些条件呢?

l  Availability可用性

l  Scalability可扩展性

l  Failover故障恢复

l  Performance高性能

简单来说,就是数据不能丢失,服务不能中断,能对故障进行感知并能自动恢复,读写性能极高。

文件存储

这一部分比较大,以后会另开主题写

单文件还是多文件

不少nosql的产品采用的是单文件存储的,数据量大以后肯定会遇到性能瓶颈,这一点无需多说,我想强调的是,采用多文件存储数据优点还是非常多的,不过也需要注意,操作系统对于能够打开的文件数目是由限制的,貌似Linux好像是1024(待确认),

Only Append

为了支持更快的写操作,数据文件的写操作只支持append,这个就不多说了,相信大部分的海量存储设计都是这样的。因此,更新操作等价于写操作,不过在写的时候第一步判断写到树的哪个位置时肯定会定位到树已有的节点上,这样可以使得这次写失效或者直接覆盖。

这样存在一个问题,就是对于失效的数据(比如更新过的数据)如何处理,比较好的办法是启动独立线程定时或手动进行清理,请注意,这是一个非常巨大的过程,它将耗光你的CPUI/O,因为要进行频繁计算和数据迁移。

数据结构

B Tree家族这一数据结构被广泛的运用于数据库索引,如MssqlB+treeoracleB-tree,熟悉索引的朋友一定很清楚,这种数据结构非常适合作为我们的Key-value存储的数据结构.关于B+tree,可以参见下图:它是一个多路搜索树, 数据存储在叶子节点上,非叶子节点作为叶子节点的索引,加速数据的查找,而叶子节点是一个有序的链表,每次搜索都会到达叶子节点才会结束,插入新数据可能会引起节点的分裂。

在本篇文章中,你需要知道,上层的节点成为INInternal Node,它持有其他节点的引用,叶子节点的上层是(Bottom Internal Node),而叶子节点则是存储数据的节点。



  

图片来自:http://blog.csdn.net/manesking/archive/2007/02/09/1505979.aspx

这部分是纯粹的数据结构,就不多说了,如果想深入了解的话可以看看这篇论文《The Ubiquitous B-Tree

设计要点

Partition

因为系统要具备高扩展性,因此,增加删除机器是频繁的操作,如何将数据均匀分散到集群中呢?比较常用的办法是hash取模的办法,但是这样一来,增加机器的瞬间,按照之前的hash取模方式,数据无法读取,这意味着需要对数据进行迁移,等待机器预热,这是很不好的办法。

目前比较公认的解决办法就是一致性哈希(consistent hashing)


首先按照机器的hash进行顺时针分布,如图,目前有5台机器,如果有一个读写请求,那么hashkey值得的一个hash值,定位到环上,如果没有定位到具体的机器,那么按照顺时针查找,找到的第一个机器就是目标节点。

如果需要新增机器,增加过程为,首先hash新机器得到其位置,加入新机器F,这时访问策略不变,那么按照之前的策略,如果hashC-F之间的数据就无法找到了,但是这样一来影响就局限于C-F之间,不象之前需要整体迁移了。


最后,为了降低增加机器所带来的影响,我们可以为其增加虚拟节点(virtual nodes)。这样的话服务器在环上的分布就比较均匀,这样多个虚拟节点将对应一个我们的物理节点,增加机器所受到的影响也会变得最小。

Replication

为了达到高可用性和数据不丢失,我们需要通过复制将数据备份到多台机器,replication的实现机制一般是通过Masterreplica之间的TCP/IP连接,然后根据相应的一致性策略将数据分发到replica上,这里的一致性策略主要包括两项:

1.         replica能够延迟master的时间,这个的意思就是说,在这个时间内更新的数据,replica可能是看不到的。例如你设置的一致性时间是3s,那么在某个特定的时刻,replica上的数据实际上可能是master3s以前的snapshot

2.         master事务提交返回之前是否需要得到replica的确认。为了尽量保证数据不丢失,master需要得到一定数量的replica确认数据更新成功之后才能提交事务。

关于数据可靠性和性能之间,是需要进行折衷的,很显然,越是高的数据保障,那么性能肯定会受到影响。在这样的情况下,需要对上层的应用进行分析,看是否允许丢失一部分数据。

另外,还有一个问题就是,数据的同步是采用master分发还是replica定时请求的问题,两者各有优缺点,前者会在replica较多的情况下遇到瓶颈,而后者可能会有一些延迟。多级同步的方式能在一定程度上解决这个问题,即master向某些机器同步,而这些机器向其他机器同步。

当然,master管理写请求而replica管理读请求,至于如何决定读写请求的分发,我们可以使用monitor节点,由它来作为读写的入口, 如下图,然后Monitor管理集群的状态和生命周期,例如Master fail后,monitor将收到事件,它将发起一次选举选出新的Master,一般的选举算法就是在集群中寻找最后一次更新的节点,因为往往它的数据是最新。还有就是在有新的机器加入集群的情况下,Monitor会告诉新机器集群内的master是谁,replica机器才能与master取得连接同步数据。


<!--[if gte mso 9]><xml> <o:OLEObject Type="Embed" ProgID="Visio.Drawing.11" ShapeID="_x0000_i1025" DrawAspect="Content" ObjectID="_1344072934"> </o:OLEObject> </xml><![endif]-->

使用MonitorMaster-replica集群

 

当然,自从有了ZooKeeper,这种监控和协同的脏活累活就可以都交给它了,利用ZooKeeper管理集群内节点的健康状况具备很大的便利,毕竟,上面这个办法的架构存在单点问题,最后肯定Monitor会拖集群的后腿。所以用ZooKeeper管理集群并且针对不同的事件作出响应。下面是一个示意图:


<!--[if gte mso 9]><xml> <o:OLEObject Type="Embed" ProgID="Visio.Drawing.11" ShapeID="_x0000_i1026" DrawAspect="Content" ObjectID="_1344072935"> </o:OLEObject> </xml><![endif]-->

ZooKeeper管理集群

最后,关于事务提交时的处理策略也值得注意,你需要为masterreplica都指定.一般情况下,我们需要在减少频繁的I/O操作和数据保障性方面进行折衷,以下提交策略可选:

1.         在事务提交时将数据由内存刷到硬盘,这样数据具有最高的保障,但是你需要等待昂贵的I/O操作。

2.         在事务提交时将数据刷到操作系统buffer中,然后由操作系统自己决定何时刷到硬盘。这样可以在虚拟机挂了的情况下保证数据不丢失,但是不能hardware failture

3.         在事务提交时数据仍然维持在内存中,而在存储系统比较闲的时候进行持久到硬盘的操作,或在内存不够用的情况下进行写磁盘操作。

GetPut操作

这两个动作是key-value存储系统最核心的两个操作,因此在设计上需要做更多的考虑。

下面是一种写操作的过程:

1.         执行tree搜索以定位插入数据所在的位置

2.         锁定该位置的父节点

3.         创建新的叶子节点

4.         将叶子节点写入。这个写操作发生在内存中,并且返回一个number它决定了叶子节点写到硬盘的位置

5.         修改父节点指向该叶子节点的引用。该父节点既持有指向内存的叶子节点的引用又持有磁盘的位置的number

6.         标记该父节点为dirty(意味着内存中的版本没有出现在磁盘中)

7.         解锁该父节点

请注意,很明显,这个写操作完全发生在内存中,那么何时将数据同步到磁盘呢?这就是后面要讲的checkpoint。通过它定时的将内存中的脏数据写到硬盘。

读操作就简单很多了,搜索tree,定位到叶子节点,取出数据就行了,当然还有一些包括为缓存服务的操作就不细讲了(比如,为每个节点计数,每次对该节点的访问都使得其+1,这样在缓存evict的时候就很有用了)

上面所说的写操作在内存中并不会影响到读操作,因为,为了加快读操作,我们会在启动时预加载硬盘数据文件的内容到内存,由于只有叶子节点存储数据,因此我们需要根据加载的叶子节点还原整棵B+tree,毫无疑问这是一个耗时的操作,但是却是值得的。

数据模型

这块比较大,这里只重点讲一下以什么数据结构存取的问题。

首先需要解决的是,存储对象的问题,很显然,我们都有存取对象的需求,那么如何将对象转换为我们的底层存储格式呢?一般的办法有序列化,JsonXML之类,下面依次讲一下优缺点:

1.       序列化。可能是比较简单易实现的办法,但是空间占用过大

2.       JsonXML都差不多,存储格式比较可读一点,解析和转换比较方便,不过对于数据量大的情况还是不推荐。

3.       字符串或者字节数组。我们按照一定的约定将对象拼成字符串,或者一次将对象的属性写入到字节数组,读取时按照相同的顺序解析即可,比较好的办法是定义一个接口,然后由客户端去实现对象字符串之间转换顺序的方法。这个比较推荐。

还有一些序列化的工具值得推荐,比如hadoop下的avro

Checkpoint

和普通的关系型数据库一样,key-value也可以有自己的checkpoint,一般情况,checkpoint是为了减少数据恢复所需要的时间.在检查点到来时,按照之前的设计,它会将所有的dirty Internal Node写入log,这样会存在一个问题,大多数情况下,checkpoint会把整棵树写到log,解决问题的办法是我们采用增量的办法进行log,例如,如果有ab加入到某个父节点。那么此时如果进行checkpoint时我们需要首先写一个完整的IN引用,并且记录对其进行的操作,add aadd b,这些操作记录在一个list中,当list变得过大以后我们又重新写一个完整的IN节点。

过期数据清理

这一部分只针对按照顺序写并且仅append的情况,为了减少I/O操作,无效数据仅仅被标记为delete且删除内存中对应的树的叶节点而不进行物理的删除,那么长期下去,失效数据会很多,这时候需要进行清理,一般的策略就是,当失效数据在文件中所占比例达到一定程度以后,执行清除操作。

1.         首先根据预先存储的记录信息判断哪些文件需要进行清理操作。

2.         扫描文件,找到仍然active的数据,拷贝到新的文件,扫描完成后删除此文件。

请注意,在写操作密集的情况下,这会造成竞争,因此尽量在访问量少的情况下执行此操作。

另外,可以使用多线程来进行清理操作。当然还有很多策略可以在清理的时候使用,比如,缓存一个叶子节点的父节点,在锁住该父节点执行迁移操作的时候可以顺便扫描该父节点下的其他叶子节点。

Need More

复杂查询

人的欲望是无止境的…….除了基于主键的检索你可能还需要基于某个属性的检索,最好还能在多个属性上查询完了以后来个取交集,这个时候怎么办呢?

首先,我们有一个基于主键的key-value数据库,key存储的主键,而value存储的对象(物理存储为byte数组),那么假如我们想对对象的某个属性如name进行查询的话,那么我们可以再建一个数据库,key是待查询的字段,value是主键数据库的id

Primary Database

Key(ID)

Value(Object)

1

Byte[]

2

Byte[]

Secondary Database

Foreign Key(Name)

Value(ID)

dahuang

2

tugou

1

这样一来按照name查询只需要查询一次secondary database取出主键id,再查一查primary database即可,这有点像正排索引和倒排索引的概率,如果对于多个字段的组合查询,只需要对其进行一次join即可,在join的时候可以先对待join的结果按照结果集大小进行排序,这样可以省下不少时间消耗。

虽然key-value存储按照上面的描述也可以支持多条件查询,但是不建议这样做,一是建立索引(二级数据库)需要额外的空间,二是这样需要多次查询影响性能,不管怎么样适度的折衷吧。

最后不得不提一下,由于B+tree的数据结构,它很好地支持范围查询(查询可以不下到叶子节点)可以极大的弥补搜索引擎中倒排索引进行范围查询需要全部扫描的缺陷。这也是其应用场景之一。

总结

Key-value在海量数据存储中占据很重要的地位,对于它的深入研究能带给我们很多启发,而它在某些局部问题上所表现的优秀的能力也值得我们关注。本文大致总结了一下目前所了解的一些问题,没有提到的东西还有很多(文件系统设计,事务,缓存等等),接下来如果有空会对文件系统设计进行详细讲解。

声明

首先,本文只是代表本人的一些浅见,不代表任何官方意见。其次,由于作者水平的原因,肯定会出现错误,欢迎指正(最好站内,给我留一点脸面-_-

参考文献:

Dynamo: Amazon’s Highly Available Key-value Store

http://blog.csdn.net/manesking/archive/2007/02/09/1505979.aspx (BTree,B-Tree,B+Tree,B*Tree都是什么)

  • 大小: 38.7 KB
  • 大小: 11.8 KB
  • 大小: 12.2 KB
  • 大小: 9.8 KB
  • 大小: 16.7 KB
47
3
分享到:
评论
21 楼 genius0101001 2012-06-18  
写得不错
20 楼 lavafree 2011-10-28  
楼主的整体设计下来,感觉非常像mongodb的设计。以主键为key,还有就是对某一个字段作索引。
19 楼 红五月 2011-03-28  
18 楼 forchenyun 2011-03-28  
54yj 写道
forchenyun 写道
54yj 写道
forchenyun 写道
54yj 写道
对b树结构的删除修改导致的老化,oracle也有一些策略,包括空间的重用策略、删除合并的时间选择等。

这一点能详细分享吗

这篇文章是否有点价值:
http://space.itpub.net/?uid-9842-action-viewspace-itemid-324586

tks,关于数据库级别的优化请多多分享

楼主的综合能力远在我之上,真的非常钦佩,目标也很远大,加油呀,给你鼓掌。
关于数据库内部的数据结构和一些算法,ali有好多高手,比如biti_rainy。
俗话说三人行必有我师呀,不知道有没有搞类似“同行评审”的活动。

有空可以组织一下,呵呵
17 楼 54yj 2011-03-27  
forchenyun 写道
54yj 写道
forchenyun 写道
54yj 写道
对b树结构的删除修改导致的老化,oracle也有一些策略,包括空间的重用策略、删除合并的时间选择等。

这一点能详细分享吗

这篇文章是否有点价值:
http://space.itpub.net/?uid-9842-action-viewspace-itemid-324586

tks,关于数据库级别的优化请多多分享

楼主的综合能力远在我之上,真的非常钦佩,目标也很远大,加油呀,给你鼓掌。
关于数据库内部的数据结构和一些算法,ali有好多高手,比如biti_rainy。
俗话说三人行必有我师呀,不知道有没有搞类似“同行评审”的活动。
16 楼 honglove 2011-03-26  
写的非常好,学习了
15 楼 forchenyun 2011-03-25  
54yj 写道
forchenyun 写道
54yj 写道
对b树结构的删除修改导致的老化,oracle也有一些策略,包括空间的重用策略、删除合并的时间选择等。

这一点能详细分享吗

这篇文章是否有点价值:
http://space.itpub.net/?uid-9842-action-viewspace-itemid-324586

tks,关于数据库级别的优化请多多分享
14 楼 54yj 2011-03-25  
forchenyun 写道
54yj 写道
对b树结构的删除修改导致的老化,oracle也有一些策略,包括空间的重用策略、删除合并的时间选择等。

这一点能详细分享吗

这篇文章是否有点价值:
http://space.itpub.net/?uid-9842-action-viewspace-itemid-324586
13 楼 forchenyun 2011-03-25  
54yj 写道
对b树结构的删除修改导致的老化,oracle也有一些策略,包括空间的重用策略、删除合并的时间选择等。

这一点能详细分享吗
12 楼 54yj 2011-03-25  
对b树结构的删除修改导致的老化,oracle也有一些策略,包括空间的重用策略、删除合并的时间选择等。
11 楼 54yj 2011-03-25  
[在join的时候可以先对待join的结果按照结果集大小进行排序,这样可以省下不少时间消耗]这个是sort merge join,表的连接方式还有hash jion和nested loop。oracle语句执行计划里面可以看到这些。
10 楼 shuiguozheng 2010-08-26  
牛人哪,回家细读
9 楼 gh_aiyz 2010-08-26  
文件句柄默认最大值1024,但通过设置可以最大支持65535
可修改系统内核参数
或者在系统启动脚本中加入这么一句:
ulimit -n 65535
仅对当前进程生效

每个文件、每个网络连接,都占用文件句柄
8 楼 forchenyun 2010-08-25  
fujohnwang 写道
那个文件句柄,我记得默认是1024,但你好像可以通过修改内核参数重新编译来增大这个数值。

tks
7 楼 fujohnwang 2010-08-25  
那个文件句柄,我记得默认是1024,但你好像可以通过修改内核参数重新编译来增大这个数值。
6 楼 liuqing9382 2010-08-24  
写的真不错,分布式储存很需要这种非关系型数据库模型,现在在这种海量数据处理上,这种key-value类型的数据库被炒的很火,像国外的google,amazon,facebook之类的都在使用类似的技术,国内的淘宝,百度也在做,希望楼主下次能发表更精彩的文章。
5 楼 kokolodo 2010-08-24  
嗯,看得不是特别的懂哦!还需再详细的看看
4 楼 sandaobusi 2010-08-24  
写得很好,以后想做这方面的工作,继续支持和关注lz!
3 楼 lyw985 2010-08-23  
不知所云
2 楼 henry19890701 2010-08-23  
顶一个先!

相关推荐

    分布式KEY-VALUE存储设计方案.docx

    分布式Key-Value存储系统通常采用简单的键值对作为数据模型,其中“键”用于唯一标识数据,“值”则是实际存储的数据。这种数据模型简单、灵活,非常适合大规模数据的存储和检索。 ##### 3.2 系统架构 一个典型的...

    基于HDFS的,分布式的key-value store.zip

    在分析这个主题时,我们将深入探讨HDFS的基本原理,它在大数据处理中的角色,以及如何将其与key-value存储模型相结合,以适应人工智能(AI)的工作负载需求。 HDFS是Apache Hadoop项目的一部分,设计用于在大规模...

    分布式key-value键值数据库与关系数据库-NoSql收集.pdf

    NoSQL,尤其是Key-Value存储,强调的是高度分布式、水平扩展的架构。在Key-Value数据库中,数据以键-值对的形式存储,查找速度快,适合处理大量简单的读写操作。这种模式特别适用于高并发的互联网应用,如缓存、...

    分布式key-value系统错误污染检测.pdf

    Voldemort是一个开源的、高可用性的key-value存储系统,适用于需要大规模数据分布存储和快速访问的场景。通过在这样的系统上实现TrackerStore,可以展示出检测系统对于读延迟的影响,这对于评价系统的性能至关重要。...

    第9章互联网分布式系统的数据资源存储与管理KeyValue存储模式.ppt

    ### 第9章 互联网分布式系统的数据资源存储与管理——KeyValue存储模式 #### 9.1 引言 在互联网时代,数据的爆炸性增长给数据管理和存储带来了前所未有的挑战。本章将探讨互联网数据资源存储与管理的关键问题,...

    基于分布式系统的海量数据存储技术.pdf

    BigTable的数据模型建立在稀疏的、分布式的、持久化存储的多维Map上,通过键值对(key-value)结构存储数据,它支持高效的数据读写和管理。 另外,文章还提到了NFS(网络文件系统),这是一个在各种不同类型的...

    HBase存储海量图片

    海量图片的存储是通过HBase实现的,HBase是一种面向列的NoSQL数据库,特别适合存储海量数据。 一、直接上传本地栅格数据将导致的问题 栅格数据的特点是每层的图片个数都为上层数量的四倍。在第20层时,仅仅第20层...

    二、大数据与分布式.pdf

    1. 基于Key-Value存储的NoSQL数据库 这种数据库利用键值对进行存储,通过哈希表维护Key和Value的映射,用户可以通过Key快速定位数据。Value通常以特定的数据结构存储,系统不对Value进行解释,应用程序根据预先约定...

    hadoop海量数据处理详解与项目实战

    Map阶段处理输入数据,并将它们转化为一系列中间的键值对(key-value pairs),Reduce阶段则对这些中间键值对进行汇总处理,输出最终结果。 3. **Hive** Hive是一个建立在Hadoop之上的数据仓库工具,能够将结构化...

    redis实战 pdf

    - **大规模的互联网应用**:随着互联网应用规模的不断扩大,传统的关系型数据库在处理大规模数据时面临着性能瓶颈,而Key-Value存储系统能够更好地应对这种挑战。 - **云存储**:在云计算环境中,Key-Value存储系统...

    7-3.超大规模时空数据的分布式存储与应用.pdf

    MongoDB作为分布式Key-Value数据库,适用于存储切片和缓存数据;Elasticsearch适用于实时数据存储和管理流式点数据;HDFS(Hadoop分布式文件系统)适合存储超大和超多CVS文件,进行分布式计算;HBase则以其毫秒级...

    一种基于NoSQL 的地图瓦片数据存储技术

    本文介绍了一种基于 NoSQL 的地图瓦片数据存储技术,旨在解决传统关系型数据库在海量空间数据存储和并发访问性能上的不足之处。通过对 NoSQL 的概述和与关系型数据库的比较,我们可以了解到 NoSQL 在数据存储和访问...

    基于NoSQL的机载LiDAR海量点云数据分布式存储.pdf

    本文利用HBase实现了键值(Key-Value)对和表结构(Table Structure)的优化设计,以达到海量点云数据高效存储和快速查询的目的。 4. 线性八叉树与虚拟格网结合:八叉树是一种空间分割的数据结构,适用于3D数据的...

    99%的海量数据处理面试题

    这类问题通常涉及到存储、处理和操作大量数据,其中“海量”意味着数据量过大,以至于无法在短时间内直接处理或者无法全部加载到内存中。 解决海量数据处理的时间和空间问题,通常采用的方法有: 1. **算法与数据...

    《Redis实战》

    #### 一、Key-Value存储系统简介与Redis的选择 **1.1 Key-Value存储系统简介** - **Voldemort**: 一个分布式键值存储系统,最初由LinkedIn开发,用于处理大规模数据读写操作。它强调一致性、高可用性和可扩展性。 ...

    50丨索引:如何在海量数据中快速查找某个数据?1

    Redis作为内存数据库,其数据结构设计通常更为简洁,如哈希表(Hash Table)用于实现Key-Value存储,提供O(1)的时间复杂度进行查找。对于索引需求,Redis可能会利用跳跃表(Skip List)来实现,它在内存中提供了类似...

    架构师系列书籍--Redis实战

    #### 一、Key-Value存储系统简介 ##### 1.1.1 Voldemort - **简介**:Voldemort是一款分布式Key-Value存储系统,由LinkedIn开发并开源。 - **特点**: - 支持分区和副本机制,提供高可用性。 - 具有容错能力,...

    Hadoop的MapReduce执行过程介绍.pdf

    在Map阶段完成后,所有相同key的value会被聚集在一起,形成一个新的key-value对列表,这是通过Partitioner完成的,它负责将数据分发到不同的Reduce任务。然后,Reducer函数接收这些列表,对每个key的所有value进行...

Global site tag (gtag.js) - Google Analytics