今天看了一下Cassandra,安装运行起来还是很简单的。Cassandra中的4个结构如下:
- Column是Cassandra中最小的单元:{name,value,timestamp}。
- SuperColumn(那么对应的value有一组值):{name,value:{{},{},{}...}}。
- ColumnFamily可以看做数据库中的表:{key1:{{},{}...},key2:{{},{}....}...}。
- KeySpace(我对这个的理解是从字面上猜的),每个KeySpace可以包括多个表(ColumnFamily)。
在cassandra.thrift文件中定义了各种程序和Cassandra交互的数据结构。一点题外话:Thrift是一种可伸缩的跨语言服务的发展软件框架。它结合强大的软件堆栈的代码生成引擎,以建设服务,工作效率和无缝地与C++、C#、Java、Python、PHP和Ruby组合。允许定义一个简单的定义文件中数据类型和服务接口。以作为输入文件,编译器生成代码用来方便地生成RPC客户端和服务器通信的无缝跨编程语言。一个简单的流程吧。
- 用地址、端口初始化TSocket。
- 将用户传入的对象转化为相应的Column。
- cassandraClient.batch_mutate将数据发送给Cassandra。
Cassandra对一致性哈希的实现:
个人理解:普通的哈希是根据key对集群数量取模,然后根据这个值来判断在哪个服务器上。这是如果其中的一个服务器挂掉了,那么就调整这个的均衡负载方法很麻烦。但是如果是根据key在一个圆圈上取值的话,那就沿着找一个或者的节点(除非所有的节点都挂掉,不然总能找到)。
- 每个节点都对应一个唯一的Token。
- 一个节点负责处理一致性哈希圆环中的一段数据保存为Range。
- Partitioner用于管理Token在一致性哈希圆环中的生成规则,并且决定每一台机器中SSTable数据的排序规则。其中5种不同的实现:
- 用MD5生成Token,Token的排序规则和BigTable的排序规则相同。
- 转换为UTF-8编码的字符串,Token的排序规则与String的排序规则相同。
- 生成LocalToken,排序规则与AbstractType的排序规则相同。
- 转化成ButesToken,排序规则与byte的排序规则相同。
- 与2转化方法相同,但是排序顺序与4相同。
Gossip:
消息在网络中的传播类似于流行病在易感人群中传播的方式。节点N将消息m随机地发送给B个邻居。Gossip在初始化的时候构造四个集合:存活的节点、失效的节点、种子节点、各个节点信息。
- 从存活的节点中随机选择一个节点发送GossipDigestSynMessage。
- 根据一定概率从失效节点中随机选择一个节点发送GossipDigestSynMessage。
- 如果之前发送GossipDigestSynMessage消息的节点中不包括种子几点,或者当前活着的节点数少于种子节点数则随机想一个种子节点发送GossipDigestSynMessage消息。
GossipDigestSynMessage中包括所有节点的地址、心跳版本号和节点状态版本号。接收到消息的节点将进行以下操作:
- 根据接收到的GossipDigest集合调用FailureDetector的report方法更新集群镇南关节点的状态。
- 对收到的GossipDigestSynMessage消息中的GossipDigest进行排序。
- 对比接收到的GossipDigest信息与本节点的GossipDigest差异,本节点需要进一步获取的节点信息由deltaGossipDigestList保存,本节点需要告诉发送GossipDigest信息节点的信息由deltaEpStateMap保存。
- 利用deltaGossipDigestList和deltaEpStateMap构建GossipDigestAckMessage,并将其发送给发送GossipDigestSynMessage消息的节点。
然后就开始处理GossipDigestAckMessage消息:
- 在本地更新GossipDigestAckMessage消息中包含需要本节点更新的节点信息,并调用FailureDetector的report方法更新集群中节点的状态。
- 将发送GossipDigestAckMessage消息的节点需要的其他节点的信息构成GossipDigestAck2Message消息。
- 将GossipDigestAck2Message发送给发送GossipDigestAckMessage消息的节点。
最后是GossipDigestAck2Message消息的处理:在本地更新GossipDigestAck2Message消息中包含需要本节点更新的节点信息并调用FailureDetector的report方法更新集群中节点的状态。
怎么感觉这个过程有点像TCP中的三次握手。。。
数据备份
在数据备份的时候需要考虑节点位置上的关系,EndpointSnitch根据需要由近到远排列地址。有四种实现方法:
- SimpleSnitch:对节点距离排序。
- PropertyFileSnitch:为每一个节点的地址指定对应的数据中心和机架的名称,对节点排序也会考虑数据中心和机架的因素。
- RackInferringSnitch:与PropertyFileSnitch相似,不同的地方在于判断节点所属机架和数据中心的逻辑。
- DynamicEndpointSnitch:在前面三种的基础上,记录节点与节点之间通信的时间间隔... ...
通过ReplicationStrategy可以知道任意一份数据备份的节点信息,同时在节点失效的时候,能够计算出应该接收HINT消息的节点。Cassantra中的三种实现ReplicationStrategy的机制:
- SimpleStrategy:根据一致性哈希圈中按顺时针方向找出下N个需要备份的节点。
- OldNetworkTopologyStrategy:与SimpleStrategy不同的是在选择第二个备份节点时找和第一个节点不在同一个数据中心的,选第三个时选择第二个节点在同一个数据中心但不在同一个机架的,剩下的就和SimpleStrategy一样了。
- NetworkTopologyStrategy:在OldNetworkTopologyStrategy的基础上,指定一个数据中心需要备份的数据份数。如果在一个数据中心要存多份会尽可能让他们在不同的机架上。
集群状态的变化机制
在Cassandra中,如果节点之间通过Gossiper协议发现集群中的状态发生了变化,将以事件的形式通知这些事件的订阅者。
StorageLoadBalancer能够计算集群中每一个节点的负载,并提供Cassandra集群的负载均衡。每当集群状态发生变化的时候StorageLoadBalancer就将节点负载变化传播下去。StorageService抽象整个Cassandra集群的关系,可以通过它来管理整个集群。MigrationManager用于动态改变Schema。