精华内容
下载资源
问答
  • 表格存储Tablestore是阿里云自研的面向海量结构化数据存储的Serverless NoSQL多模型数据库。Tablestore在阿里云官网上有各种文档介绍,也发布了很多场景案例文章,这些文章收录在这个合集中《表格存储Tablestore权威...

    前言

    表格存储Tablestore是阿里云自研的面向海量结构化数据存储的Serverless NoSQL多模型数据库。Tablestore在阿里云官网上有各种文档介绍,也发布了很多场景案例文章,这些文章收录在这个合集中
    《表格存储Tablestore权威指南》。值得一提的是,Tablestore可以支撑海量的数据规模,也提供了多种索引来支持丰富的查询模式,同时作为一个多模型数据库,提供了多种模型的抽象和特有接口。本文主要对Tablestore的存储和索引引擎进行介绍和解读,让大家对Tablestore引擎层的原理和能力,索引的作用和使用方式等有一个认识。

    基本架构

    Tablestore是一款云上的Serverless的分布式NoSQL多模型数据库,提供了丰富的功能。假设用户可以采用各种开源组件搭建一套类似服务,可以说是成本非常高昂,而使用Tablestore仅需在控制台上创建一个实例即可享受全部功能,而且是完全按量计费,可以说是0门槛。

    整体架构如下图所示,本文不展开叙述每个模块的功能。

    在服务端引擎层中,存在两个引擎:存储引擎和索引引擎。这两个引擎的数据结构和原理不同,为了方便读者理解,本文将这两个引擎称为表引擎(Table)和多元索引引擎(Searchindex)。整体来说,引擎层是基于LSM架构和共享存储(盘古),支持自动的Sharding和存储计算分离。

    表引擎

    表引擎的整体架构类似于Google的BigTable,在开源领域的实现有HBase等。

    数据模型可以定义为宽行模型,如下图所示。其中不同的分区可以加载到不同的机器上,实现水平扩展:

    首先说明一下为什么Tablestore的主键可以包含多个主键列,而像HBase只有一个RowKey。这里有几点:

    1. 多列主键列按照顺序共同构成一个主键,类似MySQL的联合主键。如果使用过HBase,可以把这里的多列主键列,拼接起来看作一个RowKey,每一列其实都只是整体主键的一部分。
    2. 第一列主键列是分区键,使用分区键的范围进行分区划分,保证了分区键相同的行,一定在同一个分区(Partition)上。一些功能依赖这一特性,比如分区内事务(Transection),本地二级索引(LocalIndex, 待发布),分区内自增列等。
    3. 业务上常需要多个字段来构成主键,如果只支持一个主键列,业务需要进行拼接,多列主键列避免了业务层做主键拼接和拆解
    4. 许多用户第一次看到多列主键列时,常会有误解,认为主键的范围查询(GetRange接口)可以针对每一列单独进行,实际上这里的主键范围指的是整体主键的范围,而非单独某一列的范围

    这个模型具有这样的一些优势:

    1. 完全水平扩展,因此可支撑的读写并发和数据规模几乎无上限。Tablestore线上也有一些业务在几千万级的tps/qps,以及10PB级的存储量。可以说一般业务达不到这样的上限,实际的上限仅取决于集群目前的机器资源,当业务数据量大量上涨时,只要增加机器资源即可。同时,基于共享存储的架构也很方便的实现了动态负载均衡,不需要数据库层进行副本数据复制。
    2. 提供了表模型,相比纯粹的KeyValue数据库而言,具有列和多版本的概念,可以单独对某列进行读写。表模型也是一种比较通用的模型,可以方便与其他系统进行数据模型映射。
    3. 表模型中,按照主键有序存储,而非Hash映射,因此支持主键的范围扫描。类似于HashMap与SortedMap的区别,这个模型中为SortedMap。
    4. Schema Free, 即每行可以有不同的属性列,数据列个数也不限制。这很适合存储半结构化的数据,同时业务在运行过程中,也可以进行任意的属性列变更。
    5. 支持数据自动过期和多版本。每列都可以存储多个版本的值,每个值会有一个版本号,同时也是一个时间戳,如果设置了数据自动过期,就会按照这个时间戳来判断数据是否过期,后台对过期数据自动清理。

    这个模型也有一些劣势:

    1. 数据查询依赖主键。可以把这个数据模型理解为SortedMap,大家知道,在SortedMap上只能做点查和顺/逆序扫描,比如以下查询方式:

      1. 主键点查:通过已知主键,精确读取表上的一行。
      2. 主键范围查:按照顺序从开始主键(StartPrimaryKey)扫描到结束主键(EndPrimaryKey),或者逆序扫描。即对Table进行顺序或逆序遍历,支持指定起始位置和结束位置。
      3. 主键前缀范围查:其实等价于主键范围查,这里只是说明,主键前缀的一个范围,其实可以转换成主键的一个范围,在表上进行顺序扫描即可。
    2. 针对属性列的查询需要使用Filter,Filter模式在过滤大量数据时效率不高,甚至变成全表扫描。通常来说,数据查询的效率与底层扫描的数据量正相关,而底层扫描的数据量取决于数据分布和结构。数据默认仅按照主键有序存储,那么要按照某一属性列查询,符合条件的数据必然分布于全表的范围内,需要扫描后筛选。全表数据越多,扫描的数据量也就越大,效率也就越低。

    那么在实际业务中,主键查询常常不能满足需求,而使用Filter在数据规模大的情况下效率很低,怎么解决这一问题呢?

    上面提到,数据查询的效率与底层扫描的数据量正相关,而Filter模式慢在符合条件的数据太分散,必须扫描大量的数据并从中筛选。那么解决这一问题也就有两种思路:

    1. 让符合条件的数据不再分散分布:使用全局二级索引,将某列或某几列作为二级索引的主键。相当于通过数据冗余,直接把符合条件的数据预先排在一起,查询时直接精确定位和扫描,效率极高。
    2. 加快筛选的速度: 使用多元索引,多元索引底层提供了倒排索引,BKD-Tree等数据结构。以上面查询某属性列值为例,我们给这一列建立多元索引后,就会给这一列的值建立倒排索引,倒排索引实际上记录了某个值对应的所有主键的集合,即Value -> List, 那么要查询属性列为某个Value的所有记录时,直接通过倒排索引获取所有符合条件的主键,进行读取即可。本质上是加快了从海量数据中筛选数据的效率。

    全局二级索引

    全局二级索引采用的仍然是表引擎,给主表建立了全局二级索引后,相当于多了一张索引表。这张索引表相当于给主表提供了另外一种排序的方式,即针对查询条件预先设计了一种数据分布,来加快数据查询的效率。索引的使用方式与主表类似,主要的查询方式仍然是上面讲的主键点查,主键范围查,主键前缀范围查。常见的关系型数据库的二级索引也是类似的原理。

    列举一个最简单的例子,比如我们有一张表存储文件的MD5和SHA1值,表结构如下:

    FilePath(主键列) MD5(属性列) SHA1(属性列)
    oss://abc/files/1.txt 0cc175b9c0f1b6a831c399e269772661 86f7e437faa5a7fce15d1ddcb9eaeaea377667b8
    oss://abc/files/2.txt 92eb5ffee6ae2fec3ad71c777531578f e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98
    oss://abc/files/3.txt 4a8a08f09d37b73795649038408b5f33 84a516841ba77a5b4648de2cd0dfcb30ea46dbb4

    通过这张表,我们可以查询文件对应的MD5和SHA1值,但是通过MD5或SHA1反查文件名却不容易。我们可以给这张表建立两张全局二级索引表,表结构分别为:

    索引1:

    MD5(主键列1) FilePath(主键列2)
    0cc175b9c0f1b6a831c399e269772661 oss://abc/files/1.txt
    4a8a08f09d37b73795649038408b5f33 oss://abc/files/3.txt
    92eb5ffee6ae2fec3ad71c777531578f oss://abc/files/2.txt

    索引2:

    SHA1(主键列1) FilePath(主键列2)
    84a516841ba77a5b4648de2cd0dfcb30ea46dbb4 oss://abc/files/3.txt
    86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 oss://abc/files/1.txt
    e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98 oss://abc/files/2.txt
       

    为了确保主键的唯一性,全局二级索引中,会将原主键的主键列也放到主键列中,比如上面的FilePath列。有了上面两张索引表,就可以通过主键前缀范围查的方式里精确定位某个MD5/SHA1对应的文件名了。

    多元索引引擎

    多元索引引擎相比于表引擎,底层增加了倒排索引,多维空间索引等,支持多条件组合查询、模糊查询、地理空间查询,以及全文索引等,还提供一些统计聚合能力(统计聚合功能待发布)。因为功能较单纯的二级索引更加丰富,而且一个索引就可以满足多种维度的查询,因此命名为多元索引。

    上面在讲解决Filter模式查询慢的问题时,提到倒排索引加快了数据筛选的速度,因为记录了某列的Value到符合条件的行的映射,Value -> List 。实际上,倒排索引这一方式,不仅可以解决单列值的检索问题,也可以解决多条件组合查询的问题。

    我们举一个订单场景的例子,比如下表为一个订单记录:

    订单号 订单(md5)(主键) 消费者编号 消费者姓名 售货员编号 售货员姓名 产品编号 产品名 产品品牌 产品类型 下单时间 支付时间 支付状态 产品单价 数量 总价钱
    o0000000000 c49f5fd5aba33159accae0d3ecd749a7 c0019 消陈九 s0020 售楚十 p0003004 vivo x21 vivo 手机 2018-07-17 21:00:00   2498.99 2 4997.98

    上面一共16个字段,我们希望按照任意多个字段组合查询,比如查询某一售货员、某一产品类型、单价在xx元之上的所有记录。可以想到,这样的排列组合会有非常多种,因此我们不太可能预先将任何一种查询条件的数据放到一起,来加快查询的效率,这需要建立很多的全局二级索引。而如果采用Filter模型,又很可能需要扫描全表,效率不高。折中的方式是,可以先对某个字段建立二级索引,缩小数据范围,再对其中数据进行Filter。那么有没有更好的方式呢?

    多元索引可以很好的解决这一问题,而且只需要建立一个多元索引,将所有可能查询的列加入到这个多元索引中即可,加入的顺序也没有要求。多元索引中的每一列默认都会建立倒排,倒排就记录了Value到List的映射。针对多列的多个条件,在每列的倒排表中找到对应的List,这个称为一个倒排链,而筛选符合多个条件的数据即为计算多个倒排链的交并集,这里底层有着大量的优化,可以高效的实现这一操作。因此多元索引在处理多条件组合查询方面效率很高。

    此外,多元索引还支持全文索引、模糊查询、地理空间查询等,以地理空间查询为例,多元索引通过底层的BKD-Tree结构,支持高效的查询一个地理多边形内的点,也支持按照地理位置排序、聚合统计等。

    索引选择

    不是一定需要索引

    1. 如果基于主键和主键范围查询的功能已经可以满足业务需求,那么不需要建立索引。
    2. 如果对某个范围内进行筛选,范围内数据量不大或者查询频率不高,可以使用Filter,不需要建立索引。
    3. 如果是某种复杂查询,执行频率较低,对延迟不敏感,可以考虑通过DLA(数据湖分析)服务访问Tablestore,使用SQL进行查询。

    全局二级索引还是多元索引

    1. 一个全局二级索引是一个索引表,类似于主表,其提供了另一种数据分布方式,或者认为是另一种主键排序方式。一个索引对应一种查询条件,预先将符合查询条件的数据排列在一起,查询效率很高。索引表可支撑的数据规模与主表相同,另一方面,全局二级索引的主键设计也同样需要考虑散列问题。
    2. 一个多元索引是一系列数据结构的组合,其中的每一列都支持建立倒排索引等结构,查询时可以按照其中任意一列进行排序。一个多元索引可以支持多种查询条件,不需要对不同查询条件建立多个多元索引。相比全局二级索引,也支持多条件组合查询、模糊查询、全文索引、地理位置查询等。多元索引本质上是通过各种数据结构加快了数据的筛选过程,功能非常丰富,但在数据按照某种固定顺序读取这种场景上,效率不如全局二级索引。多元索引的查询效率与倒排链长度等因素相关,即查询性能与整个表的全量数据规模有关,在数据规模达到百亿行以上时,建议使用RoutingKey对数据进行分片,查询时也通过指定RoutingKey查询来减少查询涉及到的数据量。简而言之,查询灵活度和数据规模不可兼得。

    关于使用多元索引还是全局二级索引,也有另外一篇文章描述:《Tablestore索引功能详解》

    除了全局二级索引之外,后续还会推出本地二级索引(LocalIndex),推出后再进行详细介绍。

    常见组合方案

    丰富的查询功能当然是业务都希望具备的,但是在数据规模很大的情况下,灵活的查询意味着成本。比如万亿行数据的规模,对于表引擎来说,因为水平扩展能力很强,成本也很低,问题不大,但是建立多元索引,费用就会非常高昂。全局二级索引成本较低,但是只适合固定维度的查询。

    常见的超大规模数据,都带有一些时间属性,比如大量设备产生的数据(监控数据),或者人产生的数据(消息、行为数据等),这类数据非常适合采用Tablestore存储。对这类数据建立索引,会有一些组合方案:

    1. 对元数据表建立多元索引,全量数据表不建立索引或采用全局二级索引。

      1. 元数据表可以是产生数据的主体表,比如设备信息表,用户信息表等。在时序模型中,产生数据的主体也可以认为是一个时间线,这条线会不断的产生新的点。
      2. Tablestore的时序数据模型(Timestream)采用的也是类似的方式,对时序数据中的时间线建立一张表,专门用来记录时间线的元数据,每个时间线一行。时间线表建立多元索引,用来做时间线检索,而全量数据则不建立索引。在检索到时间线后,对某个时间线下的数据进行范围扫描,来读取这个时间线的数据。
    2. 热数据建立多元索引,老数据不建立索引或者采用全局二级索引:

      1. 很多情况下仅需要对非常热的数据进行多种维度查询,对冷数据采取固定维度查询即可。因此冷热分离可以给业务提供更高的性价比。
      2. 目前多元索引还不支持TTL(后续会支持),需要业务层区分热数据和冷数据。

    总结

    本文对Tablestore的存储和索引引擎进行了介绍和解读,并在如何选择和应用索引方面给了一些参考,目的是加深大家对Tablestore的认识和理解,更好的应用Tablestore来解决业务需求。如果有疑问或需求,或者希望进一步技术探讨,欢迎大家加入Tablestore官方的钉钉技术交流群,群号11789671。


    原文链接
    本文为云栖社区原创内容,未经允许不得转载。

    展开全文
  • 结构化数据存储通常有两种方式:[/b] 1.将非结构化数据以文件的方式存储在文件系统中,同时将指向文件的链接或路径存储在数据库表中。这种方式数据读写的速度较快,但数据管理不方便,并需要额外考虑事务处理的...
    [b]非结构化数据包括文本、图像、音频、视频、PDF、电子表格等。非结构化数据存储通常有两种方式:[/b]

    1.将非结构化数据以文件的方式存储在文件系统中,同时将指向文件的链接或路径存储在数据库表中。这种方式数据读写的速度较快,但数据管理不方便,并需要额外考虑事务处理的一致性和数据的安全性。
    2.将非结构化数据存储在传统的数据库表的大对象字段中。这种方式充分利用数据库的事务、管理和安全特性,但在数据查询和读写的性能不高。

    为解决上面两种方式的缺点,利用其所长,最新的非结构化数据存储技术在磁盘格式、网络协议、空间管理、重做和撤销格式、缓冲区缓存以及智能的I/O子系统等方面发生重大转变,在保证了文件数据的性能的同时,还保留了数据库的优势。较有代表性的就是Oracle SecureFiles非结构化数据存储方式。
    展开全文
  • 结构化数据存储

    千次阅读 2009-06-22 14:09:00
    很多数据库应用都必须面临非结构化数据存储的问题,这个问题对整个系统往往都是非常关键的。因此我们需要一个适合的方案,折中地考虑了性能、安全和稳定性等因素。本文简单地阐述了以SQL SERVER和ORACLE为数据库管理...

    很多数据库应用都必须面临非结构化数据存储的问题,这个问题对整个系统往往都是非常关键的。因此我们需要一个适合的方案,折中地考虑了性能、安全和稳定性等因素。本文简单地阐述了以SQL SERVER和ORACLE为数据库管理系统的应用系统的实现方案(注:仅是个人观点)。

    其实不外乎两种实现方式:
          1. 使用文件系统存储文件,而在数据库中存储访问路径。这种方式的优点是实现简单,不需要DBMS的高级功能,但是这种方式无法实现文件的事务性访问,不便于数据备份和恢复,不便于数据迁移等;
          2. 使用DBMS的文件存储功能。比较常见的方式,如SQL SERVER中的FILESTREAM/BLOB,ORACLE中的BFILE/BLOB。这种方式克服了实现方式一的缺点,但是需要DBMS提供支持,也就是说这种方式的实现方式的控制权在DBMS厂商手中,DBMS实现的好则好,坏则坏。这种方式需要考虑的最重要的一点就是通过DBMS直接或间接地操作非结构化数据会不会生成过多的undo/redo,会不会影响整个系统的性能,会不会降低系统的并发程度等等。
          我个人的观点是,实现方式1适合使用廉价存储、对事务访问以及备份等关注不多的或存储超大文件(几百M)的场景,其他的场景使用实现方式2会更合适一些。

    展开全文
  • Bigtable:一个分布式的结构化数据存储系统 摘要 Bigtable是一个管理结构化数据的分布式存储系统,它被设计用来处理海量数据:分布在数千台通用服务器上的PB级的数据。Google的很多项目将数据存储在Bigtable中...

    修正了alex翻译版的几个欠妥之处
    Bigtable
    :一个分布式的结构化数据存储系统

    摘要

    Bigtable是一个管理结构化数据的分布式存储系统,它被设计用来处理海量数据:分布在数千台通用服务器上的PB级的数据。Google的很多项目将数据存储在Bigtable中,包括Web索引、Google EarthGoogle Finance。这些应用对Bigtable提出的要求差异非常大,无论是在数据规模(从URL到网页到卫星图像)还是在响应速度上(从后端的批量处理到实时数据服务)。尽管应用需求差异很大,但是,针对所有Google这些产品,Bigtable还是成功地提供了一个灵活的、高性能的解决方案。本文描述了Bigtable提供的简单的数据模型,利用这个模型,用户可以动态的控制数据的布局和格式;并且我们还将描述Bigtable的设计和实现。

    介绍

    在过去两年半时间里,我们设计、实现并部署了一个用于管理结构化数据的分布式的存储系统—在Google,我们称之为BigtableBigtable的设计目的是可靠地适应PB级别的数据和成千上万台机器。Bigtable已经实现了下面的几个目标:广泛的适用性、可扩展、高性能和高可用性。已经有超过60Google的产品和项目在使用Bigtable,包括Google AnalyticsGoogle FinanceOrkutPersonalized SearchWritelyGoogle Earth。这些产品使用Bigtable完成迥异的工作负载需求,这些需求从面向吞吐量的批处理作业到对终端用户而言延时敏感的数据服务。它们使用的Bigtable集群的配置也有很大的差异,从少数机器到成千上万台服务器,这些服务器里最多可存储几百TB的数据。

    在很多方面,Bigtable和数据库很类似:它使用了很多数据库的实现策略。并行数据库【14】和内存数据库【13】已经具备可扩展性和高性能,但是Bigtable提供了一个和这些系统完全不同的接口。Bigtable不支持完整的关系数据模型;与之相反,Bigtable为客户提供了简单的数据模型,利用这个模型,客户可以动态控制数据的布局和格式alex注:也就是对BigTable而言,数据是没有格式的,用数据库领域的术语说,就是数据没有Schema,用户自己去定义Schema,用户也可以自己推测(alex注:reason about)在底层存储中展示的数据的位置属性(alex注:位置相关性可以这样理解,比如树状结构,具有相同前缀的数据的存放位置接近。在读取的时候,可以把这些数据一次读取出来)。数据用行和列的名字进行索引,名字可以是任意的字符串。虽然客户程序通常会在把各种结构化或半结构化的数据串行化到字符串里,Bigtable同样将数据视为未经解析的字符串。通过仔细选择数据的模式,客户可以控制数据的位置。最后,可以通过BigTable的模式参数动态地控制数据读或写(control whether to serve data out of memory or from disk)

    第二节更详细地描述了数据模型,第三节概要介绍了客户端API;第四节简要介绍了BigTable依赖的底层Google基础框架;第五节描述了BigTable实现的基本原理;第6节描述了为了提高BigTable的性能而采用的一些精细的调优方法;第7节提供了BigTable的性能数据;第8节讲述了几个Google内部使用BigTable的例子;第9节讨论了我们在设计和后期支持过程中得到一些经验和教训;最后,在第10节介绍了相关工作,第11节是我们的结论。

    数据模型

    Bigtable是一个稀疏的、分布式的、持久化存储的多维度排序Mapalex注:对于程序员来说,Map应该不用翻译了吧。Mapkeyvalue 组成,后面我们直接使用keyvalue,不再另外翻译了)Map由行关键字、列关键字以及时间戳索引;Map中的每个value都是一个未经解析的字节数组。

    (row:string, column:string,time:int64)->string

    我们在仔细分析了一个类似Bigtable的系统的种种潜在用途之后,决定选用这个数据模型。我们先举个具体的例子,这个例子促使我们做了很多设计决策;假设我们想要备份海量的网页及相关信息,这些数据可以用于很多不同的项目,我们姑且称这个特殊的表为Webtable。在Webtable里,我们使用URL作为行关键字,使用网页的各种属性(aspect)作为列名,网页的内容存在“contents:”列中,并用获取该网页的时间戳作为标识(alex注:即按照获取时间不同,存储了多个版本的网页数据),如图一所示。

    图一:一个存储Web网页的例子的表的片断。行名是一个反向URLcontents列族容纳的是网页的内容,anchor列族容纳引用该网页的锚链接文本。CNN的主页被Sports IllustraterMY-look的主页引用,因此该行包含了名为“anchor:cnnsi.com”和“anchhor:my.look.ca”的列。每个锚链接数据项只有一个版本alex注:注意时间戳标识了列的版本,t9t8分别标识了两个锚链接的版本);而contents列则有三个版本,分别由时间戳t3t5,和t6标识。

    表中的行关键字是任意字符串(目前支持最大64KB的字符串,但是对大多数用户,10-100个字节就足够了)。在单一行关键字下的每一个读或者写操作都是原子的(不管在这一行里被读或者写的不同列的数目),这个设计决策能够使用户很容易地推测(reason about)对同一个行进行并发更新操作时的系统行为。

    Bigtable通过行关键字的字典顺序来维护数据。表中一定范围内的行被动态分区。每个分区叫做一个”Tablet”,Tablet是数据分布和负载均衡的单位。这样做的结果是,读取一定范围内的少数行很高效,并且往往只需要跟少数机器通信。用户可以通过选择他们的行关键字来开发这种特性,这样可以为他们的数据访问获得好的本地性(get good locality)。举例来说,我们在关键字com.google.maps/index.html的索引下为maps.google.com/index.htm存储数据。把相同的域中的网页存储在连续的区域可以让一些主机和域名的分析更加有效。

    列族

    列关键字组成的集合叫做“列族“,列族构成了访问控制的基本单位。存放在同一列族下的所有数据通常都属于同一个类型(我们把同一个列族下的数据压缩在一起)。列族必须先创建,然后才能在列族中任何的列关键字下存放数据;列族创建后,其中的任何一个列关键字下都可以存放数据。我们的意图是,一张表中不同列族的数目要小(最多几百个),并且列族在操作中很少改变。与此相反,一张表可以有无限多个列。

    列关键字的命名语法如下:列族:限定词。列族的名字必须是可打印的字符串,但是限定词可以是任意字符串。比如,Webtable有个列族language,用来存放撰写网页的语言。我们在language列族中只使用一个列关键字,用来存放每个网页的语言标识IDWebtable中另一个有用的列族是anchor;这个列族的每一个列关键字代表单独一个锚链接,如图一所示。限定词是引用该网页的站点名;数据项内容是链接文本。

    访问控制、磁盘和内存的计数都是在列族层面进行的。在我们的Webtable的例子中,上述的控制权限能帮助我们管理不同类型的应用:一些应用可以添加新的基本数据、一些可以读取基本数据并创建派生的列族、一些则只允许浏览现存数据(甚至可能因为隐私的原因不能浏览所有现存列族)。

    时间戳

    Bigtable中,每一个数据项都可以包含同一数据的不同版本;这些版本通过时间戳来索引。Bigtable时间戳是64位整型数。时间戳可由Bigtable指定,这种情况下时间戳代表精确到毫秒的“实时”时间,或者该值由库户程序明确指定。需要避免冲突的程序必须自己生成一个唯一的时间戳。数据项中不同版本按照时间戳倒序排列,所以最新的版本可以被先读到。

    为了减轻多个版本数据的管理负担,我们对每一个列族提供两个设置参数,Bigtable通过这两个参数可以对废弃版本的数据进行自动垃圾收集。用户既可以指定只保存最后n个版本的数据,也可以只保存“足够新”的版本的数据(比如,只保存最近7天的内容写入的数据)。

    在我们的例子Webtable中,我们将存储在contents:列中爬虫经过的页面的时间戳设置为这个版本的页面被实际爬过的时(alex:contents:列存储的时间戳信息是网络爬虫抓取一个页面的时间)。上面提及的垃圾收集机制可以让我们只保留每个网页的最近三个版本。

    3 API

    Bigtable提供了建立和删除表以及列族的API函数。Bigtable还提供了修改集群、表和列族的元数据的API,比如修改访问权限。

    // Open the table

    Table *T = OpenOrDie("/bigtable/web/webtable");

    // Write a new anchor and delete an old anchor

    RowMutation r1(T, "com.cnn.www");

    r1.Set("anchor:www.c-span.org", "CNN");

    r1.Delete("anchor:www.abc.com");

    Operation op;

    Apply(&op, &r1);

    客户程序可以对Bigtable进行如下的操作:写入或者删除Bigtable中的值、从个别行中查找值、或者遍历表中的一个数据子集。图2中的C++代码使用RowMutation抽象对象执行一系列的更新操作。(为了保持示例简洁,我们省略了无关细节)。对Apply的调用执行了了对Webtable的一个原子修改(mutation)操作:它为www.cnn.com增加了一个锚点,同时删除了另外一个锚点。

    Scanner scanner(T);

    ScanStream *stream;

    stream = scanner.FetchColumnFamily("anchor");

    stream->SetReturnAllVersions();

    scanner.Lookup("com.cnn.www");

    for (; !stream->Done(); stream->Next()) {

    printf("%s %s %lld %s\n",

    scanner.RowName(),

    stream->ColumnName(),

    stream->MicroTimestamp(),

    stream->Value());

    }

    中的C++代码使用Scanner抽象对象遍历一个特定行内的所有锚点。客户程序可以遍历多个列族,有几种机制可以对扫描输出的行、列和时间戳进行限制。例如,我们可以限制上面的扫描,让它只输出那些列匹配正则表达式*.cnn.com的锚点,或者那些时间戳在当前时间前10天的锚点。

    Bigtable还支持一些其它的特性,这些特性允许用户以更复杂的方法操作数据。首先,Bigtable支持单行上事务处理,利用这个功能,用户可以对存储在一个单独行关键字下的数据执行原子性的读取-更新-写入操作。虽然Bigtable提供了一个允许用户跨行关键字(at the clients?)批量写入的接口,但是,Bigtable目前还不支持通用的跨行事务处理。其次,Bigtable允许把数据项用做整数计数器。最后,Bigtable支持在服务器的地址空间内执行脚本程序。脚本程序使用Google开发的用于数据处理的Sawzall语言【28】书写。目前,我们基于SawzallAPI还不允许客户脚本将数据写回Bigtable,但是它允许多种形式的数据转换、基于任意表达式的数据过滤、以及通过多种操作符的汇总归纳。

    Bigtable可以和MapReduce12】一起使用,MapReduceGoogle开发的运行大规模并行计算的框架。我们已经开发了一套封装器(wrapper),这些封装器使Bigtable既可以作为MapReduce作业的源输入也可以作为目标输出输出。

    4 BigTable构件

    Bigtable是建立在一些其他Google基础架构之上的。BigTable使用Google分布式文件系统(GFS)17】存储日志和数据文件。BigTable集群往往运行在一个共享的机器池中,池中的机器还会运行其它各种各样的分布式应用程序,BigTable的进程经常要和其它应用的进程共享机器。BigTable依赖集群管理系统在共享机器上调度作业、管理资源、处理机器的故障、以及监视机器的状态。

    SSTable文件格式

    BigTable数据在内部使用Google SSTable文件格式存储。SSTable提供一个从键(key)到值(value)的持久化的、已排序、不可更改的映射(Map),这里的keyvalue 的都是任意的字节(Byte)串。对SSTable提供了如下操作:查询与一个指定key值相关的value,或者遍历指定key值范围内的所有键值对。从内部看,SSTable是一连串的数据块(通常每个块的大小是64KB,但是这个大小是可以配置的)。SSTable使用块索引(通常存储在SSTable 的最后)来定位数据块;在打开SSTable的时候,索引被加载到内存。一次查找可以通过一次磁盘搜索完成:首先执行二分查找在内存索引里找到合适数据块的位置,然后在从硬盘中读取合适的数据块。也可以选择把整个SSTable都映射到内存中,这样就可以在不用访问硬盘的情况下执行查询搜索了。

    Chubby分布式锁服务

    BigTable还依赖一个高可用的、持久化的分布式锁服务组件,叫做Chubby8】。一个Chubby服务包括了个活动的副本,其中一个副本被选为Master,并且积极处理请求。只有在大多数副本正常运行,并且彼此之间能够互相通信的情况下,Chubby服务才是可用的。当有副本失效的时候,出现故障时Chubby使用Paxos算法【9,23】保证副本的一致性。Chubby提供了一个名字空间,里面包括了目录和小文件。每个目录或者文件可以当成一个锁使用,对文件的读写操作都是原子的。Chubby客户程序库提供对Chubby文件的一致性缓存。每个Chubby客户程序都维护一个与Chubby服务的会话。如果客户程序不能在租约到期的时间内重新签订会话租约,这个会话就过期失效了(A clients session expires if it is unable to renew its session lease within the lease expiration time.)。当一个客户会话失效时,它拥有的锁和打开的文件句柄都失效了。Chubby客户程序可以在Chubby文件和目录上注册回调函数,当文件或目录改变、或者会话过期时,回调函数会通知客户程序。

    Bigtable使用Chubby完成以下各种任务:保证在任意时间最多只有一个活动的Master;存储BigTable数据的引导程序的位置(参考5.1节);发现tablet服务器,以及在Tablet服务器失效时进行善后(5.2节);存储BigTable的模式信息(每张表的列族信息);以及存储访问控制列表。如果Chubby长时间无法访问,BigTable就会失效。最近我们在跨越11Chubby服务实例的14BigTable集群上测量了这个影响。Bigtable服务器时钟的平均比率是0.0047%,在这期间由于Chubby不可用而导致BigTable中的部分数据不能访问(Chubby不能访问的原因可能是Chubby本身失效或者网络问题)。单个集群里受Chubby失效影响最大的百分比是0.0326%

    实现

    Bigtable的实现有三个主要的组件:链接到每个客户程序的库、一个Master服务器和多个tablet服务器。在一个集群中可以动态地添加(或者删除)一个tablet服务器来适应工作负载的变化。

    Master主要负责以下工作:为tablet服务器分配tablets,检测新加入的或者过期失效的table服务器、平衡tablet 服务器的负载、以及对GFS中的文件进行垃圾收集。除此之外,它还处理模式修改操作,例如建立表和列族。

    每个Tablet服务器都管理一组tablet(通常每个tablet服务器有大约数十个至上千个tablet)。tablet服务器处理它所加载的tablet的读写操作,以及分割增长的过大的tablet

    和很多单主节点(Single-Master)类型的分布式存储系统【17.21】类似,客户数据都不经过master服务器:客户程序直接和tablet服务器通信来进行读写操作。由于BigTable的客户程序不依赖master服务器来获取tablet的位置信息,大多数客户程序甚至完全不和master通信。因此,在实际应用中master的负载是很轻的。

    一个BigTable集群存储了很多表,每个表包含了一组tablet,而每个tablet包含了某个范围内的行的所有相关数据。初始状态下,每个表只有一个tablet组成。随着表中数据的增长,它被自动分割成多个tablet,默认情况下每个tablet的大小大约是100MB200MB

    5.1 Tablet的位置信息

    我们使用一个三层的、类似于B+[10]的结构存储tablet的位置信息(如图4)

    第一层是一个存储在Chubby中的文件,它包含了root tablet的位置信息。root tablet在一个特殊的元数据(METADATA)表包含了里所有的tablet的位置信息。每一个元数据tablet包含了一组用户tablet的的位置信息。root tablet实际上只是元数据表的第一个tablet,只不过对它的处理比较特殊—root tablet永远不会被分割—这就保证了tablet的位置层次不会超过三层。

    元数据表将每个tablet的位置信息存储在一个行关键字下,而这个行关键字是由tablet所在的表的标识符和tablet的最后一行编码而成的。每一个元数据行在内存中大约存储了1KB数据。在一个大小适中的、大小限制为128MB的元数据 tablet中,我们的三层结构位置信息模式足够寻址234(三层27+10*27+10)个tablet(或者说在128M的元数据中可以存储261个字节)。

    客户程序库会缓存tablet的位置信息。如果客户程序不知道一个tablet的位置信息,或者发现它缓存的地址信息不正确,那么客户程序就递归移动到tablet位置层次;如果客户端缓存是空的,那么寻址算法需要通过三次网络来回通信寻址,这其中包括了一次Chubby读操作。如果客户端缓存的地址信息过期了,那么寻址算法可能进行多达6次(alex注:其中的三次通信发现缓存过期,另外三次更新缓存数据)网络来回通信,因为过期缓存条目只有在没有查到数据(upon misses)的时候才能发现 (假设元数据tablet没有被频繁的移动)。尽管tablet的位置信息是存放在内存里的,所以不需访问GFS,但是,通常我们会通过预取tablet地址来进一步的减少访问开销:无论何时读取元数据表,都会为不止一个tablet读取元数据。

    在元数据表中还存储了次级信息(alex注:secondary information),包括与tablet有关的所有事件日志(例如,什么时候一个服务器开始为该tablet提供服务)。这些信息有助于排除故障和性能分析。

    5.2 Tablet分配

    每个tablet一次分配给一个tablet服务器。master服务器记录活跃的tablet服务器、当前tablettablet服务器的分配、包括哪些tablet还没有被分配。当一个tablet还没有被分配、并且有一个tablet服务器有足够的空闲空间来装载该tablet并且可用,master通过给这个tablet服务器发送一个tablet装载请求分配该tablet

    BigTable使用Chubby跟踪记录tablet服务器。当一个tablet服务器启动时,它在一个指定的Chubby目录下建立一个有唯一名字的文件,并且获取该文件的独占锁。master监控着这个目录(服务器目录)y以便范闲tablet服务器。如果tablet服务器失去了Chubby上的独占锁—比如由于网络断开导致tablet服务器丢失Chubby会话—它就停止对tablet提供服务。(Chubby提供了一种高效的机制,利用这种机制,tablet服务器能够在不招致网络拥堵的情况下检查其是否还持有该锁)。只要该文件还存在,tablet服务器就会试图重新获得对该独占锁;如果文件不存在了,那么tablet服务器就永远不能再提供服务了,它会自行退出(so it kills itself)。只要tablet 服务器终止(比如,集群的管理系统将该tablet服务器的主机从集群中移除),它会尝试释放它持有的锁,以便master尽快重新分配它的tablet

    Master负责探测一个tablet服务器何时不再为它的tablet提供服务,并且尽快重新分配那些tabletmaster通过轮询tablet服务器锁的状态来探测tablet服务器何时不再为tablet提供服务。如果一个tablet服务器报告它丢失了锁,或者master最近几次尝试都无法和该服务器通信,master就会尝试获取该tablet服务器文件的独占锁;如果master能够获取独占锁,那么就说明Chubby是正常运行的,而tablet 服务器要么是宕机了、要么是不能和Chubby通信了,因此,为了保证该tablet服务器不能再提供服,master就删除该tablet服务器在Chubby上的服务器文件。一旦服务器文件被删除了,master就把之前分配给该服务器的所有的tablet放入未分配的tablet集合中。为了确保Bigtable集群面对masterChubby之间网络问题不那么脆弱,master在它的Chubby会话过期时会主动退出。但是不管怎样,如上所述,master的故障不会改变现有tablettablet服务器的分配。

    当集群管理系统启动了一个master之后,master首先要了解当前tablet的分配状态,之后才能够修改它们。master在启动的时候执行以下步骤:(1masterChubby中获取一个唯一的master锁,用来阻止并发的master实例;(2master扫描Chubby的服务器目录,获取寻找正在运行的服务器;(3master和每一个正在运行的tablet服务器通信,搜寻哪些tablet已经分配到了tablet服务器中;(4master服务器扫描元数据表获取tablet的集合。只要扫描发现了一个还没有分配的tabletmaster就将这个tablet加入未分配的tablet 集合,该集合使该talbet有机会参与talbet分配。

    有一种复杂情况是:元数据tablet还没有被分配之前是不能够扫描它的。因此,在开始扫描之前(步骤4),如果在第三步中没有发现对root tablet的分配,master就把root tablet加入到未分配的tablet集合中。这个附加操作确保了root tablet会被分配。由于root tablet包括了所有元数据tablet的名字,master在扫描完root tablet以后才了解所有元数据信息。

    现存tablet的集合只有在以下事件发生时才会改变:建立了一个新表或者删除了一个旧表、两个现存tablet合并组成一个大的tablet、或者一个现存tablet被分割成两个小的tabletmaster可以跟踪这些改变,因为除了最后一个事件外的两个事件都是由它初始化的。tablet分割事件需要特殊处理,因为它是由tablet服务器初始化的。tablet服务器通过在元数据表中为新的tablet记录信息的方式提交分割操作。在分割操作提交之后tablet服务器会通知master。假如分割操作通知丢失(tablet服务器或者master宕机),master在请求tablet服务器装载已经被分割的tablet的时候会探测到一个新的tablet。由于在元数据tablet中发现的tablet条目只是列举了master请求加载的tablet的一部分,tablet服务器会通知master分割信息。

    5.3 Tablet服务

    如图5所示,tablet的持久化状态信息保存在GFS上。更新操作提交到存储撤销(REDO)记录的提交日志中。在这些更新操作中,最近提交的那些存放在一个叫做memtable的排序的缓冲区中;较早的更新存放在一系列SSTable中。为了恢复一个tablettablet服务器在元数据表中读取它的元数据。这些元数据包含组成一个tabletSSTable列表和一组还原点(redo points),这些点是指向包含tablet数据的任一提交日志的指针。tablet服务器把SSTable的索引读进内存,之后通过应用还原点之后提交的所有更新来重构memtable

    当写操作到达tablet服务器时,tablet服务器首先要检查这个操作格式是否正确、发送者是否有执行这个改变的权限。权限验证是通过从一个Chubby文件里读取具有写权限的操作者列表来进行的(这个文件几乎总会在Chubby客户缓存里命中)。有效的修改操作会记录在提交日志里。可以采用组提交方式(alex注:group commit)来提高大量小的修改操作的吞吐量【1316】。当一个写操作提交后,它的内容被插入到memtable里面。

    当读操作到达tablet服务器时,它同样会检查良构性和适当的权限。一个有效的读操作在一个由一系列SSTablememtable合并的视图里执行。由于SSTablememtable是按字典排序的数据结构,因此可以高效生成合并视图。

    当进行tablet的合并和分割时,引入(incoming)的读写操作能够继续进行。

    5.4 Compactions

    (alex注:这个词挺简单,但是在这节里面挺难翻译的。应该是空间缩减的意思,但是似乎又不能完全概括它在上下文中的意思,干脆,不翻译了)

    随着写操作的执行,memtable的大小不断增加。当memtable的尺寸到达一个临界值的时候,这个memtable就会被冻结,然后创建一个新的memtable;被冻结住memtable会被转换成SSTable并写入GFSalex注:我们称这种Compaction行为为Minor Compaction)。Minor Compaction过程有两个目的:一是收缩tablet服务器内存使用,二是在服务器灾难恢复过程中,减少必须从提交日志里读取的数据量。在Compaction过程中,引入(incoming)的读写操作仍能继续。

    每一次Minor Compaction都会创建一个新的SSTable。如果这个行为未经检查地持续下去,读操作可能需要合并来任意个SSTable的更新;反之,我们通过定期在后台执行Merging Compaction 过程限制这类文件(shijinSStable)的数量。Merging Compaction过程读取一些SSTablememtable的内容,输出一个新的SSTable。只要Merging Compaction过程完成了,作为输入的SSTablememtable就可以丢弃了。

    重写所有的SSTable到一个新的SSTableMerging Compaction过程叫作Major Compaction。由非Major Compaction产生的SSTable可以包含特殊的删除条目,这些删除条目能够禁止仍然可用的较早SSTable中已删除的数据(STables produced by non-major compactions can contain special deletion  entries that suppres s deleted data in older SSTables that are still live)。另一方面,Major Compaction过程生成的SSTable不包含已经删除的信息或数据。Bigtable循环扫描它所有的tablet并且定期对它们应用Major CompactionMajor Compaction机制允许Bigtable回收已经删除的数据使用的资源,并且确保已删除的数据及时从系统内消失(alex注:实际是回收资源。数据删除后,它占有的空间并不能马上重复利用;只有空间回收后才能重复使用),这对存储敏感数据的服务是非常重要的。

    优化

    上一节描述的实现需要很多优化来达到用户要求的高性能、高可用性和高可靠性的目标。为了突出这些优化,本节描述了部分实现的详细细节。

    局部性群组

    客户程序可以将多个列族组合成一个局部性群族。对每个tablet中的每个局部性群组都会生成一个单独的SSTable。将通常不会一起访问的列族分割成单独的局部性群组使读取操作更高效。例如,在Webtable表中,网页的元数据(比如语言和检验和Checksum)可以在一个局部性群组中,网页的内容可以在不同的群组:要读取网页元数据的应用程序没有必要读取整个页面内容。

    此外,可以以局部性群组为单位指定一些有用的调整参数。比如,可以把一个局部性群组设定为全部存储在内存中。设定为存入内存的局部性群组的SSTable依照惰性加载的策略装载进tablet服务器内存。加载完成之后,属于该局部性群组的列族的不用访问硬盘即可读取。这个特性对于需要频繁访问的小块数据特别有用:在Bigtable内部,我们利用这个特性进行元数据表内的列族定位(for the location column family in the metadata table)。

    压缩

    客户程序可以控制一个局部性群组的SSTable是否压缩;如果压缩,用什么格式压缩。用户指定的压缩格式应用到每个SSTable的块中(块的大小由局部性群组的调整参数操纵)。尽管为每个分别压缩浪费了少量空间(alex注:相比于对整个SSTable进行压缩,分块压缩压缩率较低),我们却受益于在只读取小部分数据SSTable的时候就不必解压整个文件了。许多客户程序使用双步(two-pass)定制压缩模式。第一步采用Bentley and McIlroys模式[6],这种模式横跨一个很大窗口压缩常见的长字符串;第二步采用快速压缩算法,即在一个16KB数据的小窗口中寻找重复数据。两步压缩都很快,在现代的机器上,编码的速率达到100-200MB/s,解码的速率达到400-1000MB/s

    虽然我们在选择压缩算法的时候强调的是速度而不是压缩的空间,但是这种两步压缩模式效果却惊人的好。比如,在Webtable的例子里,我们使用这种压缩方式来存储网页内容。在一次实验中,我们在一个压缩的局部性群组中存储了大量的网页。针对实验的目的,对每个文档我们限制其只有一个版本,而不是存储对我们可用的所有版本。该模式在空间上达到了10:1的压缩比。这比经典的Gzip在压缩HTML页面时3:1或者4:1的空间压缩比好的多;这是由于Webtable的行的布局方式:从单一主机获取的所有页面紧密存储。利用这个特性,Bentley-McIlroy算法可以从来自同一个主机的页面里识别大量共享的引用。不仅仅是Webtable,很多应用程序也通过选择行名来将相似的数据集聚,从而获取较高的压缩率。当我们在Bigtable中存储同一份数据的多个版本的时候,压缩效率会更高。

    通过缓存提高读操作的性能

    为了提高读操作的性能,tablet服务器使用二级缓存的策略。对tablet服务器代码而言(to tablet server code),扫描缓存是第一级缓存,其缓存SSTable接口返回的键值对;Block缓存是二级缓存,其缓存从GFS读取的SSTable块。对于趋向于重复读取相同数据的应用程序来说,扫描缓存非常有效;对于趋向于读取刚读过的数据附近的数据的应用程序来说,Block缓存很有用(例如,顺序读,或者在一个热点的行的同一局部性群组中随机读取不同的列)

    Bloom过滤器

    (alex注:Bloom,又叫布隆过滤器,什么意思?请参考Google黑板报http://googlechinablog.com/2007/07/bloom-filte r.html请务必先认真阅读)

    5.3节所述,一个读操作必须读取组成tablet状态的所有SSTable的数据。如果这些SSTable不在内存中,那么就需要多次访问硬盘。我们通过允许客户程序对特定局部性群组的SSTable指定Bloom过滤器【7】,来减少硬盘访问的次数。通过bloom过滤器我们可以查询一个SSTable是否包含了特定行/列对的数据。对于某些应用程序,只使用了少量的tablet服务器内粗来存储Bloom过滤器,却大幅度减少了读操作需要的磁盘访问次数。Bloom过滤器的使用也意味着对不存在的行或列的大多数查询不需要访问硬盘。

    提交日志的实现

    如果我们为每个tablet在一个单独的文件里保存提交日志,那么大量的文件会并发地写入GFS。取决于每个GFS 服务器底层文件系统实现方案,为了写入不同的物理日志文件,这些写操作会引起大量的询盘操作。另外,由于批量提交(group)中操作的数目趋向于比较少,每个tablet拥有单独的日志文件也会降低批量提交优化的效果。为了修正这些问题,我们对每个tablet服务器的唯一提交日志追加修改,不同tablet的修改操作协同混合到一个相同的物理日志文件中。

    在普通操作中使用单个日志提供了重大的性能收益,但是将恢复的工作复杂化了。当一个tablet服务器宕机时,它服务的tablet将会被移动到大量其它的tablet服务器上:每个tablet服务器通常都装载少量原来的服务器的tablet。为了恢复一个tablet的状态,新的tablet服务器要为该tablet重新应用原来的tablet服务器写的提交日志中的修改操作。然而,这些tablet修改操作被混合在同一个物理日志文件中。一种方法可以是对每一个新的tablet服务器读取完整的提交日志文件,然后只重新应用它需要恢复的tablet的相关条目。然而,在这种模式下,假如100台机器中每台都加载了来自失效的tablet服务器的一个单独的tablet,那么这个日志文件就要被读取100次(每个服务器读取一次)。

    为了避免重复读取日志文件,我们首先把提交日志的条目按照关键字(tablerow namelog sequence number)排序。排序之后,对一个特定tablet的修改操作连续存放,因此,随着一次询盘操作之后的顺序读取,修改操作的读取将更高效。为了并行排序,我们将日志文件分割成64MB的段,之后在不同的tablet服务器对每段进行并行排序。这个排序过程由master来协调,并且当一个tablet服务器指出它需要从一些提交日志文件中回复修改时排序被初始化。

    在向GFS写提交日志时有时引起性能颠簸,原因是多种多样的(比如,写操作相关GFS服务器崩溃;或者穿过到达特定组合的三个GFS服务器的网络拥塞或者过载)。为了使修改操作免受GFS瞬时延迟的影响,每个tablet服务器实际上有两个日志写入线程,每个线程写自己的日志文件,并且同一时刻,两个线程只有其中之一是活跃的。如果写入活跃日志文件的效率很低,日志文件写入切换到另外一个线程,在提交日志队列中的修改操作就会由新的活跃日志写入线程写入。日志条目包含序列号,这使得恢复进程可以省略掉由于日志进程切换而造成的重复条目。

    Tablet恢复提速

    如果master将一个tablet从一个tablet服务器移到另外一个tablet服务器,源tablet服务器会对这个tablet做一次Minor Compaction。这个Compaction操作减少了tablet服务器日志文件中没有压缩的状态的数目,从而减少了恢复的时间。Compaction完成之后,该tablet服务器停止为该tablet提供服务。在真正卸载tablet之前,tablet服务器还会再做一次(通常会很快)Minor Compaction,以消除tablet服务器日志中第一次minor compaction执行过程中产生的未压缩的状态残留。当第二次minor compaction完成以后,tablet就在不需要任何日志条目恢复的情况下被装载到另一个tablet服务器上了。

    利用不变性

    我们在使用Bigtable时,除了SSTable缓存之外,实际上所有我们产生的SSTable都是不变的,因而Bigtable系统的其它部分就被简化了。例如,当从SSTable读取数据的时候,我们不必对文件系统访问操作进行同步。这样一来,就可以非常高效的实现对行的并发操作。memtable是唯一一个能被读和写操作同时访问的可变数据结构。为了减少对memtable进行读操作时的竞争,我们让每个memtable表的行写备份copy-on-write,这样就允许读写操作并行执行。

    因为SSTable是不变的,所以永久移除已被删除数据的问题就转换成对废弃的SSTable进行垃圾收集的问题了。每个tabletSSTable都在注册在元数据表中。Master将废弃的SSTable作为对SSTable集合的“标记-清除”的垃圾回收而删除【25】,元数据表则保存了root的集合。

    最后,SSTable的不变性使得分割tablet的操作非常快捷。与为每个子tablet生成新的SSTable集合相反,我们让子tablet共享父tabletSSTable

    性能评估

    我们建立一个包括Ntablet服务器的Bigtable集群,通过改变N的值来测量Bigtable的性能和可扩展性。tablet服务器配置了1GB的内存,数据写入到一个包含1786台机器、每台机器有2400G IDE硬盘驱动的GFS单元上。N台客户机生成测试Bigtable工作负载。(我们使用和tablet服务器相同数目的客户机以确保客户机不会成为瓶颈。)每台机器有主频2GHZ的双核Opteron处理器,配置了足以容纳所有进程工作集的物理内存,以及一张千兆以太网卡。这些机器都分配到一个两层的、树状的交换网络里,在根节点上的可用总带宽大约100-200Gbps。所有的机器采用相同的主机设备,因此,任何两台机器间的往返时间都小于1ms

    Tablet 服务器、master、测试客户机、以及GFS服务器都运行在同一组机器上。每台机器都运行一个GFS服务器。其它的机器要么运行tablet服务器、要么运行客户程序、要么运行在测试过程中,同时使用这个机器池的其它作业的进程。

    R是与测试相关的相异Bigtable行关键字的数量。我们精心选择R值,保证每次基准测试对每台tablet服务器读/写的数据量都在1GB左右。

     在序列写的基准测试中,我们使用的列关键字的名字从0R-1。这个范围又被划分为10N个大小相同的区间。核心调度程序把这些区间分配给N个客户端,分配方式是:一旦客户程序处理完上一个分配给它的区间,调度程序就把下一个可用的的区间分配给它。这种动态分配的方式有助于减轻客户机上运行的其它进程对性能影响的变化。我们在每个行关键字下写入一个单独的字符串。每个字符串都是随机生成的、因此也没有被压缩(alex注:参考第6节的压缩小节)。另外,不同行关键字下的字符串也是不同的,因此也就不可能有跨行的压缩。随机写入基准测试采用类似的方法,只是行关键字在写入前先做按R取模的Hash,这样就保证了在整个基准测试期间,写入的工作负载大致均匀地分布在行存储空间内。

    序列读的基准测试生成行关键字的方式与序列写相同,只是说不是在行关键字下写入,而是读取行关键字下的字符串(这些字符串由之前序列写基准测试调用写入)。同样的,随机读的基准测试也是附于随机写操作之上的。

     扫描基准测试和序列读类似,但是使用的是BigTable提供的、从一个行范围内扫描所有值的API。由于一次RPC从一个tablet服务器取回了大量的值,因此,使用扫描减少了基准测试执行RPC的次数。

    随机读(内存)基准测试和随机读类似,但是包含基准测试数据的局部性群组被标记为“in-memory”,因此,要读的数据从tablet服务器的内存中即可得到满足,而不需要从GFS 读取数据。仅对这个测试,我们把每台tablet服务器存储的数据从1GB减少到100MB,这样就充裕地满足了tablet服务器的可用内存。

    中的两个视图展示了我们在Bigtable中读写1000-byte时基准测试的性能。图表显示了每个tablet服务器每秒钟进行的操作次数;图中的曲线显示了每秒种操作次数的总和。

    单个tablet服务器的性能

    我们首先考虑单个tablet服务器的性能。随机读的性能比其它所有操作慢一个数量级或以上(by the order of magnitude or more)。每个随机读操作都涉及通过网络从GFS传输64KBSSTabletablet服务器,这其中只有一个1000-byte的值被用到。Tablet服务器每秒大约执行1200次读操作,也就是说每秒大约从GFS读取75MB64*1200/1024)的数据。由于网络协议层的消耗、SSTable解析、以及BigTable代码执行,这个传输带宽足以占满tablet服务器的CPU,这个带宽也几乎足以占满我们系统中使用的网络链接。大多数采用这种访问模式的BigTable应用程序减小Block到一个很小的值,通常取8KB

    从内存随机读速度快很多,原因是这样的,每个1000-byte的读操作都是由tablet服务器的本地内存满足的,不需要从GFS读取64KB的大数据块。

    随机和序列写操作性能比随机读要好些,原因是每个tablet服务器把写入的内容追加到一个提交日志上,并且采用批量提交的方式,高效地流式写入GFS。随机写和序列写的性能没有显著的差别,两种方式下对tablet服务器的写操作都记录到同一提交日志中。

    序列读的性能好于随机读,原因是每次从GFS获取的64KBSSTable数据块会缓存到Block缓存中,这些缓存用来服务下一次64KB的读请求。

    扫描的性能更高,这是由于tablet服务器响应一次客户RPC就会返回大量值,所以,RPC开销基本被大量的数据摊销了。

    扩大规模(Scaling)

    随着我们将系统中的tablet服务器的数目从1增加到500,系统的整体吞吐量得到显著提高,增长了超过100倍。比如,随着tablet服务器的数量增加500倍,随机内存读的性能增加了几乎300倍。之所以会有这样的性能提升,是因为这个基准测试的性能瓶颈是单台tablet服务器的CPU

    尽管如此,性能并不是线性增长。在大多数的基准测试中,当tablet服务器的数量从1台增加到50台时,每台服务器的吞吐量会有一个明显的下降。这种下降是由于多台服务器配置中的负载不均衡导致的,通常是由于其它的程序争夺CPU和网络。我们的负载均衡算法试图处理这种不均衡,但是基于两个主要原因导致这个算法效果不尽如人意:一个是由于减少tablet的移动而导致重新均衡负载能力受限(当tablet被移动了,那么短时间内—通常是1秒内—这个tablet是不可用的),另一个是在基准测试进行中其产生的负载会有波动(alex注:the load generated by our benchmarks shifts around as the benchmark progresses)。

    随机读基准测试在扩大规模后表现最差(整体吞吐量只提升了100倍,而服务器的数量却增加了500倍)。这种现象的出现是因为(就像上面解释的那样)每读1000-byte我们都会在网络上传输一个64KB大的块。这样的网络传输消耗了我们网络中各种共享的1GB的链路,结果导致随着我们增加服务器的数量,每台服务器上的吞吐量急剧下降。

    实际应用

    截止到20068月有388个非测试用的Bigtable集群运行在各种各样的Google机器集群上,合计大约有24500tablet服务器。表1显示了每个集群上tablet服务器的大致分布情况。这些集群中,许多用于开发目的,因此在引人注意的一段时期内比较空闲。通过观察一个由14个忙碌集群、8069tablet服务器组成的群组,我们看到整体的流量超过了每秒120万次请求,发送到系统的RPC请求导致的网络负载达到了741MB/s,系统发出的RPC请求网络负载大约是16GB/s

    提供了一些目前正在使用的表的相关数据。一些表存储的是服务用户的数据,然而另一些存储的则是用于批处理的数据;这些表在总的大小、平均数据项大小、从内存中读取的数据的比例、表的模式的复杂程度上都有很大的差别。本节的其余部分,我们将主要描述三个产品研发团队如何使用Bigtable的。

    8.1 Google Analytics

    Google Analytics是用来帮助Web站点的管理员在他们网站上分析流量模式的服务。它提供了整体状况的统计数据,比如每天的独立访问的用户数量、每天每个URL的浏览量;它还提供了站点追踪报告,比如假定用户之前访问了一个指定页面,购买商品的用户的比例。

    为了使用这个服务,Web站点的管理员需要在他们的Web页面中嵌入一小段JavaScript脚本。这个Javascript程序在页面被访问的时候调用。它记录了各种Google Analytics需要的信息,比如用户的标识、获取的网页的相关信息。Google Analytics汇总这些数据,之后提供给Web站点的管理员。

    我们简略描述Google Analytics使用的两个表。原始点击Raw Click表(200TB)为每一个终端用户会话维护一行数据。行的名字是一个包含Web站点名字以及用户会话创建时间的元组。这种模式保证了访问同一个Web站点的会话是连续的,会话按时间顺序存储。这个表压缩到原来尺寸的14%

    汇总表summary20TB)包含了每个Web站点的、各种类型的预定义汇总信息。通过周期性地调度MapReduce作业,从raw click表中生成summary表的数据。每个MapReduce作业从raw click表中提取最新的会话数据。系统的整体吞吐量受限于GFS的吞吐量。这个表的压缩到原有尺寸的29%

    8.2 Google Earth

    Google运转着一批为用户提供高分辨率地球表面卫星图像的服务,既可以通过基于WebGoogle Maps接口(maps.google.com),也可以通过Google Earth可定制客户端软件访问。这些软件产品允许用户浏览地球表面:用户可以在许多不同的分辨率下平移、查看和注释这些卫星图像。这个系统使用一个表存储预处理数据,使用另外一组表存储用户数据。

    预处理流水线使用一个表存储原始图像。在预处理过程中,图像被清除,然后被合并到最终的服务数据中。这个表包含了大约70TB的数据,因此需要从磁盘读取数据。图像已经被高效压缩过了,因此Bigtable压缩被禁用。

    Imagery表的每一行与一个单独的地理区块对应。行都有名称,以确保毗邻的区域存储在了一起。Imagery表中有一个记录每个区块的数据源的列族。这个列族包含了大量的列:基本上是一个原始数据图像一列。由于每个区块都是由很少的几张图片构成的,因此这个列族是非常稀疏的。

    预处理流水线高度依赖运行在Bigtable上的MapReduce传输数据。在一些MapReduce作业中,整个系统中每台tablet服务器的处理速度是1MB/s

    这个服务系统使用一个表来索引GFS中的数据。这个表相对较小(500GB),但是这个表必须在低延迟下,针对每个数据中心每秒处理几万个查询请求。因此,这个表必须存储在上百个tablet服务器上,并且包含in-memory的列族。

    8.3 个性化查询

    个性化查询(www.google.com/psearch)是一个选择性加入服务;这个服务记录用户对各种Google属性的查询和点击,比如Web查询、图像和新闻。用户可以浏览他们查询的历史,重新访问他们之前的查询和点击;用户也可以请求基于他们历史上的Google惯用模式的个性化查询结果。

    个性化查询使用Bigtable存储每个用户的数据。每个用户都有一个唯一的用户id,然后分配一个以该id命名的行。所有的用户操作都存储在表里。一个单独的列族被用来储存各种类型的行为(比如,有个列族可能是用来存储所有Web查询的)。每个数据项将相应的用户动作发生的时间作为Bigtable时间戳。个性化查询在Bigtable上使用MapReduce生成用户配置文件。这些用户配置文件用来个性化当前(live)查询结果。

    个性化查询的数据在多个Bigtable的集群上备份,以便提高数据可用性同时减少由客户端的距离而造成的延时。个性化查询的开发团队最初在Bigtable之上建立了一个客户侧(client side)的复制机制,该机制保证了所有副本的最终一致性。现在的系统则使用了内嵌的复制子系统。

    个性化查询存储系统的设计允许其它团队在它们自己的列中加入新的用户级(per-user)数据,因此,很多其他需要按用户配置选项和设置的Google属性使用了该系统。在多个团队之间共享表的结果是产生了非比寻常的众多列族。为了更好的支持数据共享,我们加入了一个简单的配额机制(alex注:quota,参考AIX的配额机制),限制任意特定客户在共享表中消耗的空间;该机制也为使用该系统存储用户级信息的产品团体提供了隔离。

    经验教训

    在设计、实现、维护和支持Bigtable的过程中,我们得到了有用的经验和一些有趣的教训。

    一个教训是,我们发现,很多类型的错误都会导致大型分布式系统受损,不仅仅是通常的网络中断、或者很多分布式协议中设想的fail-stop错误(alex注:fail-stop failture,指一旦系统failstop,不输出任何数据;fail-fast failture,指fail不马上stop,在短时间内return错误信息,然后再stop)。比如,我们遇到过下面这些类型的错误导致的问题:内存数据损坏、网络中断、时钟偏差、机器挂起、扩展的和非对称的网络分区(alex注:extended and asymmetric network partitio ns,不明白什么意思。partition也有中断的意思,但是我不知道如何用在这里)、我们使用的其它系统的Bug(比如Chubby)、GFS 配额溢出、计划内和计划外的硬件维护。随着我们在这些问题中得到更多经验,我们通过修改各种协议来解决(address)这些问题。比如,我们在RPC机制中加入了检验和Checksum。我们通过移除系统的其他部分针对另一部分作出的假设来解决这些问题。例如,我们不再假设一个给定的Chubby操作只返回固定错误码集合中的一个值。

    我们学到的另外一个教训是,我们明白了在彻底了解一个新特性如何使用之前,延迟添加这个新特性是非常重要的。比如,我们最初计划在我们的API中支持通用目的的事务处理。但是由于我们还不会马上用到这个功能,因此,我们并没有去实现它。现在Bigtable上已经运行了很多实际应用,我们已经能够检查它们的真实需求;然后发现大多数应用程序只是需要单行事务处理。在人们需要分布式的事务处理的地方,最重要的使用时用来维护二级索引,因此我们增加了一个特殊的机制去满足这个需求。新的机制在通用性上比分布式事务差很多,但是更高效(特别是在更新遍布上百行或者更多数据的时候),而且与我们积极“跨数据中心”备份模式交互的很好。

    我们从支持Bigtable中得到的一个实际的经验就是,适当的系统级监控是非常重要的(比如,既监控Bigtable自身,也监控使用Bigtable的客户程序)。比如,我们扩展了我们的RPC系统,因此对于一个RPC的例子,它详细记录了代表RPC的很多重要操作。这个特性允许我们检测和修正很多的问题,比如tablet数据结构上的锁的竞争、在提交Bigtable修改操作时对GFS的写入非常慢的问题、以及在元数据表的tablet不可用时,元数据表无法访问的问题。关于监控的用途的另外一个例子是,每个Bigtable集群都在Chubby中注册了。这可以允许我们追踪所有集群,发现它们的大小、检查运行的我们软件的版本、他们接收的流量,以及检查是否有类似于意外高延迟等问题。

    我们得到的最宝贵的经验是简单设计的价值。考虑到我们系统的代码量(大约100000行生产代码(alex注:non-test code)),以及随着时间的推移,代码以难以预料的方式演变的现实,我们发现清晰的设计和编码给维护和调试带来的巨大帮助。这方面的一个例子是我们的tablet服务器成员协议。我们第一版的协议很简单:maste周期性地和tablet服务器签订租约,tablet服务器在租约过期自动退出。不幸的是,这个协议在网络问题面前大大降低系统的可用性,并且对master服务器恢复时间很敏感。我们多次重新设计这个协议,直到它表现优异。然而,最终的协议太复杂,并且依赖一些Chubby很少被其他应用程序运用的特性的行为。我们发现我们花费了过量的时间调试一些古怪的边角问题(obscure corner cases) ,不仅在Bigtable代码中,也在Chubby代码中。最后,我们废弃了这个协议,转向了一个新的简单的协议,该协议仅仅依赖最广泛使用Chubby的特性。

    10  相关工作

    Boxwood24】项目的有些组件在某些方面和ChubbyGFS 以及Bigtable重叠,因为它也提供了诸如分布式协议、锁、分布式块存储以及分布式B-tree存储。在有重叠部分的实例中,看来Boxwood组件的定位要比相应Google低。Boxwood项目的目标是为诸如文件系统、数据库等高级服务提供基础架构,然而Bigtable的目标是直接为希望存储数据的客户程序提供支持。

    许多近期的项目已经处理了很多难题,例如在广域网上提供了分布式存储或者高级服务,通常是“Internet规模”的。这其中包括了分布式的Hash表方面的工作,这项工作由一些诸如CAN29】、Chord32】、Tapestry37】和Pastry30】的项目发起。这些系统的强调的关注点不是由于Bigtable出现的,比如高度变化的带宽、不可信的参与者、频繁的更改配置等;去中心化和拜占庭式容错(alex注:Byzantine,即拜占庭式的风格,也就是一种复杂诡秘的风格。Byzantine Fault表示:对于处理来说,当发错误时处理器并不停止接收输入,也不停止输出,错就错了,只管算,对于这种错误来说,这样可真是够麻烦了,因为用户根本不知道错误发生了,也就根本谈不上处理错误了。在多处理器的情况下,这种错误可能导致运算正确结果的处理器也产生错误的结果,这样事情就更麻烦了,所以一定要避免处理器产生这种错误。)也不是Bigtable的目标。

    就提供给应用程序开发者的分布式数据存储模型而言,我们相信,分布式B-Tree或者分布式Hash表提供的键值对模型有很大的局限性。键值对模型是很有用的组件,但是它们不应该是提供给开发者唯一的组件。我们选择的模型比简单的键值对丰富的多,它支持稀疏的、半结构化的数据。尽管如此,它也足够简单,可以标榜为高效普通文件的代表(it lends itself to a very efficient flat-file representation);它也是透明的(通过局部性群组),允许我们的使用者对系统的重要行为进行调整。

    有些数据库厂商已经开发了并行的数据库系统,能够存储海量的数据。Oracle的实时应用集群数据库RAC27】使用共享磁盘存储数据(Bigtable使用GFS),并且有一个分布式的锁管理系统(Bigtable使用Chubby)。IBM DB2并行版本【4】基于一种类似于Bigtable的、无共享的架构(a shared-nothing architecture)【33】。每个DB2的服务器都负责处理存储在一个本地关系型数据库中的表中的行的一个子集。这两种产品都提供了一个带有事务功能的完整的关系模型。

    Bigtable的局部性群组认识到了类似于压缩和磁盘读取方面性能的收益,这是从其他系统观察到的,这些系统以基于列而不是行的存储方式组织数据,这种系统包括C-Store134】、商业产品例如Sybase IQ1536】、SenSage31】、KDB+22】,以及MonetDB/X10038】的ColumnDM存储层。另外一种在普通文件中进行垂直和水平分区并获得很好的数据压缩率的系统是AT&TDaytona数据库【19】。局部性群组不支持Ailamaki系统中描述的CPU级缓存优化【2】。

    Bigtable采用memtableSSTable存储对tablet的更新的方法与Log-Structured Merge Tree26】存储索引数据更新的方法类似。这两个系统中,排序的数据在写入到磁盘前都先缓冲在内存中,读取操作必须从内存和磁盘中合并数据。

    C-StoreBigtable共享很多特点:两个系统都采用无共享架构,都有两种不同的数据结构,一种用于当前的写操作,另外一种存放“长时间使用”的数据,并且提供一种机制将数据从一种格式转换为另一种。两个系统在API上有很大的不同:C-Store操作更像关系型数据库,而Bigtable提供跟低层次的读写接口,并且设计用来支持每台服务器每秒数千次操作。C-Store也是个“读性能优化的关系型数据库管理系统”,然而Bigtable对密集型读写应用提供了很好的性能。

    Bigtable的负载均衡器也必须解决无共享数据库面对的一些类似的负载和内存均衡问题(比如,【1135】)。我们的问题在某种程度上简单一些:(1)我们不需要考虑同一份数据可能有多个副本的问题,同一份数据可能由于视图或索引的原因以替换的形式表现出来;(2)我们让用户决定哪些数据应该放在内存里、哪些放在磁盘上,而不是试图动态的决断;(3)我们的不用执行复杂的查询或者对其进行优化。

    11  结论

    我们已经讲述完了BigtableGoogle中一个存储结构化数据的分布式系统。Bigtable的集群从20054月开始已经投入产业使用了,在此之前我们花了大约7-年设计和实现这个系统。截止到20068月,有超过60个项目使用Bigtable。我们的用户对Bigtable实现提供的高性能和高可用性很满意,随着时间的推移,当资源需求改变时,他们可以通过简单的多增加机器来扩展集群的容量。

    考虑到Bigtable的编程接口并不常见,一个有趣的问题是:我们的用户适应它有多难?新的使用者有时不太确定如何最好地使用Bigtable接口,特别是如果他们已经习惯于支持通用目的的事务处理的关系型数据库。然而,许多Google产品都成功的使用了Bigtable的事实证明,我们的设计在实践中行之有效。

    我们现在正在实现对Bigtable加入一些新的特性,比如支持二级索引,以及多master副本的、跨数据中心复制的Bigtable的基础架构。我们也已经开始将Bigtable部署为服务供其它的产品团队使用,这样个人团队就不需要维护他们自己的Bigtable集群了。随着我们服务集群的扩大,我们需要在Bigtable系统内部处理更多的资源共享问题了【3】。

    最后,我们发现,建设Google自己的存储解决方案有很多重大优势。通过为Bigtable设计我们自己的数据模型,我们获得了很大的灵活性。另外,我们对Bigtable实现,以及Bigtable依赖的其它的Google的基础组件的控制,意味着我们在系统出现瓶颈或效率低下的情况时,可以清除这些问题。

    展开全文
  • Bigtable:一个分布式的结构化数据存储系统 译者:alex  摘要 Bigtable是一个分布式的结构化数据存储系统,它被设计用来处理海量数据:通常是分布在数千台普通服务器上的PB级的数据。Google的很多项目...
  • 大数据在结构化数据存储方面的第一种模式:实时查询数据库;大数据在结构化数据数据存储方面的第二种模式:大数据仓库;思考题:1.实时查询数据库能否替换实时操作数据库吗?2.大数据仓库可以替代数据仓库吗?
  • Bigtable是一个分布式的结构化数据存储系统,它被设计用来处理海量数据:通常是分布在数千台普通服 务器上的PB级的数据。Google的很多项目使用Bigtable存储数据,包括Web索引、Google Earth、 Google Finance。这些...
  • Bigtable :一个分布式的结构化数据存储系统[中文版]  Bigtable :一个分布式的结构化数据存储系统 译者:alex  摘要 Bigtable 是一个分布式的结构化数据存储系统,它被设计用来处理海量...
  • 为了支持结构化数据存储服务的高可用性和可扩展性,非关系型数据库的架构通常比较复杂,而且具体实现也不尽相同。我们分析了几种主流的非关系型数据库,总结了它们具体实现中的关键功能,其中主要包括了应用接口,...
  • 在移动通信网络中计算和存储体现在对终端用户信息的处理上,计算表示对用户信息的运算和分发,存储表示对用户信息的持久。 在核心网中,数据被分为两大类: 一类是 用户签约信息,与用户信息身份对应,单独存储在...
  • 也许人们会问,能否采用传统的关系型数据库来实现结构化数据存储呢?实际上,一些web 2.0应用在诞生的初期也尝试过关系型数据库,比如,微博网站twitter就曾经用过MySQL,不过随着访问量的激增,MySQL表现出来的可...
  • 1.protobuf 介绍 Protobuf全称Google Protocol Buffers ...结构化数据存储格式(xml, json) 用于通信协议、数据存储等 高效的序列化和反序列化 语言无关、平台无关、扩展性好 官方支持C++, Java, Python三种语言
  • Bigtable:一个分布式的结构化数据存储系统 译者:alex 摘要 Bigtable是一个分布式的结构化数据存储系统,它被设计用来处理海量数据:通常是分布在数千台普通服务器上的PB级的数据。Google的很多项目使用...
  • 最近想了解下图片等非结构化数据存储 在哪里更适合。 参考链接如下: https://baike.baidu.com/item/%E9%9D%9E%E7%BB%93%E6%9E%84%E5%8C%96%E6%95%B0%E6%8D%AE/309808?fr=aladdin 非结构化数据是数据结构不规则...
  • Bigtable:一个分布式的结构化数据存储系统(转载)
  • ## 参考文献 Google Protocol Buffer 的使用和原理 Protobuf 的 proto3 与 proto2 的区别 protobuf3语言指南
  • Bigtable:一个分布式的结构化数据存储系统译者:alex摘要Bigtable是一个分布式的结构化数据存储系统,它被设计用来处理海量数据:通常是分布在数千台普通服务器上的PB级的数据。Google的很多项目使用Bigtable存储...
  • 结构化数据存储与查询小结

    千次阅读 2012-09-10 22:05:44
    暑假老师要写篇海量非结构化数据的存储和查询方面的综述,下面是自己参考几篇论文写得。写得不好,有的仅个人理解,有什么问题,希望指出.  当今信息化时代充斥着大量的数据。海量数据存储是一个必然的趋势。然而...
  • Bigtable:一个分布式的结构化数据存储系统 摘要 Bigtable是一个管理结构化数据的分布式存储系统,它被设计用来处理海量数据:分布在数千台通用服务器上的PB级的数据。Google的很多项目将数据存储在Bigtable中,...
  • 阿里妹导读:任何应用系统都离不开对数据的处理,数据也是驱动业务创新以及向智能发展最核心的东西。数据处理的技术已经是核心竞争力。在一个完备的技术架构中,通常也会由应用系统以及数据系统构成。应用系统负责...
  • 大数据结构化数据存储应用模式

    千次阅读 2014-02-14 13:40:20
    从而产生了大数据在结构化数据存储方面的第一种模式:实时查询数据库; 三、大数据仓库-HDFS&Hive  基于关系型数据库的数据仓库,同样面临数据存储规模的问题,因此在银行业务中,同样也只能存储短期的数据,...
  • 文章目录5.1 HBase简介1 什么是HBase2 BigTable3 面向列的数据库4 什么是非结构化数据存储5 HBase在Hadoop生态中的地位6 HBase与HDFS7 HBase使用场景 5.1 HBase简介 1 什么是HBase HBase是一个分布式的、面向列的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 34,121
精华内容 13,648
关键字:

结构化数据存储