Cassandra是点对点分布式系统,集群中各节点平等,数据分布于集群中各节点,各节点间每秒交换一次信息。每个节点的commit log捕获写操作来确保数据持久性。数据先被写入memtable-内存中的数据结构,待该结构满后数据被写入SSTable-硬盘中的数据文件。所有的写内容被自动在集群中分区并复制。
Cassandra数据库面向行。授权用户可连接至任意数据中心的任意节点,并通过类似SQL的CQL查询数据。集群中,一个应用一般包含一个keyspace,一个keyspace中包含多个表。
客户端连接到某一节点发起读或写请求时,该节点充当客户端应用与拥有相应数据的节点间的协调者(coordinator)以根据集群配置确定环中的哪个节点当获取这个请求。
关键词
Ø Gossip:点对点通信协议,用以Cassandra集群中节点间交换位置和状态信息。
Ø Partitioner:决定如何在集群中的节点间分发数据,也即在哪个节点放置数据的第一个replica。
Ø Replica placement strategy:决定在哪些节点放置数据的其他replica。Cassandra在集群中的多个节点存储数据的多份拷贝-replicas来确保可靠和容错。
Ø Snitch:定义了复制策略用来放置replicas和路由请求所使用的拓扑信息
Ø cassandra.yaml文件:Cassandra主配置文件
Ø system: Cassandra的系统keyspace,存放table、keyspace的属性信息等。而属性信息可通过CQL或其他驱动设置。
1. 节点间通信
Cassandra使用点对点通讯协议gossip在集群中的节点间交换位置和状态信息。gossip进程每秒运行一次,与至多3个其他节点交换信息,这样所有节点可很快了解集群中的其他节点信息。
配置gossip(在cassandra.ymal中设置)
Ø cluster_name:节点所属集群名,集群中每个节点应相同。
Ø listen_address:供其他节点连接至该节点的IP地址或主机名,当由localhost设为公共地址。
Ø seed_provider:逗号分隔的IP地址(种子列表),gossip通过种子节点学习环的拓扑,集群中各节点种子列表当相同。多数据中心集群中每个数据中心的种子列表当至少包含一个该中心内的节点。
Ø storage_port:节点间通讯端口,集群中各节点当一致。
Ø initial_token:用于single-node-per-token结构,节点在环空间只拥有一段连续的token范围。
Ø num_tokens:用于virtual nodes,定义了节点在环空间所拥有的随机分配的token数目。
失败检测与恢复
Ø gossip可检测其他节点是否正常以避免将请求路由至不可达或者性能差的节点(后者需配置为dynamic snitch方可)。
Ø 可通过配置phi_convict_threshold来调整失败检测的敏感度。
Ø 对于失败的节点,其他节点会通过gossip定期与之联系以查看是否恢复而非简单将之移除。若需强制添加或移除集群中节点需使用nodetool工具。
Ø 一旦某节点被标记为失败,其错过的写操作会有其他replicas存储一段时间(需开启hinted handoff,若节点失败的时间超过了max_hint_window_in_ms,错过的写不再被存储。)Down掉的节点经过一段时间恢复后需执行repair操作,一般在所有节点运行nodetool repair以确保数据一致。
2. 数据复制和分发
Cassandra中分发、复制同时进行。Cassandra被设计为点对点系统,会创建数据的多个副本存储在集群中的一组节点中。Cassandra中数据被组织为表,由primary key标识,primary key决定数据将被存储在哪个节点。
需指定的内容
Virtual nodes:指定数据与物理节点的所属关系
Partitioner:在集群内划分数据
Replicationstrategy:决定如何处理每行数据的replicas
Snitch:定义replicationstrategy放置数据的replicas时使用的拓扑信息
一致性哈希
表中每行数据由primary key标识,Cassandra为每个primarykey分配一个hash值,集群中每个节点拥有一个或多个hash值区间。这样便可根据primary key对应的hash值将该条数据放在包含该hash值的hash值区间对应的节点中。
虚拟节点
使用虚拟节点控制数据在集群中的分布
若不使用虚拟节点则需手工为集群中每个节点计算和分配一个token。每个token决定了节点在环中的位置以及节点应当承担的一段连续的数据hash值的范围。如上图上半部分,每个节点分配了一个单独的token代表环中的一个位置,每个节点存储将row key映射为hash值之后落在该节点应当承担的唯一的一段连续的hash值范围内的数据。每个节点也包含来自其他节点的row的副本。而是用虚拟节点允许每个节点拥有多个较小的不连续的hash值范围。如上图中下半部分,集群中的节点是用了虚拟节点,虚拟节点随机选择且不连续。数据的存放位置也由row key映射而得的hash值确定,但是是落在更小的分区范围内。
使用虚拟节点的好处
Ø 无需为每个节点计算、分配token
Ø 添加移除节点后无需重新平衡集群负载
Ø 重建死掉的节点更快
Ø 改善了在同一集群使用异种机器
数据复制
Cassandra在多个节点中存放replicas以保证可靠性和容错性。replicationstrategy决定放置replicas的节点。replicas的总数由复制因子- replication factor确定,比如因子为2代表每行有两份拷贝,每份拷贝存储在不同的节点中。所有的replicas无主从之分。replication factor通常不能超过集群中节点总数。然而,可现增加replication facto之后在将节点增至期望的数量。当replication factor超过总结点数时,写操作被拒绝,但读操作可进行,只要满足期望的一致性级别。
当前有两种可用的复制策略:
Ø SimpleStrategy:仅用于单数据中心,将第一个replica放在由partitioner确定的节点中,其余的replicas放在上述节点顺时针方向的后续节点中。
Ø NetworkTopologyStrategy:可用于较复杂的多数据中心。可以指定在每个数据中心分别存储多少份replicas。在每个数据中心放置replicas的方式类似于SimpleStrategy,但倾向于将replicas放在不同rack,因为同一rack的节点倾向于同时失败。配置每个数据中心分别放置多少replicas时要考虑两个主要方面:(1)可满足本地读而非跨数据中心读;(2)失败场景。两种常用的配置方式为(1)每个数据中心两份replicas,(2)每个数据中心3份replicas。当然,用于特殊目的的非对称配置也是可以的,比如在读操作较频繁的数据中心配置3份replicas而在用于分析的数据中心配置一份replicas。
复制策略在创建keyspace时指定,如
CREATEKEYSPACE Excelsior WITH REPLICATION = { 'class' : 'SimpleStrategy','replication_factor' : 3 };
CREATEKEYSPACE "Excalibur" WITH REPLICATION = {'class' :'NetworkTopologyStrategy', 'dc1' : 3, 'dc2' : 2};
其中dc1、dc2这些数据中心名称要与snitch中配置的名称一致。
3. Partitioners
在Cassandra中,table的每行由唯一的primarykey标识,partitioner实际上为一hash函数用以计算primary key的token。Cassandra依据这个token值在集群中放置对应的行。
三种partitioner(在cassandra.yaml中设置)
Ø Murmur3Partitioner:当前的默认值,依据MurmurHash哈希值在集群中均匀分布数据。
Ø RandomPartitioner:依据MD5哈希值在集群中均匀分布数据。
Ø ByteOrderedPartitioner:依据行key的字节从字面上在集群中顺序分布数据。(不推荐使用)
Murmur3Partitioner和RandomPartitioner使用token向每个节点指派等量的数据从而将keyspace中的表均匀分布在环中,即使不同的表使用不同的primary key。读写请求均被均匀的分布。ByteOrderedPartitioner允许通过primary key顺序扫描(可通过index达到同样目的),但已引起如下问题(1)较复杂的负载均衡,(2)顺序的写易导致热点,(3)多表不均匀的负载均衡。
注意:若使用虚拟节点(vnodes)则无需手工计算tokens。若不使用虚拟节点则必须手工计算tokens将所得的值指派给cassandra.ymal主配置文件中的initial_token参数。具体可参考:http://www.datastax.com/documentation/cassandra/2.0/webhelp/index.html#cassandra/architecture/../configuration/configGenTokens_c.html
4. Snitches
提供网络拓扑信息,用以确定向/从哪个数据中心或者网架写入/读取数据。
注意:(1)所有节点需用相同的snitch;(2)集群中已插入数据后由更改了snitch则需运行一次fullrepair。
Ø Dynamic snitching
监控从不同replica读操作的性能,选择性能最好的replica。dynamic snitch默认开启,所有其他snitch会默认使用dynamic snitch 层。
Ø SimpleSnitch
默认值,用于单数据中心部署,不使用数据中心和网架信息。使用该值时keyspace复制策略中唯一需指定的是replication factor
Ø RackInferringSnitch
根据数据中心和网架确定节点位置,而数据中心及网架信息又有节点的IP地址隐含指示。
Ø PropertyFileSnitch
根据数据中心和网架确定节点位置,而网络拓扑信息又由用户定义的配置文件cassandra-topology.properties 获取。在节点IP地址格式不统一无法隐含指示数据中心及网架信息或者复杂的复制组中使用该值。需注意的是:(1)配置文件中数据中心名需与keyspace中复制策略中指定的数据中心名称一致;(2)配置文件中需包含集群中任一节点;(3)集群中各节点内cassandra-topology.properties配置文件需相同。
Ø GossipingPropertyFileSnitch
Ø 在cassandra-rackdc.properties配置文件中定义本节点所属的数据中心和网架,利用gossip协议与其他节点交换该信息。若从PropertyFileSnitch切至该值,则需逐节点逐次更新值为GossipingPropertyFileSnitch以确保gossip有时间传播信息。
Ø EC2Snitch
用于部署在Amazon EC2中且所有节点在单个区域中的集群。
Ø EC2MultiRegionSnitch
Ø 用于部署在AmazonEC2中,且节点跨多个区域的集群。
5. 客户端请求
client连接至节点并发出read/write请求时,该node充当client端应用与包含请求数据的节点(或replica)之间的协调者,它利用配置的partitioner和replicaplacement策略确定那个节点当获取请求。
5.1写请求
协调者(coordinator)将write请求发送到拥有对应row的所有replica节点,只要节点可用便获取并执行写请求。写一致性级别(write consistency level)确定要有多少个replica节点必须返回成功的确认信息。成功意味着数据被正确写入了commit log个memtable。
上例为单数据中心,11个节点,复制因子为3,写一致性等级为ONE的写情况。
5.2多数据中心的写请求
基本同上,但会在各数据中心分别选择一个协调者以处理该数据中心内的写请求。与client直接连接的coordinator节点只需将写请求发送到远程数据中心的coordinator一个节点即可,剩余的由该coordinator完成。若一致性级别设置为ONE或者LOCAL_QUORUM则仅与直接协调者位于同一数据中心的节点需返回成功确认。
上例为双单数据中心,各11个节点,复制因子为6,写一致性等级为ONE的写情况。
5.3读请求
Ø 直接读请求
Ø 后台读修复请求
与直接读请求联系的replica数目由一致性级别确定。后台读修复请求被发送到没有收到直接读请求的额外的replica,以确保请求的row在所有replica上一致。
协调者首先与一致性级别确定的所有replica联系,被联系的节点返回请求的数据,若多个节点被联系,则来自各replica的row会在内存中作比较,若不一致,则协调者使用含最新数据的replica向client返回结果。
同时,协调者在后台联系和比较来自其余拥有对应row的replica的数据,若不一致,会向过时的replica发写请求用最新的数据进行更新。这一过程叫read repair。
上例为单数据中心,11个节点,复制因子为3,一致性级别为QUORUM的读情况。
6. 数据库内部
6.1数据管理
使用类似Log-StructuredMerge Tree的存储结构,而非典型的关系型数据库使用的B-Tree结构。存储引擎连续的将数据以追加的模式写物磁盘并持续存储数据。节点间/内的操作并行运行。因不使用B-Tree故无需协同控制,在写时不必执行更新。Cassandra在SSD中性能表现极佳。
高吞吐量和低延迟
操作并行运行,吞吐量和延迟相互独立。log-structured设计避免询盘开销。去除on-disk数据修改,省时且延长SSD寿命。无on-disk型的数据修改故无需锁定写请求这样的协同控制。无主、从,在所有节点运行同样的代码。
单独的表目录
/var/lib/cassandra/data/ks1/cf1/ks1-cf1-ja-1-Data.db
其中/var/lib/cassandra/data/为cassandra.yaml中指定的数据文件目录。ks1为keyspace名cf1/为columnfamilies名。这样可将表连接至选定的目标位置以便于将活跃的表移到更快的存储介质,或者将表分不到多个可用的存储设备以均衡负载
6.2关于写
复制的角色
通过在多个同级节点创建数据的多个副本保证可靠性和容错。表是非关系型的,无需过多额外工作来维护关联的表的完整性,因此写操作较关系型数据库快很多。
写过程
先将数据写进内存中的数据结构memtable,同时追加到磁盘中的commitlog中。表使用的越多,对应的memtable应越大,cassandra动态的为memtable分配内存,也可自己手工指定。memtable内容超出指定容量后memtable数据(包括索引)被放进将被刷入磁盘的队列,可通过memtable_flush_queue_size配置队列长度。若将被刷入磁盘的数据超出了队列长度,cassandra会锁定写。memtable表中的数据由连续的I/O刷进磁盘中的SSTable,之后commit log被清空。每个表有独立的memtable和SSTable。
6.3关于更新、删除和hinted handoff writes
更新(cassandra中插入重复的primarykey也被看做是更新操作)
不直接在磁盘中原地更新而是先在memtable进行所有的更新。最后更新内容被刷入磁盘存储在新的SSTable中,仅当column的时间戳比既存的column更新时才覆盖原来的数据。
删除
Ø 不会立即从磁盘移除删除的数据
被删除的数据会被tombstone标记以指定其状态,它会存在一定的时间(由gc_grace_seconds指定),超出该时间后compaction进程永久删除该column。
Ø 若不例行性的执行节点repair操作,被删除的column可能重新出现
若删除期间节点down掉,被标记为tombstone的column会发送信号给Cassandra使其重发删除请求给该replica节点。若replica在gc_grace_seconds期间复活,会最终受到删除请求,若replica在gc_grace_seconds之后复活,节点可能错过删除请求,而在节点恢复后立即删除数据。需定期执行节点修复操作来避免删除数据重现。
hinted handoff writes
在不要求一致性时确保写的高可用,在cassandra.yaml中开启该功能。执行write操作时若拥有对应row的replica down掉了或者无回应,则协调者会在本地的system.hints表中存储一个hint,指示该写操作需在不可用的replica恢复后重新执行。默认hints保存3小时,可通过max_hint_window_in_ms改变该值。
提示的write不计入consistencylevel中的ONE,QUORUM或ALL,但计入ANY。ANY一致性级别可确保cassandra在所有replica不可用时仍可接受write,并且在适当的replica可用且收到hint重放后该write操作可读。
移除节点后节点对应的hints自动移除,删除表后对应的hints也会被移除。
仍需定期执行repair(避免硬件故障造成的数据丢失)
6.4关于读
从SSD并行随机读取,延时极低(不推荐cassandra使用转盘式硬盘)。以partition key读/写,消除了关系型数据库中复杂的查询。
读SSTable
首先检查Bloom filter,每个SSTable都有一个Bloomfilter,用以在进行任何磁盘I/O前检查请求的partition key对应的数据在SSTable中存在的可能性。若数据很可能存在,则检查Partition key cache(Cassandra表partition index的缓存),之后根据index条目是否在cache中找到而执行不同步骤:
Ø 找到
从compression offset map中查找拥有对应数据的压缩快。
从磁盘取出压缩的数据,返回结果集。
Ø 未找到
搜索Partition summary(partition index的样本集)确定index条目在磁盘中的近似位置。
从磁盘中SSTable内取出index条目。
从compression offset map中查找拥有对应数据的压缩快。
从磁盘取出压缩的数据,返回结果集。
回顾插入/更新数据
读的过程
由insert/update过程可知,read请求到达某一节点后,必须结合所有包含请求的row中的column的SSTable以及memtable来产生请求的数据。
例如,要更新包含用户数据的某个row中的email 列,cassandra并不重写整个row到新的数据文件,而仅仅将新的email写进新的数据文件,username等仍处于旧的数据文件中。上图中红线表示Cassandra需要整合的row的片段用以产生用户请求的结果。为节省CPU和磁盘I/O,Cassandra会缓存合并后的结果,且可直接在该cache中更新row而不用重新合并。
6.5关于事务和协同控制
不支持RDBMS中具有回滚和锁定机制的ACID事务,但提供了一定程度的原子性(行级)、隔离性(行级)、持久性和eventual/tunable 类型的一致性(因不支持连接和外键,故不提供ACID场景下的一致性)。
Ø 原子性
row-level,对一个row的插入/更新被当做一个原子操作。不支持要么都做要么都不做的多行插入/更新。不支持在一个replica上write成功而在其他replica上write失败的回滚。用时间戳确定column的最新更新。若多个session同时更新同样的column则使用最近的更新。
Ø 一致性
l Tuneable一致性
提供partition容错。用户可以以单个操作为基础决定需多少个节点接收DML操作或响应SELECT操作。
l Linearizable一致性
l 轻量事务(compare-and-set)的一系列隔离级别。在tuneable一致性不足以满足要求时使用,如执行无间断的相继操作或同时/不同时运行一个操作产生同样的结果。Cassandra2.0使用类似2-phase commit的Paxos consensus协议实现Linearizable一致性。(为支持该一致性引入了SERIAL类型的consistency level及在CQL中使用了带IF从句的轻量事务)
Ø 隔离性
Cassandra2.0开始支持row-level的隔离性。对行的写操作在完成之前对其他用户不可见。
Ø 持久性
同时将数据写入内存中的memtable及磁盘中的commit log。服务器故障时若memtable尚未刷入磁盘,在故障恢复后可重放commit log恢复丢失数据。这提供了本地持久性。数据在其他节点的副本加强了持久性。
轻量事务
Cassandra2.0中引入,弥补Tuneable一致性。
n INSERT INTO emp(empid,deptid,address,first_name,last_name) VALUES(102,14,'luoyang','Jane Doe','li') IF NOT EXISTS;
n UPDATE emp SET address = 'luoyang' WHERE empid = 103 and deptid = 16IF last_name='zhang';
6.6配置数据一致性
Cassandra中,一致性级别可配置,以确定请求的数据如何在不同的replica保持一致性,从而平衡响应时间和数据精确性。
Ø 写一致性
指明在返回确认至客户端前,write操作必须成功的replica数。
l ANY:write至少在一个replica成功。即使所有replica 都down掉,在写hinted handoff后write仍成功。在replica恢复后该write可读。
l ONE:write必须成功写入至少一个replica的commit log和memtable。
l TWO:至少两个
l THREE:至少三个
l QUORUM:至少(replication_factor/ 2) + 1个
l LOCAL_QUORUM:至少(replication_factor/ 2) + 1个,且与协调者处于同一数据中心
l EACH_QUORUM:所有数据中心,至少(replication_factor/ 2) + 1个
l ALL:全部
l SERIAL:至少(replication_factor/ 2) + 1个,用于达成轻量事务的linearizable consistency
需注意的是:实际上write还是会被发到所有相关的replica中,一致性级别只是确定必需要反馈的replica数。
Ø 读一致性
指明在返回数据值客户端前,需要相应read请求的相关replica数。Cassandra从这些数量的replica中根据时间戳检查最新的数据。级别同写一致性。
可通过cqlsh命令CONSISTENCY设置keyspace的一致性,也可编程设置一致性。