Apache Cassandra是一套开源分布式Key-Value存储系统。它最初由Facebook开发,用于储存特别大的数据。Facebook目前在使用此系统。
主要特性:
分布式
基于column的结构化
高伸展性
Cassandra的主要特点就是它不是一个数据库,而是由一堆数据库节点共同构成的一个分布式网络服务,对Cassandra 的一个写操作,会被复制到其他节点上去,对Cassandra的读操作,也会被路由到某个节点上面去读取。对于一个Cassandra群集来说,扩展性能 是比较简单的事情,只管在群集里面添加节点就可以了。
Cassandra是一个混合型的非关系的数据库,类似于Google的BigTable。其主要功能比 Dynomite(分布式的Key-Value存 储系统)更丰富,但支持度却不如文档存储MongoDB(介于关系数据库和非关系数据库之间的开源产品,是非关系数据库当中功能最丰富,最像关系数据库 的。支持的数据结构非常松散,是类似json的bjson格式,因此可以存储比较复杂的数据类型。)Cassandra最初由Facebook开发,后转变成了开源项目。它是一个网络社交云计算方面理想的数据库。以Amazon专有的完全分布式的Dynamo为基础,结合了Google BigTable基于列族(Column Family)的数据模型。P2P去中心化的存储。很多方面都可以称之为Dynamo 2.0。
和其他数据库比较,有几个突出特点:
模式灵活 :使用Cassandra,像文档存储,你不必提前解决记录中的字段。你可以在系统运行时随意的添加或移除字段。这是一个惊人的效率提升,特别是在大型部 署上。
真正的可扩展性 :Cassandra是纯粹意义上的水平扩展。为给集群添加更多容量,可以指向另一台电脑。你不必重启任何进程,改变应用查询,或手动迁移任何数据。
多数据中心识别 :你可以调整你的节点布局来避免某一个数据中心起火,一个备用的数据中心将至少有每条记录的完全复制。
一些使Cassandra提高竞争力的其他功能:
范围查询 :如果你不喜欢全部的键值查询,则可以设置键的范围来查询。
列表数据结构 :在混合模式可以将超级列添加到5维。对于每个用户的索引,这是非常方便的。
分布式写操作 :可以在任何地方任何时间集中读或写任何数据。并且不会有任何单点失败。
10年前,Eric Brewer教授指出了著名的CAP理论,后来Seth Gilbert 和 Nancy lynch两人证明了CAP理论的正确性。CAP理论告诉我们,一个分布式系统不可能满足一致性,可用性和分区容错性这三个需求,最多只能同时满足两个。
熊掌与鱼不可兼得也。关注的是一致性,那么您就需要处理因为系统不可用而导致的写操作失败的情况,而如果您关注的是可用性,那么您应该知道系统的read操作可能不能精确的读取到write操作写入的最新值。因此系统的关注点不同,相应的采用的策略也是不一样的,只有真正的理解了系统的需求,才有可能利用好CAP理论。
作为架构师,一般有两个方向来利用CAP理论
key-value存储,如Amaze Dynamo等,可根据CAP三原则灵活选择不同倾向的数据库产品。
领域模型 + 分布式缓存 + 存储 (Qi4j和NoSql运动),可根据CAP三原则结合自己项目定制灵活的分布式方案,难度高。
我准备提供第三种方案:实现可以配置CAP的数据库,动态调配CAP。
CA:传统关系数据库
AP:key-value数据库
而对大型网站,可用性与分区容忍性优先级要高于数据一致性,一般会尽量朝着 A、P 的方向设计,然后通过其它手段保证对于一致性的商务需求。架构设计师不要精力浪费在如何设计能满足三者的完美分布式系统,而是应该进行取舍。
不同数据对于一致性的要求是不同的。举例来讲,用户评论对不一致是不敏感的,可以容忍相对较长时间的不一致,这种不一致并不会影响交易和用户体验。而产品价格数据则是非常敏感的,通常不能容忍超过10秒的价格不一致。
CAP理论的证明:大体逻辑是如果要分布式,那么更新数据的时候,要么加锁(或其他技术)保证一致,要么不加锁保证可用;如果两个都想保证,就不能分布式了。
Gossip协议是一个Gossip思想的P2P实现。现代的分布式系统经常使用这个协议,他往往是唯一的手段。因为底层的结构非常复杂,而且Gossip也很有效。
假设有{p,q,…}为协议参与者。每个参与者都有一个自己信息的表。用编程语言可以描述为:记InfoMap = Map,那么每个参与者都要维护一个InfoMap类型的变量localinfo。同时每一个参与者要知道所有其他参与者的信息,即要维护一个全局表,即Map类型的变量globalMap。每个参与者更新自己的localInfo,而由Gossip协议负责将更新的信息同步到整个网络上。
如何信息同步。每个节点和系统中的某些节点成为peer(如果系统规模较小,和系统中所有的其他节点成为peer)。
节点peer组成
例如,peer的数量设置为3,A与D、E、F成为peers,D与A、B、C成为peers。A与D、E、F完成信息同步,D与ABC完成信息同步。这样最终所有的节点都能够获得最新的全局信息。Gossip语义为谣言,这种传播信息的方式与现实中的谣言具有类似的特征。
节点间的同步方式有3种:
Push:最简单的情况下,一个节点p向q发送整个GlobalMap
Pull:p向q发送digest,q根据digest向p发送p过期的(key,(value,version))列表
Push-pull:节点p向q发送整个GlobalMap,q更新GlobalMap后将p过期的列返还给p,p再更新自己的GlobalMap(因为要发送整个GlobaMap,这种方式数据网络流量会很大,同步信息的操作将会给系统带来很大的压力)
N,R,W:其中N代表副本的数量总数,R表示一个读请求需要的机器参与总数,W代表一个写请求需要的机器参与总数。
当R+W>N时,系统呈现强一致性
当R+W=
假设一份数据需要存储3份副本A、B、C,当一个写请求在A、B机器上完成。同时客户端有一个读操作请求,这时候,如果只读1份,很可能读取的是C机器上的副本,造成数据的不一致性。
而这时如果读取2份副本,则无论如何至少可以得到1份正确的数据。再通过时间戳即可判断副本中哪个是最新的。即可保证读写数据的一致性。在cassandra中采用的是R=2,W=2,N=3的参数设置。
Bloom Filter是一种空间效率很高的随机数据结构,它利用位数组很简洁地表示一个集合,并能判断一个元素是否属于这个集合。Bloom Filter的这种高效是有一定代价:在判断一个元素是否属于某个集合时,有可能会把不属于这个集合的元素认为属于这个集合。因此,Bloom Filter不适合那些“零错误”的应用场合。而在能容忍错误率的应用场合下,Bloom Filter通过极少的错误换取存储空间的极大节省。
由于cassandra采用的是p2p环状架构,需要一个hash算法来统一各节点。
我们把每台server分成v个虚拟节点,再把所有虚拟节点(n*v)随机分配到一致性哈希的圆环上,这样所有的用户从自己圆环上的位置顺时针往下取到第一个vnode就是自己所属节点。当此节点存在故障时,再顺时针取下一个作为替代节点。
当动态增加和删除时不需要做重新hash,只需要往环上增减。当一段环上负载过重还可以将一个节点再虚拟成多个节点分配到教空闲的环上去。
Cassandra采用类似BigTable的数据组织结构。
Keyspace
Cassandra中的最大组织单元,里面包含了一系列Column family,Keyspace一般是应用程序的名称。你可以把它理解为Oracle里面的一个schema,包含了一系列的对象。
Column family(CF)
CF是某个特定Key的数据集合,每个CF物理上被存放在单独的文件中。从概念上看,CF有点象数据库中的Table.
Key
数据必须通过Key来访问,Cassandra允许范围查询,例如:start => '10050', :finish => '10070'
Column
在Cassandra中字段是最小的数据单元,column和value构成一个对,比如:name:“jacky”,column是name,value是jacky,每个column:value后都有一个时间戳:timestamp。
和数据库不同的是,Cassandra的一行中可以有任意多个column,而且每行的column可以是不同的。从数据库设计的角度,你可以理解 为表上有两个字段,第一个是Key,第二个是长文本类型,用来存放很多的column。这也是为什么说Cassandra具备非常灵活schema的原因。
Super column
Super column是一种特殊的column,里面可以存放任意多个普通的column。而且一个CF中同样可以有任意多个Super column,一个CF只能定义使用Column或者Super column,不能混用。下面是Super column的一个例子,homeAddress这个Super column有三个字段:分别是street,city和zip: homeAddress: {street: "binjiang road",city: "hangzhou",zip: "310052",}
Cassandra借鉴了bigtable的设计,采用memtable和sstable的方式。和关系数据库一样,cassandra在写数据之前,也需要先记录日志,称之为commitlog,然后数据才会写入到Column Family对应的Memtable中,并且Memtable中的内容是按照key排序好的。Memtable是一种内存结构,满足一定条件后批量刷新到磁盘上,存储为SSTable。这种机制,相当于缓存写回机制(Write-back Cache),优势在于将随机IO写变成顺序IO写,降低大量的写操作对于存储系统的压力。SSTable一旦完成写入,就不可变更,只能读取。下一次Memtable需要刷新到一个新的SSTable文件中。所以对于Cassandra来说,可以认为只有顺序写,没有随机写操作。
因为SSTable数据不可更新,可能导致同一个Column Family的数据存储在多个SSTable中,这时查询数据时,需要去合并读取Column Family所有的SSTable和Memtable,这样到一个Column Family的数量很大的时候,可能导致查询效率严重下降。因此需要有一种机制能快速定位查询的Key落在哪些SSTable中,而不需要去读取合并所有的SSTable。Cassandra采用的是Bloom Filter算法,通过多个hash函数将key映射到一个位图中,来快速判断这个key属于哪个SSTable。关于Bloom Filter,有兴趣的可以去看看参考文章4,5和6。
为了避免大量SSTable带来的性能影响,Cassandra也提供一种定期将多个SSTable合并成一个新的SSTable的机制,因为每个SSTable中的key都是已经排序好的,因此只需要做一次合并排序就可以完成该任务,代价还是可以接受的。所以在Cassandra的数据存储目录中,可以看到三种类型的文件,格式类似于:
其中Data.db文件是SSTable数据文件,SSTable是Sorted Strings Table的缩写,按照key排序后存储key/value键值字符串。index.db是索引文件,保存的是每个key在数据文件中的偏移位置,而Filter.db则是Bloom Filter算法生产的映射文件。
优点:
分布式:Cassandra采用分布式存储,无中心节点,避免单点失败,可扩展性良好。
存储:类bitable的列式存储,数据查询的效率提高。
读写:对于大量的随机读写有较好的支持,因为facebook作为一个社交类网站需要有大量的随机读写操作,当初的设计上就考虑到了这一点。
缺点:
Twitter在其7.9一篇博客中提到暂停使用Cassandra来代替Mysql存储feed的计划。
我们来分析一下Twitter停止使用Cassandra的原因
1. Cassandra仍然缺少大并发海量数据访问的案例及经验,Cassandra来源自Facebook,但是在Facebook内部Cassandra 目前只用在inbox search产品上,容量大约有100-200T。且Inbox Search在Facebook的基础架构中也并非核心应用。并且还传出不少rumors说facebook已经放弃Cassandra。
2. 新产品需要一定稳定期,Cassandra代码或许还存在不少问题,但是Twitter如果投入大量的精力来改进Cassandra和比较优化MySQL 的投入来看有点得不偿失。在QCon Beijing上@nk也提到 Cassandra在Twitter的内部测试中曾经暴露出不少严重的问题。
此问题曾经在QCon Beijing 2010做过介绍,在去年的第一期广州技术沙龙也有过交流,类似Twitter这样的网站使用Cassandra的主要原因有
1. 数据增长规模需要不断增加新服务器,传统的切分方案在面临增删硬件时候需要手工维护,当数据规模速度增快,业务又不运行停机维护,手工维护的成本增加造成系统运维不堪重负。
2. 不能简单增加服务器解决请求量增长的问题,需要数据架构师精细的规划。
3. 每一个新的特性都需要重复评估数据拆分及访问优化的问题,架构师需要投入大量精力review几乎相同的业务场景。
Twitter的调整对于MySQL业界来说或许是一大利好,MySQL虽然受近期Oracle收购阴影的影响,但是对于目前大多数拥有海量数据访问的网站依然是他们第一选择。MySQL简单,可靠,安全,配套工具完善,运维成熟。业界碰到的大部分可扩展性方面的问题在MySQL中其实都有清晰明确的解决方法。虽然重复sharding的问题很烦,增删机器相关的运维工作也很繁琐,但是这些工作量还是在可以接受的范围内。
究竟Twitter这次策略改变是NoSQL运动的一次挫折还是前进中的一段小插曲?我们拭目以待。目前另外一大Web 2.0巨头Digg仍然在使用Cassandra。不过据最新消息Digg的工程副总裁因为Cassandra在业务中表现不佳而离职,Cassandra离正式商用还有一段距离。
由于hadoop对于数据查询的支持不佳,对数据分析支持很好,稳定性佳。而cassandra的数据结构利于查询,稳定性还待研究。所以可以考虑结合两者各自的优点来处理不同的事物。
这里存在一个整合的问题,如何整合?
用例一
从HDFS中读取数据然后插入到cassandra中
1、 将需要插入cassandra的数据上传到HDFS中
2、 启动MapReduce程序
这种类型的整合其实和cassandra本身关系不大,只是在mapreduce过程中将out指向cassandra。
用例二
直接从cassandra中读取数据,然后进行相应的计算
这个功能在cassandra0.6.x版本中才有。其可以从cassandra直接读取MapReduce需要的数据,实现对于Cassandra的全表扫描功能
1、 在MapReduce程序中指定使用的KeySpace,ColumnFamily,和SlicePredicate等和Cassandra相关的参数
2、 启动Hadoop MapReduce程序(需hadoop0.20.x版本支持)
可以考虑的三种策略:
1、 直接使用cassandra代替hadoop,存储、计算和查询都用其来做。不过cassandra架构对mapreduce算法的效率尚待评估(虽然现在apache有意将HDFS和Mapreduce区分,以使得mapreduce可以用到更多的地方,而不是被HDFS绑定,不过不可否认,HDFS上运行mapreduce的效率和稳定性目前来说是最好的选择)
2、 依旧使用hadoop,从硬件和架构上优化其性能。让查询速度达到可容忍范围之内
3、 在hdfs上存储数据源,数据分析的工作完全由hadoop来做。对于查询,定时往cassandra上传热点数据(或全部数据)并更新,在cassandra上建立查询接口。
Cassandra实际来说是Amazon之Dynamo的开源版本,其设计采用了多项新算法和架构。目前cassandra最新版为0.6.7,是一个比较新的开源项目。
其优势在于快速查询,避免任何单点失败,集群具有高可扩展性和易管理特点。不过对于我们的产品目前的情况来说,其存在以下问题:
1、它未采用hdfs文件系统,所以存在与Hadoop难以协同的问题。数据源在cassandra存储体系中,不利于mapreduce分析。
2、该开源体系尚不成熟,代码稳定性存在不确定性,先后被一些大型互联网公司采用又弃用。
3、其快速查询的功能有hbase这样的列存式数据库这样的替代品。
Hbase从当初的0.20版本提升到0.89版本,稳定性和性能有很大的提升,正在评估其实用性。Hbase数据存储在hdfs上,和hadoop一脉相承,具有很好的协同效应。
4、其产品设计适合互联网微薄或者博客这类交互性要求高的产品,今后还是具有很好的前景。