[Cassandra教程](十二)NoSQL也有视图

摘要:

在传统数据库之中,视图是个非常有用的功能,NoSQL一般不具有这种能力。 另外,对于NoSQL来说,如何设计数据模型,通常来说需要一开始就非常明确自己的数据是如何存储的,以此来设计自己的存储方式。 甚至会通过存储多份数据的方式来达到目的。而对于Cassandra来说,Materialized View(物化视图)是非常实用、好用的功能,可以同时比较完美的解决NoSQL的视图缺失与对降低对数据模型扩充能力的设计要求。

原文来源:http://www.flyml.net/2016/10/30/cassandra-tutorial-materialized-view/

正文:

之前我们曾经讲过,之前推荐的做法是数据反范式, 即通过将数据存储多份的方式提高性能。 举个例子: 我们需要存储用户信息:

上面主键为username. 如果我们需要根据username来查询,会非常简单与快捷

可是当我们需要根据email进行查询的时候,当前的数据结构就不足以支撑我们的需求了。(如果你使用Allow Filtering做全表扫描,你可以跳过这一节了。。。)

原文来源:http://www.flyml.net/2016/10/30/cassandra-tutorial-materialized-view/

方案1: 反范式 (denormalization)

注意: 上面的主键已经变成了email。

这种方式其实还不错,只是我们在维护起来会比较费劲:

  • 需要维护多张表。
  • CRUD都需要多次,在代码层面也会比较麻烦
    如果需要根据company查询,又需要新增一张表,那么代码层面可能要全部的做一下调整
方案2: 二级索引(secondary index)

第二种方式是创建二级索引.:

在查询的时候,就跟普通的MySQL查询很类似了:

  • 优点: 使用简单、维护方便,代码层面不需要做改动。
  • 缺点: 性能大打折扣

我们来看看二级索引的原理: 二级索引,其实是自动创建了一个隐藏表,以上面的例子来说,会自动创建下面的隐藏表:

而在设计上,二级索引并不会进行全局分布,也就是说,虽然email作为partition key, 但是email仅限于本节点。

比如: abc@test.com 的原始数据在节点A,那么索引数据一定也要节点A。

个人的理解是:这是一种折中方案。 因为如果考虑到全局分区的话,还需要进行一致性、写性能等考虑。会比较复杂。但是这也不是不能实现的,实现了的版本就是我们今天的主题Materialized View。 因此,当我们通过email查询的时候,coordinator并不知道当前要去哪个节点查询数据,从而需要向所有的节点发出查询请求。并将各个节点的返回数据进行汇总。 如果节点的数量比较少,影响不大,但是如果节点数量比较多,集群规模比较大,网络的消耗就相当明显了。

另外,为什么称其为二级索引而不是索引 ? 个人理解: Primary key本身就是一级索引了。当我们进行数据操作的时候,默认是通过Primary key 找到行,而二级索引的作用是为了找到一级索引。

其实Index并不是一无是处:

当我们需要进行数据分析的时候,我们需要从很多甚至全部节点进行数据读取,并且响应时间并不是我们的重要指标的时候,索引还是挺好用的。 只不过这种方式另外一个缺点就是容易OOM。可能是因为存在hot spot, 并且数据都要汇总到coordinator~

目前还出现了一种优化之后的二级索引: SASI 笔者目前并没有了解过。 大家感兴趣可以进去看看。

方案3:Materialized View (简称MV)

中文翻译? 个人理解可以翻译成“物化视图”。

这里面有两个要点:
1. 物化 (materialized)
2. 视图 (view)

MV本身是一个视图,概念与MySQL的视图类似。 但是如果我们使用过MySQL的视图,我们知道MySQL之中的视图是一个虚拟表。而在Cassandra之中,MV是真正的存储着真实的数据,因此称为Materialized(物化)。

 

首先我们看看如何创建一个MV:

当原始表做了改动之后,视图并不需要跟着改动。 因为它的数据来源于select查询语句。

如果我们需要按照username与company进行查询,那么我们还可以创建下面的MV:

官方的说法:

Materialized views give you the performance benefits of denormalization, but are automatically updated by Cassandra whenever the base table is

我们再来看看官方的测试数据:

MV是如何工作的?

首先我们看看官方的介绍图:

 

之前我们也曾经说过,在Cassandra之中,Update == Insert。
举个例子:

无论之前是否存在这一条记录,最后我们都能查询到当username=wenjun_yang的时候,email = ‘new_email_addr@trendmicro.com.cn’

这是因为Update的过程是:

  1. 将数据写到commitLog
  2. 经过一段时间,这些数据最终会被写到硬盘上面
  3. 老的数据在下一次compaction的时候会从硬盘上面删除

即:当我们使用update的时候,并不会先去看原来是否有这一条。 而当我们使用了MV之后,update ≈ insert

这是因为:

  1. 执行的结果还是相同的
  2. 执行的过程已经不同了。
    此时update操作需要先read再写
    另外在一致性检查上面,MV做的事情也非常复杂。对性能也是有消耗。

借用官方的解释图片,首先我们看看使用二级索引是如何找到一条数据的:

然后我们对比着看看使用MV又是如何找到一条数据的:

实际上,MV是相当于自动新创建了一张表,由Cassandra自动为你维护! (根据我的理解,这一点跟MySQL的视图非常不一样,MySQL的视图实际上是一种虚拟表,并不真正的存储数据)

这对我们进行写与删除操作肯定是有影响的。

  1. 当我们更新数据的时候,会首先看看之前是否已经有了数据。如果有的话,需要先把MV之中的相关数据删除
  2. 当删除原始数据的时候,也需要把MV之中的数据打上tombostone的标记

官方的对比数据:

(1)MV与原始写的对比

(2)MV对比人工反范式

MV 性能总结:

  • 从MV读取数据跟从原始表读数据的性能是一样的
  • 写数据的时候,每一个MV性能会下降10%.
  • 如果Primary Key 只有1个,MV的性能要好于人工反范式.
  • 对于复合主键(Compund primary keys),MV相对来说还是要快的,但是当复合主键太长(比如>100)的时候,人工可能还要快一些
  • 如果原始表有大规模删除,MV需要发起很多CQL来查询与删除。性能可能会急剧下降
  • MV主键的设计还是要考虑是否会造成热点(hot spot).
    hot spot 依然会造成性能问题,甚至OOM

另外,当前MV只支持select。 (其他的在路上。。。)

本文为原创文章,转载请注明出处
原文链接:http://www.flyml.net/2016/10/30/cassandra-tutorial-materialized-view/
点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注