Cassandra介绍

简介

  Cassandra在2008年7月被Facebook开源。Cassandra最初的版本主要是由亚马逊(Amazon)和微软(Microsoft)的一名前雇员编写的。它深受亚马逊首创的分布式key/value数据库Dynamo的影响。Cassandra实现了一个没有单点故障的动态复制模型,但是添加了一个更强大的列家族数据模型。下面我基于对它的理解说说它的一些概念。

ring与vnodes

  cassandra集群的token的区间在 负2的63次方 2的63之间. 集群中所有的节点分分隔这个token环,老版本的cassandra采用静态分隔的方式,需要手工配置每个节点的token范围,并提供工具用于平均分隔这个环,但静态分隔维护困难且易出错,容易形成热点。在新版本的cassandra引入了虚拟节点(vnodes)概念,它把这个token环分隔为很多固定的段,且体多少段由每个节点的“num_tokens”配置决定,默认为256。初始时,cassandra把所以节点的num_tokens值相加,得到这个集群内存在vnodes的数量,然后把token均分,打散分散到各节点中。

Cassandra介绍_第1张图片

数据通过分区器(默认为Murmur3Partitioner)计算出hash值,比较决定落入哪个vnodes。

分区器

   一旦有数据进集群就不能修改分区器,配置参数为“partitioner”。用于把数据划归哪个vnodes的算法。默认分区器是org.apache.cassandra.dht.Murmur3Partitioner,但为兼容旧版本,还支持RandomPartitioner分区器,它是MD5方式,它比Murmur3Partitioner慢很多,所以被替换。cassandra还支持org.apache.cassandra.dht. OrderPreservingPartitioner分区器,这个分区器就是按key的顺序来分区,且物理存储也是按key的字母表顺序来存储,这就与Hbase的分区规则一样了,对key的设计非常重要,也就是把本该由系统来处理的数据倾斜问题上升为业务自己来处理,确实需要丰富的业务知识才能完成这一任务。如果key较均匀分布确实很棒,区间查询速度会快一些,但如果中key并不能按我们预想的那么均匀,那么它的查询并不会比Murmur3Partitioner快。另外支持的分区器是:ByteOrderedPartitioner,如果key不会转为string,可以考虑使用,这应该是适用于较特殊的领域了。

 虽然Murmur3Partitioner随机选择令牌,但它仍然容易受到热点的影响;但是,与保留顺序的分区器相比,这个问题大大减少了。事实证明,为了最小化热点,需要额外的拓扑知识。为了解决这个问题,3.0中增加了对令牌选择的改进。在cassandra中配置"allocate_tokens_for_keyspace"属性。具有特定名称空间,指示分区程序基于该名称空间的复制策略优化令牌选择。如果集群只有一个名称空间,或者所有名称空间都具有相同的复制策略,那么这种方法非常有用。在3.0版本中,这个选项只对Murmur3Partitioner可用。

复制策略

先看一段名称空间创建的示例:

CREATE KEYSPACE my_keyspace WITH replication =
{'class': 'SimpleStrategy',
'replication_factor': 1} AND durable_writes = true;

上面class就是指需要在这个名称空间上需要实施的复制策略,要写的第一个副本就是按分区器来分派的,replication_factor是指要复制的份数,即数据备份到1台服务器,但不能超过集群机器数量。否则会报"Not enough replicas available for query at consistency QUORUM "异常。命名空间的这个配置可以修改“ALTER KEYSPACE 名称空间 WITH replication = {‘class’: ‘SimpleStrategy’, ‘replication_factor’: ‘1’}”,如果减小了复制份数,需要在每个节点上执行命令: nodetool clean 完成数据的清理。常用的复制策略如下:

  • SimpleStrategy: 放的是单数据中心,它只关心ring环的下N个节点,并不关心它在不在一个机架上(拓扑)
  • NetworkTopologyStrategy: 适合多数据中心,不同数据中心会尽量放到不同的机架上,因为有同一机架有可能因为电源、冷却、网络问题同时失败。如果所有机架放满了,再放到相同机架上。
  • OldNetworkTopologyStrategy:与NetworkTopologyStrategy相同,但算法更简单它将第二个副本放在与第一个不同的数据中心中,将第三个副本放在第一个数据中心的不同机架中,再通过遍历环上的后续节点来放置任何剩余的副本。
  • LocalStrategy: cassandra保留复制策略。LocalStrategy只将数据保存在本地节点上,不将此数据复制到其他节点。Cassandra将此策略用于存储关于本地节点和集群中其他节点的元数据的系统名称空间。

告密者(snitch)

   通过“endpoint_snitch”属性进行配置,告密者会收集cassandra网络拓扑结构的信息,以便Cassandra能够有效地路由请求。告密者会找出节点与其他节点之间的关系(推断数据中心是复制策略的工作)。通过更新cassandra中的"endpoint_snitch"属性进行配置。它的实现都在包:org.apache.cassandra.locator. 中并且实现 了IEndpointSnitch接口。
  cassandra还提供了一个称为动态监控的特性,可以帮助优化读写路由。它是这样工作的。您选择的告密者被另一个称为DynamicEndpointSnitch的告密者包裹。动态告密者从所选的告密者获得对拓扑的基本理解。然后监控对其他节点的请求的性能,甚至跟踪哪些节点正在执行压缩。性能数据用于为每个查询选择最佳副本。这使
Cassandra避免将请求路由到性能较差的副本。cassandra的读取数据时,判断要读的副本是否本地有存储,如果没有在本地存储,则由动态告密者(dynamic snitch)那里知识一个速度最快的节点发送内容,同时向其它节点(由读一致性决定发送几个节点)发送摘要请求。所以配置合适的告密者对读性能非常重要。

  动态告密者如何判断节点性能最佳呢?cassandra 修改了的gossip协议,加入了Phi增量故障检测算法。为了避免由于网络波动而频烦修改性能最佳节点,可以合理配置 “dynamic_snitch_badness_threshold”属性,想要成为新的最佳节点,那么它必须比擂主(旧的最佳节点)的性能好于这个阀值才能替换掉擂主,每个节点的得分都会定期重置,以便性能较差的节点能够证明它已经恢复了最佳状态。常用的告密者有:

  • SimpleSnitch: 它不理解网络的拓扑结构,所以它不知道集群中的机架和数据中心,这使得它不适合多数据中心部署,注意,如果使用了此告密者,那个名称空间的复制策略也要设置为“SimpleStrategy”

  • PropertyFileSnitch: 用于配置的属性文件cassandra-topology.properties提供网络拓扑。如果使用此告密者或者其它具有机架感知功能的告密者时,那个名称空间的复制策略必须使用“NetworkTopologyStrategy”,这种方式对于频繁更改节点时不太方便,你让cassandra来计算数据中心和机架位置还不能直接告诉它来的更安全和直接,也能达到更好的性能。

  • GossipingPropertyFileSnitch:这也是一个具有机架感知功能的告密者,它通过gossip协议来传达整个cassandra集群的拓扑结构,每个节点都在cassandra-rackdc.properties文件中定义好自己所属的数据中心和机架信息,然后通过gossip协议来进行交换,最后形成一个整体的拓扑结构。如果存在cassandra-topology.properties文件,那么它也会起作用。

  • RackInferringSnitch:这个具有机架感知的告密者是跟据节点的ip地址来猜测数据中心和机架的。这个要求集群中的节点采用一致的网络方案。如果两个主机的IP地址的第二段有相同的值,那么它们将被确定位于同一个数据中心。如果两个主机的IP地址的第三段有相同的值,那么它们将被确定在同一个机架上。

读路径

cassandra如何执行查询数据操作?对于开发人员可以不关心,但对于架构师却不能不理,理解它对于性能优化,模型设计都会有帮助。

Cassandra介绍_第2张图片

  读路径:判断要读的副本是否是本地,如果没有在本地则由动态告密者(dynamic snitch)那里拿到一个性能最好的节点发送内容请求,同时向其它节点(由读一致性决定发送几个节点)发送摘要请求(摘要请求类似于标准读取请求,只是副本返回所请求数据的摘要或散列。),本地协调者从最快节点那拿到了内容和摘要信息,再与其它节点的摘要进行比较,如果摘要相同返回结果给客户端,如果摘要不同,则要发送读修复请求。

Cassandra介绍_第3张图片

查询分以下几部:

  • 第一步: 行缓存有没有要查询数据(由于耗内在较大原因,默认不开行缓存)
  • 第二步:查key缓存(如果查到,直接拿到偏移量去取数据,如果没继续搜索)
  • 第三步:查询memtables表数据(一张表只有一个memtables,搜索较为简单)
  • 第四步:查SSTables(由于一张表存在多个SSTables,逐一全文件搜索不现实,先用Bloom过滤器来过滤一下没有要搜索的数据SSTables,再跟据SSTable二层索引(一层索引摘要,一层索引内容)进行搜索)查到memtables和SSTables表后,合并数据,过滤墓碑数据返回给客户端(如果是摘要请求就计算摘要后返回)
  • 第五步:如果开启了行缓存,还需要把搜索到的数据更新到行缓存

Cassandra实现了几个特性来优化SSTable搜索: 键缓存、Bloom过滤器、SSTable索引和摘要索引。

读修复

读修复触发时机:

   读取修复可以在返回客户机之前或之后执行。如果您正在使用两个更强的一致性级别之一(QUORUM或ALL),那么读取修复将在数据返回给客户机之前进行。如果客户端指定了一个弱一致性级别(比如One),那么在返回客户端之后,可以选择在后台执行read repair。导致给定表的后台修复的读操作的百分比由表的read_repair_chance和dc_local_read_repair_chance选项决定。

  因为采用了强一致性,那么副本发生不致的概率非常小,那么代价就不大,在返回给客户机之前进行修复是可取的。如果采弱一致性,谁也不能保证不一致的副本有多少,那么先返回给客户机结果再修复是较明智的设计,至于要不要异步地在后端修复那是由要修改的副本占比决定,read_repair_chance就是那个阀值,超过阀值那么异步地后端修复,没超过直接修复。

读修复:

​ 协调器节点通过为每个请求列选择一个值来合并数据。它比较从副本返回的值,并返回具有最新时间戳的值。如果Cassandra发现不同的值存储在相同的时间戳中,它将按字典顺序比较这些值,并选择值较大的值。这种情况应该非常罕见。合并数据是返回给客户机的值。

写路径

cassandra如何落盘对于性能调优至关重要。

Cassandra介绍_第4张图片

如果与客户端连接的节点不在同一个数据中心时,cassandra会在别的数据中心选择一个协调器,并把数据发送给协调器,由协调器发送给本数据中心的其它副本。

Cassandra介绍_第5张图片

写操作:

  • 第一步: 写提交日志(与mysql的redo/undo的日志一样,属于物理日志,用于失败重放操作)
  • 第二步:回到Memtables(与ES的flush到内存操作类似,放到内存,避免了磁盘IO操作,提交写性能)
  • 第三步:写内存的缓存(如果有配置Row缓存才会执行此步骤,Memtables毕竟是临时存放,这个就是二级缓存,为了加快读速度, 但它会占用较大的内存, 默认是关闭的,key Caches默认是打开的)
  • 第四步:写SSTables(如果Memtables满了才会执行此步骤,与ES的刷新到磁盘相同原理,以顺序的append方式加到SSTables,顺序写的速度会快很多, 比Btree的覆盖写要快的多,如果SSTables满了会执行压缩操作)
  • 第五步:写hints(写动作是同步发请求到所有的副本的,如果其中有节点写失败,则要保存一个提示,如果达到了设置的写一致性的最低要求,那么仍然返回写成功)

  当然,这只是写路径的一个简单概述,没有考虑到诸如计数器修改和物化视图之类的变量。对具有实体化视图的表的写操作更为复杂,因为分区必须被锁定。Cassandra在内部利用已记录的批来维护物化视图。要更深入地讨论写路径,请参考Michael Edge在Apache Cassandra Wiki上的精彩描述。

一致性级别

cassandra特别灵活,各环境就可以自定义,向上面的读写路径的参数可配置,告密者、分区器、复制策略等都是可配置的。更进一步,cassandra可以让用户按需求执行自己配置的一致性级别。

读一致性:

级别 描述
ONE, TWO,THREE 立即返回响应查询的第一个节点所持有的记录。
创建一个后台线程来检查该记录与其他副本上的相同记录。
如果其中任何一个已经过期,则执行读修复将它们同步到最近的值。
LOCAL_ONE 与第一个类似,附加的要求是响应节点位于本地数据中心。
QUORUM 查询所有节点。一旦大多数副本((复制因子/ 2)+ 1)响应,返回给客户机
带有最新时间戳的值。然后,如果需要,在后台对所有剩余的副本执行读修复。
LOCAL_QUORUM 与QUORUM类似,附加的要求是响应节点位于本地数据中心。
EACH_QUORUM 确保每个数据中心中都有一个节点仲裁响应
ALL 查询所有节点。等待所有节点响应,并将带有最新时间戳
的记录返回给客户机。然后,如果需要,在后台执行读修复。
如果任何节点没有响应,则读取操作将失败。

写一致性:

级别 描述
ANY 确保在返回客户机之前将值写入至少一个副本节点,如果有节点写入失败,允许将hints算作写入。
ONE, TWO,
THREE
在返回客户机之前,确保将该值写入至少一个、两个或三个节点的提交日志和memtable。
LOCAL_ONE 与上面类似,附加的要求是响应节点位于本地数据中心。
QUORUM 确保至少大多数副本都收到了写操作((复制因子/ 2)+ 1)。
LOCAL_QUORUM 与QUORUM类似,其中响应节点位于本地数据中心。
EACH_QUORUM 确保每个数据中心中都有一个节点仲裁响应。
ALL 确保复制因子指定的节点数在返回到客户机之前接收到写操作。如果有一个副本对写操作没有响应,则操作失败。

设置一致性

通过CQL连接上的一致性级别为“ONE”

Cassandra介绍_第6张图片

代码设置一致性

	//全局设置一致性
	QueryOptions queryOptions = new QueryOptions();
		queryOptions.setConsistencyLevel(ConsistencyLevel.ANY);
		Cluster cluster = Cluster.builder().addContactPoint("localhost").withPort(9042).withQueryOptions(queryOptions)
				.build();

//statement设置一致性
PreparedStatement prepare = session
				.prepare("INSERT INTO rjzjh.users (id, userName, com) VALUES (?, ?, ?) IF NOT EXISTS");
		UUID id = UUID.fromString("756716f7-2e54-4715-9f00-91dcbea6cf52");
		BoundStatement bind = prepare.bind(id, "zjh2", "xplat");
		bind.setConsistencyLevel(ConsistencyLevel.ANY);

轻量级事物(LWT)

CAS

cassandra像其它nosql数据库一样不支持完整的ACID语义,但它仍支持轻量级的事物。它包含有下列语议

  • 每个事务的范围仅限于一个分区
  • 每个事务都由读和写组成,也称为“比较和设置”操作。只有在比较成功时才执行该集合。
  • 如果一个事务因为现有值与您期望的值不匹配而失败,Cassandra将包含当前值,这样您就可以决定重试还是中止,而不需要发出额外的请求。
  • 不支持“USING TIMESTAMP”选项

IF NOT EXISTS语法、、、、、、、、、、、、、、、、、、、、、、、、、
cqlsh> INSERT INTO hotel.hotels (id, name, phone) VALUES (
‘AZ123’, ‘Super Hotel at WestWorld’, ‘1-888-999-9999’) IF NOT
EXISTS;

UPDATE…IF
UPDATE hotel.hotels SET name=‘Super Hotel Suites at WestWorld’
… WHERE id=‘AZ123’ IF name=‘Super Hotel at WestWorld’;

检查事务结果:

	private void insert() {
		PreparedStatement prepare = session
				.prepare("INSERT INTO rjzjh.users (id, userName, com) VALUES (?, ?, ?) IF NOT EXISTS");
		UUID id = UUID.fromString("756716f7-2e54-4715-9f00-91dcbea6cf52");
		BoundStatement bind = prepare.bind(id, "zjh2", "xplat");
		bind.setConsistencyLevel(ConsistencyLevel.ANY);
		ResultSet rs = session.execute(bind);
		System.out.println("applied=" + rs.wasApplied());
		if (rs.wasApplied()) {
			Row row = rs.one();
			System.out.println("applied=" + row.getBool("[applied]"));
		} else {
			Row row = rs.one();
			System.out.println("applied=" + row.getBool("[applied]") + ",userName=" + row.getString("userName"));
		}
	}

当错误发生时,会输出旧的数据完整值:

applied=false
applied=false,userName=zjh

除了常规一致性级别外,条件写语句还可以具有串行一致性级别,CAS依赖Paxos协议来达成分布式共识。这些操作有一个paxos阶段和一个提交阶段。前者的一致性级别是在使用setSerialConsistencyLevel()的语句上设置的,而后者的一致性级别是使用常用的setConsistencyLevel()方法定义的。串行一致性当参与的节点正在协商提议的写时,必须回复的节点数量。下面显示了两个可用选项

级别 描述
SERIAL 这是默认的串行一致性级别,指示节点仲裁必须响应。
LOCAL_SERIAL 类似于SERIAL,但表示事务只涉及本地数据中心中的节点。

批处理

虽然轻量级事务仅限于单个分区,Cassandra提供了一个批处理机制,允许将对多个分区的修改分组到一个语句中。批处理事物支持的语义如下:

  • 批处理中只能包含修改语句(插入、更新或删除)。
  • 批处理是原子性的——也就是说,如果批处理被接受,批处理中的所有语句最终都会成功。这就是为什么
    Cassandra的批有时称为原子批或日志批。
  • 属于给定分区key的批中的所有更新都是在隔离状态下执行的,但是不能保证分区之间的隔离。这意味着可以在批处理完成之前读取对不同分区的修改。
  • 批处理不是事务机制,但是可以在批处理中包含轻量级事务语句。批处理中的多个轻量级事务必须应用于同一个分区。
  • 计数器修改只允许在称为计数器批处理的特殊批处理形式中进行。计数器批处理只能包含计数器修改。

三种批类型:

  • 日志批(LOGGED): 批提交后首先写到分布式批日志中(distributed batch log),保证它的原子性。
  • 非日志批(UNLOGGED):不写分布式日志,不能保证原子性
  • 计数器批(COUNTER): 所有的操作只能是计数器操作(counter operations)

使用示例

代码如下:

	private void insertBatch() {
		SimpleStatement st1 = new SimpleStatement("INSERT INTO rjzjh.users (id, userName, com) VALUES (?, ?, ?)",
				UUID.fromString("756716f7-2e54-4715-9f00-91dcbea6cf53"), "zjh3", "xplat3");
		SimpleStatement st2 = new SimpleStatement("INSERT INTO rjzjh.users (id, userName, com) VALUES (?, ?, ?)",
				UUID.fromString("756716f7-2e54-4715-9f00-91dcbea6cf54"), "zjh4", "xplat4");
		BatchStatement batch = new BatchStatement();
		batch.add(st1);
		batch.add(st2);
		ResultSet rs = session.execute(batch);
		System.out.println("applied=" + rs.wasApplied());
	}

CQL:

cqlsh> BEGIN BATCH
INSERT INTO hotel.hotels (id, name, phone)
VALUES ('AZ123', 'Super Hotel at WestWorld', '1-888-999-9999');
INSERT INTO hotel.hotels_by_poi (poi_name, id, name, phone)
VALUES ('West World', 'AZ123', 'Super Hotel at WestWorld',
'1-888-999-9999');
APPLY BATCH;

批的性能

批量操作指是的原子批,并不能提高性能,并不是批处理。第一次使用批量更新时,用户常常会混淆批量更新的性能。但事实批处理并不能提高性能,实际上会降低性能,并可能导致垃圾收集压力。

二级索引与物化视图

查询限制

cassandra的查询限制条件较多,查询的条件必需是在PRIMARY KEY中出现的列,其它列不能用于查询。如:

PRIMARY KEY (hotel_id, date, room_number)

查询条件可以是: WHERE hotel_id=‘AZ123’ and date>‘2016-01-05’ and date<‘2016-01-12’;

但不能是:WHERE hotel_id=‘AZ123’ and room_number=101; 这样会报错:InvalidRequest: code=2200 [Invalid query] message=“PRIMARY KEY column “room_number” cannot be restricted as preceding column “date” is not restricted”

也就是说,前面的查询条件“date”没有出现时并不能出现后面的查询条件“room_number”。cassandra可以允许分区键“hotel_id”没有出现,只需要上关键字:“ALLOW FILTERING”,如:

WHERE date=‘2016-01-25’ ALLOW FILTERING;

但cassandra不推荐使用: ALLOW FILTERING 因为它可能导致非常昂贵的查询。如果您发现自己需要这样的查询,您将需要重新检查数据模型,以确保您已经设计了支持查询的表。

二级索引

由于查询有诸多的限制,需要对模型的设计比较慎重,但在某些场景下面,却实又存在对非PRIMARY KEY上的查询要求,如之前的设计由于新的需求导致查询字段并没有出现在PRIMARY KEY中,这个时候可以考虑采用二级索引来实现。语法示例:

CREATE INDEX user_last_name_idx ON my_keyspace.user (last_name);

这样我也就可以直接用这个字段进行查询:WHERE last_name = ‘Nguyen’;

  二级索引主要的难点是不能规整地映射到分区中,有两种主要的方法来支持二级索引进行分区:基于文档的分区和基于词条的分区。cassandra和Elasticsearch等采用的是基于文档分区的二级索引,它在分个分区上各自维护自己的二级索引,每个分区只管自己文档面不关心其它分区的文档,文档分区索引也称为本地索引,并不是全局索引。在读取时一般来说需要读所有分区(除非做做特殊处理才能达到只读一个分区)的二级索引,查询到结果后进行合并最后返回正结果。这种查询分区数据为听方法称为分散/聚集,很明显这种二级索引的代价确实高昂。所以要求我们能构建合适的分区方案,尽量由单个分区就能满足二级索引查询。

  由于这个原因,涉及二级索引的查询通常涉及更多的节点(也许是全部节点),这样会使得查询的开销大大增加。我们不能比较随意设置我们的二级索引,cassandra对二级索引字段的优化有些要求,下面三种情况不能建索引:

  • 高基数:字段全是唯一的,如:id ,email

  • 低基数: 字段全是一样的,如:男、女

  • 经常性做删除和更新的字段

SASI(SSTable Attached Secondary Index)

Cassandra 3.4版本包含了二级索引的另一种实现,称为SSTable Attached secondary Index (SASI)。SASI是由苹果公司开发的,并作为Cassandra的二级索引API的开源实现发布。顾名思义,SASI索引是作为每个SSTable文件的一部分计算和存储的,而前面提到的旧的二级索引实现原理是做为一张独立的“隐藏”表。它的创建语法示例:

CREATE CUSTOM INDEX user_last_name_sasi_idx ON user (last_name)
USING 'org.apache.cassandra.index.sasi.SASIIndex';

SASI索引提供了传统二级索引实现不了的功能,比如在索引列上执行不等式(大于或小于)查询的能力。还可以使用新的CQL LIKE关键字对索引列进行文本搜索。例如,您可以使用以下查询来查找姓氏以N开头的用户:

SELECT * FROM user WHERE last_name LIKE 'N%';

尽管SASI索引确实比传统索引执行得更好,因为它不需要从其他表读取数据,但它无论如何也逃脱不了需要多节点的查询汇聚需求。

物化视图

  上面提及的二级索引,由于PRIMARY KEY没有改变,也就是意味了它的数据存储位置是不变的,无论做什么索引都不能改变需要查多个节点做汇聚步骤,查询的开销避免不了,物化视图则是最彻底的以空间换时间的方案,它可以把之间的非PRIMARY KEY列提升为PRIMARY KEY,由cassandra完成原数据到物化视图的数据更分发的工作。如:

Cassandra介绍_第7张图片

它比较适合二级索引处理不了的情形,如“高基”字段,如上面的确认号"confirm_number",它不可能存在相同的确认号,那用它来做物化视图中的分区键就比较合适了。它也可以简化应用,如果要满足不同场景的查询需要多个逻辑模型,虽然是同一份数据,应用程序也要保证多份物理模型的同步工作,有了物化视图,就无需这么麻烦,应用程序只同步一份数据,通过物化视图,cassandra内部保证了多份模型的数据一致性。它会改变写路径,对写的性能会有些损耗。创建物化视图示例:

cqlsh> CREATE MATERIALIZED VIEW
reservation.reservations_by_confirmation
AS SELECT *
FROM reservation.reservations_by_hotel_date
WHERE confirm_number IS NOT NULL and hotel_id IS NOT NULL and
start_date IS NOT NULL and room_number IS NOT NULL
PRIMARY KEY (confirm_number, hotel_id, start_date, room_number);

主键列的分组使用与普通表相同的语法。最常见的用法是首先将附加列作为分区键,然后是基表主键列,用于物化视图的聚类列。AS SELECT子句标识了我们希望物化视图包含的基表中的列。我们可以引用单独的列,但在本例中,通过使用通配符*选择所有列都作为视图的一部分。物化视图也有一些限制条件:

  • 物化视图必须包含基表主键中的所有列。这个限制使Cassandra避免将基表中的多行折叠成物化视图中的一行,这将大大增加管理更新的复杂性。

  • WHERE子句提供了对过滤的支持。注意,必须为物化视图的每个主键列指定一个过滤器,即使它非常简单,只需指定值不为空即可。

压缩存储

建表语句可以带“COMPACT STORAGE”关键字表示需要对这张表进行压缩存储。如:

CREATE TABLE playlists_2 (   id uuid,   song_id uuid, title text,
PRIMARY KEY  (id, song_id )
) WITH COMPACT STORAGE;

cassandra在存储到SSTable时,它会把记录数据进行合并压缩存储,如未采用压缩存储的数据:

$ sstable2json Metrics/playlists_1/*Data*

[
    {
        "columns": [
            [
                "7db1a490-5878-11e2-bcfd-0800200c9a66:",
                "",
                1436971955597000
            ],
            [
                "7db1a490-5878-11e2-bcfd-0800200c9a66:title",
                "Ojo Rojo",
                1436971955597000
            ]
        ],
        "key": "62c3609282a13a0093d146196ee77204"
    },
    {
        "columns": [
            [
                "aadb822c-142e-4b01-8baa-d5d5bdb8e8c5:",
                "",
                1436971955602000
            ],
            [
                "aadb822c-142e-4b01-8baa-d5d5bdb8e8c5:title",
                "Guardrail",
                1436971955602000
            ]
        ],
        "key": "444c3a8a25fd431cb73e14ef8a9e22fc"
    }
]

采用存储压缩的数据格式:

[
    {
        "columns": [
            [
                "7db1a490-5878-11e2-bcfd-0800200c9a66",
                "Ojo Rojo",
                1436972070334000
            ]
        ],
        "key": "62c3609282a13a0093d146196ee77204"
    },
    {
        "columns": [
            [
                "aadb822c-142e-4b01-8baa-d5d5bdb8e8c5",
                "Guardrail",
                1436972071215000
            ]
        ],
        "key": "444c3a8a25fd431cb73e14ef8a9e22fc"
    }
]

跟据相关的测试,结果显示无压缩存储将多需要大约 35% 的存储空间(并且任何额外的处理都需要如此)。这样就清楚地知道了在我们写入的数据卷当中,非压缩存储中 35% 的存储是没有被有效利用正常工作的。那么那里为什么会有一个选项?cassandra不直接全部实施压缩存储?

  压缩存储也有它的限制条件,就是除了Partition Key和Clustering Key两部分组成的Primary Key字段外,只能出现列非Partition Key,而且也不能新增和删除表的字段。如果在你的数据模式中需要更多的列值,你有两个选择:不使用压缩存储,或者将他们序列化成单一列数据值。

静态列(static column)

与java的静态字段类似,静态列可以被多条记录共用,它对于同一个Partition Key使用的字段都是都存储一份的,这样不但大大减少了存储空间,而且对于管理也及为方便。下面是一个定义的示例:

CREATE TABLE "iteblog_users_with_status_updates" (
  "username" text,
  "id" timeuuid,
  "email" text STATIC,
  "encrypted_password" blob STATIC,
  "body" text,
  PRIMARY KEY ("username", "id")
);

上面由“STATIC”关键字标识的列都属于静态列。静态列也有下面的限制条件:

  • 如果表没有定义 Clustering columns(又称 Clustering key),不能添加静态列.

  • 如果建表的时候指定了 COMPACT STORAGE,这时候也不允许存在静态列。

  • 如果列是 partition key/Clustering columns 的一部分,那么这个列不能说明为静态列。

使用实践

静态列的这些特性用于做表的关联非常有用。因为 Cassandra 中是不支持 join 的,静态列相当于把两张表进行了 join 操作。我们可以对事实表进行建模,对把维表做成一个自定义类型,并设置为静态列。还有就是对明细表建模时把那个对应主档表的关联字段设置为partition key,那么再把主档表想引入的字段全部设置为静态列,是不是就达到了join的目的?而且此时,对主档的任何修改只要带一个“partition key”查询条件更新就可以了。这样明细表的所有关联在“partition key”下的记录都被同时修改了,对于在静态字段的任何distinct都不用担心性能。

其它配置

  • rpc_address: 监听地址,它与rpc_interface两个属性设置一个就可以了
  • rpc_port:rpc端口默认为9160,Thrift客户端要连接的端口,但它慢慢地废弃,尽量不要使用Thrift。
  • listen_interface:监听地址,它与listen_interface两个属性设置一个就可以了。默认是localhost,如果想要搭建一个集群的话,这个配置必需修改。
  • storage_port:属性指定用于节点间通信的端口,默认为7000。如果您将在穿越公共网络的网络环境中使用Cassandra,或者在云部署中使用多个区域,那么应该配置ssl_storage_port(默认为7001)。配置安全端口还需要配置节点间加密选项。
  • start_native_transport:是否启用本地传输端口,默认为true,绑定的地址就rpc_address属性配置
  • native_transport_port: 只有在start_native_transport为true才有效,就是用于CQL监听的端口,默认9042
  • rpc_keepalive: rpc保活,默认为true,它对rpc/native两种协议都支持,还有其它属来设置线程数, 连接数和 帧大小
  • data_file_directories: 数据文件存储路径,可以支持多个路径,如果有多个磁盘时可指向不同挂载点的目录
  • commitlog_directory: 提交日志存储路径
  • disk_failure_policy:磁盘失败采用的策略
  • commit_failure_policy: 提交日志失败采用的策略
  • jvm: 需要修改jvm.options文件,默认情况,机器小于1G内存,则堆设置为机器的50%,如果堆 大于4G内存,则堆设置为机器的25%,最大为8G。如果采用CMS的垃级收集器,建议堆大小不能超过8G,因为那样会导致垃级收集时间较长。

你可能感兴趣的:(大数据)