Cassandra最初源于Facebook, 但国内使用的公司并不多,远没有Hbase和MongoDB多。
Cassandra没有像BigTable或HBase那样选择中心控制节点,而选择了无中心的P2P架构,网络中的所有节点都是对等的,它们构成了一个环,节点之间通过P2P协议每秒交换一次数据,这样每个节点都拥有其它所有节点的信息,包括位置、状态等。
1、Cassandra Ring
客户端可以连接集群中的任一个节点,和客户端建立连接的节点叫协作者(coordinator),它相当于一个代理,负责定位该次请求要发到哪些实际拥有本次请求所需数据的节点上去获取,但如何获取并返回,主要根据客户端要求的一致性级别(Consistency Level)来定,比如:ONE指等于只要有一个节点返回数据就可以对客户端做出响应,QUONUM指需要返回几个根据用户配置数目,ALL指等于数据复制份数的所有节点都返回结果才能向客户端做出响应,对于数据一致性要求不是特别高的可以选择ONE,它是最快的一种方式。
2、Cassandra的核心组件
(1)Gossip
点对点的通讯协议,用来相互交换节点的位置和状态信息。当一个节点启动时就立即本地存储Gossip信息,但当节点信息发生变化时需要清洗历史信息,比如IP改变了,通过Gossip协议,每个节点它期交换自己和它已经交换过信息的节点的数据,每个被交换的信息都有一个版本号,这样当有新的数据时可以覆盖老数据,为了保证数据交换的准确性,所有的节点必须使用同一份集群列表,这样的节点又被称作seed.
(2)Partioner
负责在集群中分配数据,由它来决定哪些节点放置第一份copy,一般情况会使用Hash来做主键,将每行数据分布到不同的节点上,以确保集群的可扩展性。
(3)Replica placement strategy:
复制策略,确定哪个节点放置复制数据,以及复制的份数。
(4)Snitch
定义一个网络拓扑图,用来确定如何放置复制数据,高效地路由请求。
(5)cassandra.yaml:主配置文件,设置集群的初始化配置、表的缓存参数、调优参数和资源使用、超时设定、客户端连接、备份和安全。
3、写请求
当写事件发生时,首先由commit log捕获写事件并持久化,保证数据的可靠性。之后数据也会被写入到内存中,叫做memtable,当内存满了之后写入数据文件,叫SSTable,它是Log-Structured Storage Table的简称。如果客户端配置了Consistency Level是ONE,意味着只要有一个节点写入成功,就由代理节点(Coordinator)返回给客户端写入完成。当然这中间有可能会出现其它节点写入失败的情况,Cassandra自己会通过Hinted Handoff或Read Repaire或者Anti-entropy Node Repaire方式保证数据最终一致性。
对于多数据中心的写入请求,Cassandra做了优化,每个数据中心选取一个Coordinator来完成它所在数据中心的数据复制,这样客户端连接的节点只需向数据中心的一个节点转化复制请求即可,由这个数据中心的Coordinator来完成该数据中心内的数据复制。
Cassandra的存储结构类似LSM树(Log-Structured Merge Tree)这种结构,写入可以并发写入,不像B+树一样需要加锁。
Commit Log记录每次写请求的完整信息,使用append only方式,Commit Log会在Memtable中的数据刷入SSTable后被清除,因此它不会占用太多磁盘空间。
写入Memtable时,Cassandra能够动态为它分配内存空间。
每个表会包含多个Memtable和SSTable。
(1)hinted handoff
首先介绍下Quorum机制,Quorum机制是分布式系统中常用的用来保证数据冗余和最终一致性的投票算法。N代表总的副本数,R表示读需要的副本数,W表示写需要的副本数。Quorum机制要求必须满足以下两个约束:
W+R>N
W>N/2
第一条规则保证了一个数据副本不会同时被读写。
第二条规则保证了数据的串行化修改。一份数据的多个副本不可能同时被两个写请求修改。
Quorum机制存在一个tradeoff,当越大时,R就可以越小,这样写开销较大,读开销越小。反之,W越小,R就需要越大,这样写开销小,读开销就大。在系统设计时,根据场景需要可以自由调整参数。
在Quorum机制的一些场景下,如果某台节点failover了,可能会导制写不可用。举个例子,对于N=3,W=3,R=1的场景,如果宕机一台节点,这个时候所有的写都不可用了。为了避免这种情况对写可用性的影响,在Dynamo的论文中提出了hinted handoff方法,使用了一种‘sloppy quorum’机制。
假设存在A、B、C、D四个节点,某个副本存在于ABC三个节点上,此时A宕机,那么原先写入A的副本数据可以暂时写到D上并标记。等A恢复后,D再把为A记的增量数据回传给A,通过这个方法,可以提高写的高可用。
(2)read repaire
当客户端并发从多个节点读数据时,它可以发现过时的响应,并把新的值写回过期的备份里。适合常读取的数据。
(3)anti-entroy
这种机制只用于永久性的错误恢复,另外为了将节点的数据传输降到最低,在实际数据传输并,各节点交换的最自己的那份据的message digest。
4、读请求
读取数据时,首先检查Bloom filter,每个SSTable都有一个Bloom filter用来检查parition key是否在这个SSTable,如果存在,再检查partition key cache,然后再做如下操作:
如果在cache中能找到索引,到compression offset map中找拥有这个数据的数据块,从磁盘上取得压缩数据并返回结果集。如果在cache中找不到索引,搜索partition summary确定索引在磁盘上的大概位置,然后获取索引入口,在SSTable上执行一次单独的寻道和一个顺序读取操作,读数据时会合并Memtable中缓存的数据、多个SSTable中的数据,才返回最终的结果。
5、数据整理(Compaction)
更新操作不立即更新,Cassandra会把数据顺序写入到一个新的SSTable,并打上时间戳标明数据的新旧。它也不会立马做删除操作,而是用Tombstone来标记要删除的数据。Compaction时将多个SSTable文件中的数据整合到新的SSTable文件中,当旧的SSTable上的读请求一完成,会被立即删除,空余出来的空间可以重新利用。