Cassandra -去中心化的结构化存储系统 ,详细架构

5.3          节点关系 (Membership)

Cassandra中集群的节点关系依赖Scuttlebutt, Scuttlebutt基于高效的反熵GOSSIP协议。Scuttlebutt最突出的特征,是他具有高效的CPU,gossip通道利用率。在Cassandra系统中,Gossip协议不仅用来做节点关系管理,也用来传输系统相关的控制状态。

5.3.1                Failure Detection

失效检测,是一种机制,通过它节点可以获取其他节点是不是在正常工作。在Cassandra中,失效检测还用来避免节点在一些操作中,同一些不可到达节点的通讯。 Cassandra采用修改过的Accrual Failure Detector. Accrual 模块不会返回一个Boolean值来标识节点是工作还是宕机状态,相反,这个模块会返回每个受监控节点的一个评估的等级,用Φ来表示,这样做的目的是Φ实际表示的一个范围,这个范围可以动态调整以反映被监控节点的网络和负载情况。

下面具体解释一下 Φ 的含义: 给定一个 Φ 的临界值,然后假定我们在 Φ=1的时候,认为A节点有问题,我们这个猜测的错误(我们的结论,可能被心跳线等其他的状态信息所推翻)概率为10% ,那么当Φ=2的时候,我们猜错的概率只有1% ,在Φ=3的时候,我们猜错的概率为0.1%  …  在系统中每个节点都维护着一个其他节点发出的gossip消息的内部到达时间的滑动窗口, 系统会计算这些到达时间的分布,然后计算Φ的值。尽管原始的论文中建议通过高斯分布来拟合数据,但是我们发现根据gossip 通道的特点和他对于延迟的影响,指数分布会有更高的拟合精度。据我们所知,我们是最先采用上述方式来使用基于gossip 的 Accrual Failure Detection。  Accrual Failure Detectors 在速度和精度上都表现良好,经调整,在网络状况和服务器负载情况检查上,也有尚佳表现。

5.4          启动 Bootstrapping

当一个节点最先加入到集群中时,系统会给他在环中,随机分配一个位置。考虑到容灾需求,这个映射关系会在节点本地和Zookeeper中,都做存储。然后系统会在集群中通过gossip协议广播这个位置信息。然后环中所有的节点,都知道了这个信息。这就保证了任何节点都能将key路由到其他正确的节点上面。当一个节点准备加入到集群的时候,他会读一个配置文件,配置文件中包含一些集群中可以联系的节点。我们将这些初始的联系节点,称之为集群种子。集群种子也可以通过Zookeeper配置服务来提供。

在Facebook的环境中,节点离线(因为失效或者维护任务)经常瞬间完成,但是也可能持续一段时间。失效可以以多种形态出现,比如磁盘错误,CPU损坏。一般节点都是临时离线,因此不需要数据的从新分布或者修复不可达的副本。相似的,因为不小心启动了一个新的Cassandra节点,会导致人为的错误,这会让在每个Cassandra实例中的每个消息中,都会包括节点的名字。假如一个人为的配置错误让一个节点加入了错误的Cassandra实例中,会导致集群名字失效。因为这些原因,因此需要一种明确的机制,来向Cassandra实例中增加或者移除节点。管理员可以通过命令行工具或者浏览器连接到Cassandra节点上面,进行节点的增加与删除操作。

5.5          集群扩容  (Scaling the Cluster )

当一个新的节点加入到系统中的时候,系统在环中为他分配一个位置,这样他就可以缓解一些节点的过重的负担。新的节点会承担一些其他节点的职能。不管是通过命令行还是web界面增加节点,系统都会执行初始化算法。其他的节点会通过内存拷贝技术,将数据传输到新的节点。根据运维经验,单节点数据传输速度能达到40M/s。我们目前在研究类似于Bittorrent多副本传输技术,通过多个副本给上线节点传输数据,从而加快节点启动过程。

5.6          本地持久化 (Local  Persistence)

Cassandra系统依赖本地文件系统做数据持久化。存储结构为了更有效的获取数据而设计。一般写操作包括两个步骤,先写到提交日志里面,这样可以保证系统持续服务和系统故障时候,数据可以恢复。系统会在提交日志文件成功之后,在更新内存中的数据结构。在每个节点上面,为了达到最高的数据吞吐量,都会采用一块独立的硬盘写数据提交日志。当发现内存中对象数目和数据大小达到一定的阀值的时候,数据会被dump到磁盘上面。节点都会安装多块普通硬盘,每次写操作,会写到一块指定硬盘。所有的写操作都顺序进行,并且会建立基于ROW KEY的索引,以便于快速查找。这些回写的索引,会像数据文件一样存储。当这种类型的文件多到一定程度,在后台会启动一个合并进程,将这些文件合并成一个文件。在BigTable中也有类似的合并操作。

一个典型的读操作,会先在内存中的数据结构做查找,如果内存未命中,再去做文件系统查找。做文件查找的时候,会按照由新到老的顺序。在做磁盘文件系统查找的时候,我们判断key是否在一些文件中存在。为了加快效率,如果一个文件没有包含key,那么系统就不会扫描这个文件。为此,对于每个文件的所包含的key信息,做摘要,然后写到内存里面,先采用布隆过滤器直接过滤内存。如果一个key指向了一个含有多个列的列族,那么我在做基于这个key的读取操作的时候,还需要额外的索引机制来获取列,这时单纯的key索引已经不能满足需求。为了防止扫描磁盘上每个列,我们维护了一个列的索引,这样我们可以直接去磁盘的指定块获取这个列数据。因为key索引的列 会序列化到磁盘上面,所以我们以256k为一块, 这个值也可以配置,不过我们发现对于生产环境的负载,256k已经能够满足需求。

Leave a Reply

Your email address will not be published. Required fields are marked *