目前,知名度比较高的通用存储系统包括:Google GFS&Bigtable,Amazon Dynamo,Microsoft Azure存储系统及Yahoo PNUTS。其中,GFS&Bigtable,Azure存储系统及Yahoo PNUTS都有总控节点,Amazon Dynamo采用去中心化的P2P设计。
Amazon Dynamo看起来很优美,比如Dynamo论文中提到的技术比较酷,Dynamo没有中心节点,可以支持更大的系统规模。然而,Dynamo不是我心目中的理想架构,因为Dynamo有一致性的问题,系统设计复杂但解决的问题有限。如果需要保证一致性,就必须要求同一时刻对同一份数据只有一个更新节点,Dynamo显然不符合要求,可能出现数据丢失等不一致的情况。虽然对于很多场景能够通过冲突合并来解决,另外,Dynamo由于采用一致性Hash的方法进行数据分布,数据不是连续存储的,不能支持高效的扫描操作,所以数据模型只能是简单的<Key, Value>模型,不能支持类似数据仓库这种需要扫描某个用户且单个用户数据量可能特别大的应用场景。总之,去中心化的系统一般有两个问题:1, 一致性问题;2, 顺序扫描效率低下。
Microsoft Azure和普通NOSQL系统设计差别挺大的。普通的NOSQL系统一般是从业务出发,支持某一类业务必要的特性,而Microsoft Azure针对的主要用户为企业级用户,设计从SQL全集出发,尽量支持能够支持的SQL特性。和Yahoo PNUTS一样,Microsoft Azure采用单层设计,不同的是,Yahoo PNUTS通过将操作日志写入到可靠的消息中间件来简化系统其它部分的设计,Microsoft Azure将操作日志Replication功能做为子表服务节点的一个模块(用户使用的Azure数据库实例相当于一个子表),保证操作日志至少同步到两台机器才返回给客户端。抛开Microsoft Azure由于兼顾过多的SQL特性导致的工程复杂度及性能损耗,Microsoft Azure架构的一个问题是:由于每个子表的操作日志分开,多个子表之间的操作日志无法聚合,所以,单机支持的子表个数有限,比如Microsoft Azure限制单机的Azure数据库实例最多为650个。如果单个子表太大,负载平衡效果必然不够好;如果单个子表较小,比如常见的256MB一个子表,单机服务的数据有限。Microsoft Azure的设计可参考论文。
Yahoo PNUTS采用消息中间件Yahoo Message Broker来进行操作日志的可靠存储。虽然多个子表将操作日志写入到不同的Queue,不过在消息中间件中,每个Message Broker可以服务很多的Queue,多个子表写入的操作日志仍然可以因为写入一台机器而进行聚合。Yahoo PNUTS的主要问题是消息中间件本身的扩展性,由于消息中间件为了避免复杂性设计成一个同构的系统,消息中间件本身存储的数据量不能太大。线下的计算,比如MapReduce批处理,计算过程中更新量很大,消息中间件不能胜任。(同构系统的问题参考系统可扩展性文章)
GFS和Bigtable两层的设计是一个几乎完美的组合。GFS本质上是一个弱一致性系统,可能出现重复记录、记录乱序等各种问题(后续有文章专门分析原因),Bigtable是GFS之上的一个索引层,为了服务百PB级别的应用,采用两级的B+树索引结构。GFS保证成功的记录至少写入一次并由Bigtable记录索引,由于Bigtable是一个强一致性系统,整个系统对外表现为强一致性系统。为了保证Bigtable的强一致性,同一时刻同一份数据只能被一台机器服务,且Bigtable论文中的Tablet Server对每个Tablet是没有备份的。当Tablet Server宕机时,由于只需要排序很少的操作日志并且加载服务的Tablet的索引,宕机恢复可以做到一分钟以内。Bigtable分裂和迁移到只需要修改或者加载索引数据,因此效率很高,整个系统的扩展性很好。GFS&Bigtable架构广受质疑之处主要体现在两个方面:
1, GFS&Bigtable架构实时性不好。
2, Bigtable Tablet Server的单点问题。
第一个对Bigtable实时性的质疑,大体有两个原因:第一点是由于Bigtable的Tablet和GFS的Chunk可能不在一台机器上,读取操作可能要走网络;第二点是由于Bigtable每次随机读取都需要读取一块数据,比如16K, 32K或者64K。第一点可以通过本地化策略来解决,第二点由于磁盘的寻道时间一般为8~10ms,读取一整块的overhead不会太高,且Bigtable系统内部的Block Cache和Key-Value Cache可以解决很多问题。开源的HBase和Hypertable由于缺少大规模应用环境还不够稳定,实时性确实做得不好,不过这不是架构本身的问题,而是架构的复杂性导致的实现问题。
第二个对Bigtable的质疑,我们可以通过多数据中心的Replication来解决:同一个数据中心内部的Bigtable系统保证强一致性,机房之间通过回放操作日志进行数据同步,保证最终一致性。同一时刻只有一个集群提供写服务,其它集群提供读服务。当然,对应用方来说仍然是一整套系统,当某台Tablet Server宕机时,只影响短时间部分数据的写服务,读服务如果不要求强一致性不受影响。
描述CAP理论时我们经常会说,Dynamo是AP的系统,Bigtable是CA的系统。然而,Bigtable的分区可容忍性做得也还不错:Bigtable在机房断电,机房之间网络故障时仍然可以对外提供服务。假设在三个机房部署了三套Bigtable集群,且采用三机房五节点的方式部署了Chubby服务,任何一个机房断电或者某两个机房之间网络故障系统仍然能够对外服务。多个机房同时故障或者三个机房被分成三个区的情况理论上有可能,工程上可以认为不可能。所以,不要为了满足CAP理论上的特性而设计系统,业务需求才是本质。
总之,GFS&Bigtable设计在可扩展性,容错,负载平衡等各方面都有很大的优势,并且集群越大优势越明显,问题是整套系统过于复杂,对工程师,应用环境,管理层忍耐力都是一个考验。