这个题目写的有点大,但是我一直想写一个这方面文章,虽然我没系统学过分布式系统理论,但是接触了这么多分布式系统之后,隐约感觉这些系统有很多共通之处。现在如果我让你开发一个分布式系统,准确点叫分布式task系统,比如从kafka订阅数据,然后写到hdfs,归档。注意不光是能支持某一个topic,不只是固定的partition数目,你该怎么写?
这个需求,其实可以使用jstorm来做,写一些spout就可以了,或者弄个2层结构,spout-bolt,spout拉数据交给bolt,bolt写到hdfs。jstorm本身就是一个分布式计算系统,确切地说是一个流计算系统,任务管理,消息传输,负载均衡,容灾等,都由jstorm框架帮你完成。
那么,如果让你写一个分布式存储系统呢?分布式存储比分布式计算可能要难一点,因为涉及到更多的数据一致性问题,像hdfs,hbase,kafka其实都属于分布式存储系统,每个系统有自己的存储特点和格式,对于hbase和kafka这种更接近数据库类型的系统来讲,写数据意味着把数据交给你管理,每一条都要尽可能保证数据不丢,那么基本流程可能是先写wal,然后写到内存,内存再异步刷到磁盘。对于kv存储系统,磁盘中保存数据的仍然是文件,但是这个文件可不简单,并不是随便写的文件,通常都有自己的格式,这种格式一方面是为了方便存储,提高存储效率,另一方面这种文件格式更多地是为了你搜索或者随机检索方便,比如b tree或者LSM等数据结构。之所以写wal是为了容灾考虑。
那有人说些wal是持久化,为什么不直接写数据库文件呢,原因很简单,就是写wal快,比写数据库文件要快,为了提高写性能考虑。虽然光写内存更快,但是没有持久化这些数据掉电之后,数据丢失怎么办。所以wal又叫redo log,即顺序写进去,写入数据库出现问题的时候,会从这里找到重新做。
分布式数据库,存储系统之所以分布式,就是把数据分块,分region,或者分partition存储,每个partition又可以多个副本,同时副本又有主从。一般主副本接受读写请求,然后将请求转发到其它副本,由这个主副本协调读写操作。
数据副本在全集群要尽量均匀分布,一方面是为了防止存储倾斜,有些机器容量都满了,有些还很空闲,比如hdfs。另一方面又是为了读写考虑,要防止读写热点,毕竟单点的qps,tps是有限制的,同时也为了容灾考虑。基于均分分布原理,这些系统又有了rebalance的概念。
分布式存储和分布式计算,其实有很多相同的地方,分布式存储管理的单元是数据块,partition,region等数据颗粒的副本,读写等,而分布式任务,管理的是一个作业的task,并发的task,task的状态等。同样存在task 负载均衡的问题,即调度的问题。
分布式系统一般都有一个协调服务,做分布式锁,状态管理,甚至心跳等。zookeeper基本上在大数据领域,用得比较多,同时也比较可靠。
说到这里,不得不提一下Apache helix,这个东西基本上帮我实现了自己的想法,即使用zookeeper做一个分布式系统框架,利用这个框架,既可以开发分布式task,也可以做分布式存储,它帮我们完成了分布式管理中的绝大多数工作。
从今天开始,我会用几篇文章,讲解一下apache helix的原理,架构以及实现细节。