摘要:
在传统数据库之中,视图是个非常有用的功能,NoSQL一般不具有这种能力。 另外,对于NoSQL来说,如何设计数据模型,通常来说需要一开始就非常明确自己的数据是如何存储的,以此来设计自己的存储方式。 甚至会通过存储多份数据的方式来达到目的。而对于Cassandra来说,Materialized View(物化视图)是非常实用、好用的功能,可以同时比较完美的解决NoSQL的视图缺失与对降低对数据模型扩充能力的设计要求。
原文来源:http://www.flyml.net/2016/10/30/cassandra-tutorial-materialized-view/
正文:
之前我们曾经讲过,之前推荐的做法是数据反范式, 即通过将数据存储多份的方式提高性能。 举个例子: 我们需要存储用户信息:
1 2 3 4 5 6 7 |
CREATE TABLE user ( username text, password text, email text, company text, PRIMARY KEY (username) ); |
上面主键为username. 如果我们需要根据username来查询,会非常简单与快捷
1 |
select * from user where username = ? |
可是当我们需要根据email进行查询的时候,当前的数据结构就不足以支撑我们的需求了。(如果你使用Allow Filtering做全表扫描,你可以跳过这一节了。。。)
原文来源:http://www.flyml.net/2016/10/30/cassandra-tutorial-materialized-view/
方案1: 反范式 (denormalization)
1 2 3 4 5 6 7 |
CREATE TABLE user_by_email ( email text, username text, password text, company text, PRIMARY KEY (email) ); |
注意: 上面的主键已经变成了email。
这种方式其实还不错,只是我们在维护起来会比较费劲:
- 需要维护多张表。
- CRUD都需要多次,在代码层面也会比较麻烦
如果需要根据company查询,又需要新增一张表,那么代码层面可能要全部的做一下调整
方案2: 二级索引(secondary index)
第二种方式是创建二级索引.:
1 |
CREATE INDEX user_email_index ON user(email); |
在查询的时候,就跟普通的MySQL查询很类似了:
1 |
select * from user where email = ? |
- 优点: 使用简单、维护方便,代码层面不需要做改动。
- 缺点: 性能大打折扣
我们来看看二级索引的原理: 二级索引,其实是自动创建了一个隐藏表,以上面的例子来说,会自动创建下面的隐藏表:
1 2 3 4 5 |
CREATE TABLE email_index( email text, username text, PRIMARY KEY((email), username) ); |
而在设计上,二级索引并不会进行全局分布,也就是说,虽然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:
1 2 3 4 |
创建MVCREATE MATERIALIZED VIEW user_by_email AS SELECT * FROM user WHERE email IS NOT NULL PRIMARY KEY (email, username); |
当原始表做了改动之后,视图并不需要跟着改动。 因为它的数据来源于select查询语句。
如果我们需要按照username与company进行查询,那么我们还可以创建下面的MV:
1 2 3 4 |
CREATE MATERIALIZED VIEW user_by_company AS SELECT company, username, email FROM user WHERE company IS NOT NULL AND email IS NOT NULL PRIMARY KEY (company, username); |
官方的说法:
Materialized views give you the performance benefits of denormalization, but are automatically updated by Cassandra whenever the base table is
我们再来看看官方的测试数据:
MV是如何工作的?
首先我们看看官方的介绍图:
之前我们也曾经说过,在Cassandra之中,Update == Insert。
举个例子:
1 2 3 |
UPDATE users SET email = 'new_email_addr@163.com' WHERE username = 'wenjun_yang'; |
无论之前是否存在这一条记录,最后我们都能查询到当username=wenjun_yang的时候,email = 'new_email_addr@163.com'
这是因为Update的过程是:
- 将数据写到commitLog
- 经过一段时间,这些数据最终会被写到硬盘上面
- 老的数据在下一次compaction的时候会从硬盘上面删除
即:当我们使用update的时候,并不会先去看原来是否有这一条。 而当我们使用了MV之后,update ≈ insert
!
这是因为:
- 执行的结果还是相同的
- 执行的过程已经不同了。
此时update操作需要先read再写。
另外在一致性检查上面,MV做的事情也非常复杂。对性能也是有消耗。
借用官方的解释图片,首先我们看看使用二级索引是如何找到一条数据的:
然后我们对比着看看使用MV又是如何找到一条数据的:
实际上,MV是相当于自动新创建了一张表,由Cassandra自动为你维护! (根据我的理解,这一点跟MySQL的视图非常不一样,MySQL的视图实际上是一种虚拟表,并不真正的存储数据)
这对我们进行写与删除操作肯定是有影响的。
- 当我们更新数据的时候,会首先看看之前是否已经有了数据。如果有的话,需要先把MV之中的相关数据删除
- 当删除原始数据的时候,也需要把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/

文章评论