cassandra 数据库_考虑Apache Cassandra数据库

cassandra 数据库

在数据库历史文章“什么不转水转,恶有恶报,”(见相关主题 )米哈尔斯通布雷克详细介绍了如何存储技术已经随着时间而演变。 在开发关系模型之前,开发人员尝试了其他模型,例如层次结构图和有向图。 值得注意的是,基于SQL的关系模型(甚至是现在的事实标准)已经流行了大约30年。 鉴于计算机科学的悠久历史和快速发展,这是一个了不起的成就。 关系模型已经非常完善,以至于多年来,为应用程序架构师选择数据存储是一个容易的选择。 选择始终是关系数据库。

诸如不断增加的系统用户基础,移动设备,扩展的用户在线存在,云计算和多核系统等发展已导致规模越来越大的系统。 诸如谷歌和亚马逊之类的高科技公司率先解决了规模问题。 他们很快发现关系数据库不足以支持大规模系统。

为了避免这些挑战,Google和Amazon提出了两个替代解决方案:Big Table和Dynamo(请参阅参考资料 ),他们放宽了关系数据模型提供的保证以实现更高的可扩展性。 埃里克·布鲁尔(Eric Brewer)的“ CAP定理”(请参阅参考资料 )随后将这些观察形式化。 它声称,对于可伸缩系统,一致性,可用性和分区容忍度是折衷方案,无法构建包含所有这些属性的系统。 不久,基于Google和Amazon的早期工作,并了解了有关可伸缩系统的知识,提出了一种新型的存储系统。 它们被命名为“ NoSQL”系统。 该名称首先表示“如果要扩展就不要使用SQL”,后来又重新定义为“不仅限于SQL”,表示除基于SQL的解决方案外还有其他解决方案。

NoSQL系统很多,每个系统都会放松或更改关系模型的某些方面。 值得注意的是,没有一种NoSQL解决方案适用于所有情况。 每种方法都比关系模型更好,并且可以针对用例的某些子集进行扩展。 我之前的文章“在数据存储干草堆中为您的应用程序找到正确的数据解决方案”讨论了如何使应用程序需求与NoSQL解决方案相匹配(请参阅参考资料 )。

Apache Cassandra(请参阅参考资料 )是第一个也是使用最广泛的NoSQL解决方案之一。 本文对Cassandra进行了详细的介绍,并指出了您首次查看Cassandra时不容易发现的细节和棘手的要点。

阿帕奇·卡桑德拉(Apache Cassandra)

Cassandra是NoSQL Column系列实现,使用Amazon Dynamo引入的架构方面支持Big Table数据模型。 卡桑德拉的一些优点是:

  • 高度可扩展且高度可用,没有单点故障
  • NoSQL列系列实现
  • 很高的写入吞吐量和良好的读取吞吐量
  • 类似于SQL的查询语言(自0.8开始),并支持通过二级索引进行搜索
  • 可调一致性和对复制的支持
  • 灵活的架构

这些积极的方面使推荐Cassandra变得容易,但是对于开发人员来说,探究Cassandra的细节和棘手点以掌握该程序的复杂性至关重要。

Cassandra根据列族数据模型存储数据, 如图1所示

图1. Cassandra数据模型
该图显示了键空间中的列和行关系

Cassandra数据模型由列,行,列族和键空间组成。 让我们详细看一下每个部分。

  • 列– Cassandra数据模型中最基本的单元,每列都包含一个名称,一个值和一个时间戳。 对于此讨论,请忽略时间戳,然后可以将一列表示为名称/值对(例如author =“ Asimov”)。
  • 行–标有名称的列的集合。 例如, 清单1显示了如何表示行:
    清单1.行示例
    "Second Foundation"-> {
        author="Asimov", 
        publishedDate="..",
        tag1="sci-fi", tag2="Asimov"
        }

    Cassandra由许多存储节点组成,并将每一行存储在一个存储节点中。 在每一行中,Cassandra始终存储按其列名排序的列。 使用此排序顺序,Cassandra支持切片查询,给定一行,用户可以检索给定列名称范围内的列的子集。 例如,范围为tag0到tag9999的切片查询将获取名称在tag0和tag9999之间的所有列。

  • 列族–标有名称的行的集合。 清单2显示了示例数据的外观:
    清单2.列族示例
    Books->{
        "Foundation"->{author="Asimov", publishedDate=".."},
        "Second Foundation"->{author="Asimov", publishedDate=".."},
        …
        }

    人们常说列族就像关系模型中的表。 如以下示例所示,相似之处到此结束。

  • 键空间–一组许多列族在一起。 它只是列族的逻辑分组,并且为名称提供了隔离的范围。

最后,超级列位于一个列族中,该族将一个键下的多个列分组。 由于开发人员不鼓励使用超级列,因此在此不再讨论。

Cassandra与RDBMS数据模型

根据以上对Cassandra数据模型的描述,数据被放置在每个列族的二维(2D)空间中。 要检索列族中的数据,用户需要两个键:行名和列名。 从这个意义上讲,关系模型和Cassandra都是相似的,尽管存在一些关键差异。

  • 关系列在表中的所有行之间是均匀的。 数据项之间通常存在明确的垂直关系,而Cassandra列则不是这种情况。 这就是Cassandra将列名与每个数据项(列)一起存储的原因。
  • 使用关系模型,二维数据空间就完整了。 2D空间中的每个点至少应在其中存储空值。 同样,Cassandra并非如此,它可以包含仅包含少量项目的行,而其他行可以包含数百万个项目。
  • 对于关系模型,模式是预定义的,无法在运行时更改,而Cassandra允许用户在运行时更改模式。
  • Cassandra始终存储数据,以便根据列名对列进行排序。 这使得使用切片查询通过列搜索数据更加容易,但是除非使用保留顺序的分区程序,否则通过行搜索数据将更加困难。
  • 另一个重要的区别是RDMBS中的列名表示有关数据的元数据,但不表示数据。 但是,在Cassandra中,列的名称可以包含数据。 因此,Cassandra行可以具有数以百万计的列,​​而关系模型通常具有数十列。
  • 关系模型使用定义明确的不可变架构,支持复杂的查询,其中包括JOIN,聚合等。 使用关系模型,用户可以定义数据模式而不必担心查询。 Cassandra不支持JOIN和大多数SQL搜索方法。 因此,架构必须迎合应用程序所需的查询。

要探索以上差异,请考虑一个图书评分网站,用户可以在其中添加书籍(作者,排名,价格,链接),评论(文本,时间,名称)并标记它们。 该应用程序需要用户支持以下操作:

  • 新增书籍
  • 为书添加评论
  • 为书籍添加标签
  • 上市书按等级排序
  • 列出带有标签的图书
  • 列出给定书号的评论

用关系模型来实现上述应用程序是很简单的。 图2显示了数据库设计的实体关系(ER)图。

图2.图书评级网站的ER模型
图书网站数据模型流程图

让我们看看如何使用Cassandra数据模型来实现这一点。 清单3显示了Cassandra的潜在模式,其中第一行表示“ Books”列家族,该家族具有多行,每行具有该书的属性作为列。 <TS1>和<TS2>表示时间戳。

清单3.书评示例的Cassandra模式
Books[BookID->(author, rank, price, link, tag<TS1>, tag<TS2> .., 
    cmt+<TS1>= text + "-" + author) …] 
Tags2BooksIndex[TagID->(<TS1>=bookID1, <TS2>=bookID2, ..) ] 
Tags2AuthorsIndex[TagID->(<TS1>=bookID1, <TS2>=bookID2, ..) ]
RanksIndex["RANK" -> (rank<TS1>=bookID)]

表1是根据架构的样本数据集。

表1.书评网站的样本数据
列族名 样本数据集
图书


“基金会”->(“作者” =“ Asimov”,“等级” = 9,“价格” = 14,“ tag1” =“科幻”,“ tag2” =“未来”,“ cmt1311031405922” =“最佳book-sa​​njiva“,” cmt1311031405923“ =”很好,我不同意-srinath“)
“ I Robot”->(“ author” =“ Asimov”,“ rank” = 7,“ price” = 14,“ tag1” =“科幻”“ tag2” =“机器人”,“ cmt1311031405924” =“ Asimov的best-srinath“,” cmt1311031405928“ =”我喜欢基础更好的sanjiva“)
排名指数 “排名”->(9 =“ Foundation”,7 =“ I Robot”)
标签2图书索引
“ sci-fi”->(“ 1311031405918” =“基金会”,“ 1311031405919” =“我的机器人”
“未来”->…
标签2作者索引 “科幻”->(1311031405920 =“ Asimov”)
“未来”->…

此示例显示了关系模型和Cassandra模型之间的一些设计差异。 Cassandra模型将有关书籍的数据存储在称为“书籍”的单个列族中,而其他三个列族是为支持查询而建立的索引。

详细查看“ Books”列系列,该模型使用一行来代表每本书,其中书名是行ID。 有关该书的详细信息表示为该行中存储的列。

仔细观察,您可能会注意到存储的数据项(例如注释以及与书籍具有1:M关系的标签)也位于一行中。 为此,请将时间戳添加到标签和注释的列名称中。 这种方法将所有数据存储在同一列中。 该操作避免了必须进行JOIN检索数据。 Cassandra通过这种方法避免了对JOIN的支持。

这提供了几个优点。

  • 您可以通过读取完整行的单个查询来读取有关某本书的所有数据。
  • 通过使用以cmt0-cmt9999和tag0-tag9999作为开始和结束范围的切片查询,可以检索没有JOIN的注释和标签。

因为Cassandra存储按列名排序的列,所以进行切片查询非常快。 值得注意的是,将有关数据项的所有详细信息存储在一行中以及使用排序顺序是Cassandra数据设计背后最关键的想法。 大多数Cassandra数据模型设计都以某种形式遵循这些想法。 用户可以在存储数据和建立索引时使用排序顺序。 例如,将时间戳添加到列名的另一个副作用是,当列名按排序顺序存储时,列名由时间戳后缀固定的注释按创建时的顺序存储,并且搜索结果将具有相同的顺序。

Cassandra不支持基本设计中的任何搜索方法。 尽管它支持二级索引,但它们使用稍后构建的索引来支持,二级索引具有一些限制,包括缺乏对范围查询的支持。

因此,Cassandra数据设计中的最佳结果需要用户通过构建自定义索引并利用列和行的排序顺序来实现搜索。 其他三栏族(Tags2BooksIndex,Tags2AuthorsIndex和RankIndex)正是这样做的。 由于用户需要搜索带有标签的书籍,因此“ Tags2BooksIndex”列系列通过将标签名称存储为行ID,并将该标签标记的所有书籍存储为该行下的列来建立索引。 如该示例所示,时间戳被添加为列键,但这是为了提供唯一的列ID。 搜索实现只是通过按标记名查找行,并通过读取存储在该rowID中的所有列来查找匹配项,从而简单地读取索引。

表2讨论了如何使用上述Cassandra索引实现应用程序所需的每个查询。

表2.查询实现的比较
查询说明 以SQL查询 Cassandra的实现
列出按等级排序的书籍

运行查询
"Select * from Books order by rank" ,然后在每个结果上执行"Select tag from Tags where bookid=?" and "Select comment from Comments where bookid=?" "Select tag from Tags where bookid=?" and "Select comment from Comments where bookid=?"
在“ RankIndex”列族上执行切片查询以接收书籍的有序列表,并且对每本书在“ Books”列族上执行切片查询以读取有关该书的详细信息。
给定标签,找到其书上具有给定标签的作者。 Select distinct author from Tags, Books where Tags.bookid=Books.bookid and tag=? 使用切片查询从Tags2Authors中读取给定标签的所有列。
给定标签,列出具有给定标签的书。 Select bookid from Tags where tag=? 使用切片查询从Tags2BooksIndex读取给定标签的所有列。
给定一本书,请按照创建评论的时间顺序列出该书的评论。 Select text, time, user from Comments where bookid=? Order by time 在“书籍”列族中,从与给定书籍对应的行中进行切片查询。 由于使用时间戳作为列名,因此它们按排序顺序排列。

尽管上述设计可以有效地支持书评网站所要求的查询,但是它只能支持设计的查询,而不能支持即席查询。 例如,如果不建立新索引,它将无法执行以下查询。

  • Select * from Books where price > 50;
  • Select * from Books where author="Asimov"

通过构建适当的索引或编写代码以遍历数据,可以更改设计以支持那些查询和其他查询。 但是,与关系模型相比,需要自定义代码来支持新查询是一个局限,在关系模型中,添加新查询通常不需要更改架构。

从0.8发行版开始,Cassandra支持二级索引,用户可以在其中指定给定属性的搜索,并且Cassandra会基于该属性自动构建用于搜索的索引。 但是,该模型提供的灵活性较小。 例如,二级索引不支持范围查询,也不保证结果的排序顺序。

在Java环境中使用Cassandra

Cassandra有许多用不同语言编写的客户。 本文重点介绍Hector客户端(请参阅参考资料 ),它是Cassandra中使用最广泛的Java客户端。 用户可以通过将Hector JAR添加到应用程序类路径来添加到他们的应用程序。 清单4显示了一个样本Hector客户端。

首先,连接到Cassandra集群。 使用Cassandra入门页面(请参阅参考资料 )中的指示信息来设置Cassandra节点。 除非更改其配置,否则它通常在端口9160上运行。接下来,定义一个键空间。 这可以通过客户端或通过conf / cassandra.yaml配置文件来完成。

清单4. Cassandra的样本Hector客户端代码
Cluster cluster = HFactory.createCluster('TestCluster', 
        new CassandraHostConfigurator("localhost:9160"));

//define a keyspace
Keyspace keyspace = HFactory.createKeyspace("BooksRating", cluster);

//Now let's add a new column. 
String rowID = "Foundation"; 
String columnFamily = "Books";

Mutator<String>
 mutator = HFactory.createMutator(keyspace, user);
mutator.insert(rowID, columnFamily, 
        HFactory.createStringColumn("author", "Asimov"));

//Now let's read the column back 
ColumnQuery<String, String, String>
        columnQuery = HFactory.createStringColumnQuery(keyspace);
columnQuery.setColumnFamily(columnFamily).setKey(”wso2”).setName("address");
QueryResult<HColumn<String, String>
 result = columnQuery.execute();
System.out.println("received "+ result.get().getName() + "= " 
        + result.get().getValue() + " ts = "+ result.get().getClock());

下载中找到有关图书评级示例的完整代码。 它包括切片查询和其他复杂操作的样本。

卡桑德拉建筑

看完Cassandra的数据模型后,让我们回到其架构,从分布式系统的角度了解其优势和劣势。

图3显示了Cassandra集群的体系结构。 第一个发现是Cassandra是一个分布式系统。 Cassandra由多个节点组成,并在这些节点之间分布数据(或使用数据库术语将它们分片)。

图3. Cassandra集群
Cassandra集群图,显示了每个节点如何循环连接

Cassandra使用一致的哈希将数据项分配给节点。 简单来说,Cassandra使用哈希算法为存储在Cassandra中的每个数据项的键(例如,列名,行ID)计算哈希值。 哈希范围或所有可能的哈希值(也称为键空间)在Cassandra群集中的节点之间分配。 然后,Cassandra将每个数据项分配给该节点,该节点负责存储和管理数据项。 论文“ Cassandra-分散式结构化存储系统”(请参阅参考资料 )提供了有关Cassandra体系结构的详细讨论。

生成的体系结构提供以下属性:

  • Cassandra对用户透明地在其节点之间分发数据。 任何节点都可以接受任何请求(读取,写入或删除)并将其路由到正确的节点,即使数据没有存储在该节点中也是如此。
  • 用户可以定义需要多少个副本,Cassandra透明地处理副本的创建和管理。
  • 可调整的一致性:在存储和读取数据时,用户可以为每个操作选择期望的一致性级别。 例如,如果在写入或读取时使用“仲裁”一致性级别,那么将从集群中超过一半的节点写入和读取数据。 对可调一致性的支持使用户可以选择最适合用例的一致性级别。
  • Cassandra提供了非常快的写入速度,实际上比读取速度快,因为后者可以每个节点传输大约80-360MB /秒的数据。 它使用两种技术来实现。
    • Cassandra将大多数数据保留在负责节点的内存中,所有更新都在内存中完成,并以惰性方式写入持久性存储(文件系统)。 但是,为了避免丢失数据,Cassandra将所有事务写入磁盘中的提交日志。 与更新磁盘中的数据项不同,对提交日志的写入仅是追加操作,因此避免了写入磁盘时的旋转延迟。 有关磁盘驱动器性能特征的更多信息,请参阅参考资料
    • 除非写入请求完全一致性,否则Cassandra会将数据写入足够的节点,而不会解决任何数据不一致的问题,只有在第一次读取时才能解决不一致问题。 此过程称为“读取修复”。

最终的架构具有高度可扩展性。 您可以构建一个Cassandra群集,该群集具有100个节点中的10个节点,这些节点能够处理TB级到PB级的数据。 分布式系统需要权衡取舍,规模几乎永远不会免费。 如前所述,从关系数据库迁移到Cassandra可能会给用户带来很多惊喜。 下一节将讨论其中的一些。

卡桑德拉可能带来的惊喜

从关系数据库移至Cassandra时,请注意这些差异。

没有交易,没有JOIN

众所周知,Cassandra不支持ACID事务。 尽管它具有批处理操作,但不能保证该批处理操作中的子操作是以原子方式执行的。 这将在“ 失败的操作可能留下更改”下详细讨论。

此外,Cassandra不支持JOIN。 如果用户需要联接两个列族,则必须以编程方式检索和联接数据。 对于大型数据集,这通常既昂贵又耗时。 如示例中所述,Cassandra通过在同一行中存储尽可能多的数据来规避此限制。

没有外键和键是不可变的

Cassandra不支持外键,因此Cassandra无法代表用户管理数据一致性。 因此,应用程序应处理数据一致性。 此外,用户无法更改键。 建议对需要更改密钥的用例使用代理密钥(生成的密钥代替密钥,并将密钥作为属性进行管理)。

键必须是唯一的

每个键(例如,行键和列键)在其范围内必须唯一,并且如果同一键已使用两次,它将覆盖数据。

有两个解决方案。 首先,您可以使用组合键。 换句话说,通过将多个字段组合在一起来创建键,并且该解决方案通常与行键一起使用。 第二种解决方法是,当同一密钥有两次出现的危险时,请使用随机值或时间戳将密钥后缀。 当索引将值存储为列名时,通常会发生这种情况。 例如,在图书评级应用程序中,排名被用作列名。 为避免两个条目具有相同的列名,因为它们具有相同的等级,因此将时间戳记添加到该列中作为后缀。

操作失败可能会导致更改

如前所述,Cassandra不支持原子操作。 相反,它支持幂等运算。 幂等操作使系统保持相同状态,而不管操作执行了多少次。 所有Cassandra操作都是幂等的。 如果操作失败,则可以毫无问题地重试。 这提供了一种从瞬态故障中恢复的机制。

Cassandra也支持批处理操作,但是它们也没有任何原子性保证。 由于操作是幂等的,因此客户端可以继续重试,直到该批次的所有操作都成功为止。

幂等运算不等于原子运算。 如果操作成功,则一切正常,结果与原子操作相同。 如果操作失败,则客户端可以重试,如果操作成功,则一切都很好。 但是,如果与重试操作不同,即使重试后操作仍然失败,则可能会产生副作用。 不幸的是,对于Cassandra,这是程序员必须应对的复杂性。

搜索很复杂

搜索未内置在Cassandra体系结构的核心中,并且使用排序顺序将搜索机制放在了顶层,如前所述。 Cassandra支持辅助索引,系统会在其中使用某些受限功能来自动构建它们。 当二级索引不起作用时,用户必须学习数据模型并使用排序顺序和切片建立索引。

与建筑物搜索方法相关的三种复杂区域:

  1. 构建自定义搜索方法需要程序员在一定程度上了解索引和有关存储的详细信息。 因此,与关系模型相比,Cassandra需要更高技能的开发人员。
  2. 自定义索引在很大程度上取决于排序的顺序,并且很复杂。 排序顺序有两种类型:第一,列始终按名称排序,第二,仅当使用保留顺序的分区程序(请参阅参考资料 )时,行排序顺序才起作用。
  3. 与关系模型不同,添加新查询通常需要新索引和代码更改。 这要求开发人员在存储数据之前分析查询。

不建议使用超级列和保留顺序的分隔符

当对多级数据建模时,Cassandra超级列可能会很有用,在多级数据中,层次结构又增加了一层。 但是,任何可以用超级列建模的东西,也可以通过列来支持。 因此,超级列不提供额外的电源。 而且,它们不支持二级索引。 因此,Cassandra开发人员不鼓励使用超级列。 尽管没有确定终止支持的确切日期,但可能会在将来的版本中发生。

Cassandra中的分区程序决定了如何在Cassandra节点之间分配(分片)数据,并且有许多实现。 如果使用了保留顺序的分区程序,则rowID将按排序顺序存储,并且Cassandra也可以跨rowID进行切片(搜索)。 该分区程序不会在其节点之间均匀地分布数据,但是,对于大型数据集,某些节点可能会受到压力,而其他节点却被轻载。 因此,开发人员也不鼓励使用保留顺序的分区程序。

故障修复是手动的

如果Cassandra群集中的节点发生故障,如果您有副本,则该群集将继续工作。 完全恢复(用于重新分配数据并补偿丢失的副本)是通过称为节点工具的命令行工具进行的手动操作(请参阅参考资料 )。 同样,当发生手动操作时,系统将不可用。

它记得删除

卡桑德拉(Cassandra)的设计使其即使节点发生故障(或断开连接)并稍后恢复运行,也可以继续正常工作。 结果是这使数据删除复杂化。 例如,假设节点已关闭。 按下时,数据项已在副本中删除。 当不可用的节点重新打开时,它将在同步过程中重新引入已删除的数据项,除非Cassandra记得该数据项已被删除。

因此,Cassandra必须记住该数据项已被删除。 在0.8版本中,Cassandra会记住所有数据,即使已将其删除。 这导致磁盘使用量持续增长,以进行更新密集型操作。 Cassandra不必记住所有已删除的数据,而只需记住数据项已被删除的事实。 此修复程序在更高版本的Cassandra中完成。

结论

本文研究了一些细节,当您考虑使用Cassandra时,这些细节并不明显。 我描述了Cassandra数据模型,并将其与关系数据模型进行了比较,并演示了Cassandra的典型架构设计。 一个关键的观察结果是,与将数据分为多个表的关系模型不同,Cassandra倾向于在同一行中保留尽可能多的数据,以避免必须将这些数据连接起来进行检索。

您还研究了基于Cassandra的方法的一些局限性。 但是,这些限制对于大多数NoSQL解决方案都是常见的,并且通常是有意识的设计折衷,以实现高可伸缩性。


翻译自: https://www.ibm.com/developerworks/opensource/library/os-apache-cassandra/index.html

cassandra 数据库

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值