分布式存储理解

Published: by Creative Commons Licence

  • Tags:

本文仅个人的一些思路和想法,很多地方都没有进行验证。

分布式存储

假设我们实现了一个文件存储系统,其对外提供了读写API。实现非常简单,我们复用了OS提供的文件系统来为我们存储和管理文件。

随着业务的发展,下面的三个问题崭露头角:

  1. 那么随着文件量的增加,因此我们很快发现一张硬盘已经无法容纳所有的文件了。
  2. 同时我们也发现数据的价值难以估摸,一旦丢失将造成无法挽回的代价。
  3. 同时随着接入应用越来越多,单机的带宽和性能都已经达到瓶颈,我们已经无法为更多的接入应用提供高效的读写操作了。

下面我们依次解决这三个问题:

对于问题1,我们发现硬盘无法容纳所有文件,那么我们就有两个选择,为硬盘扩容,或者使用多张硬盘,并实现自己的文件系统来屏蔽这一特性,从而使得上层服务不用被修改。显然前者终究会面临到一个无法再扩容的瓶颈,而后者则允许我们使用一些碎片化的硬件组成一个完整的超大容量硬盘抽象。因此我们选择后者。 我们称我们自建的文件系统为分布式文件系统(DFS)。但是对于一些特别大的文件,一个硬盘可能无法完整保存,所以我们选择对文件进行分块,块的大小是固定相等的(其中最后一块可以不同)。为了保证DFS仅处理一个任务(将多个硬盘视作一个),我们提供额外的分块文件系统(BFS),其架构在DFS之上,进行自动分块操作。

对于问题2,由于数据可能会因为物理、灾害等意外原因而丢失或损坏,为了降低意外发生导致数据丢失的概率,我们可以为每各文件增加多份副本(replica)。事实上单副本损坏的概率是很高的,而两个处于不同地域的副本同时损坏的概率则低很多,但也不能保证完全不可能发生,因此我们选择使用三副本的策略。这样就可以保证数据始终处于完整。这里我们为每个DFS中的硬盘均增加额外的两块副本硬盘。

由于问题2的解决方案是使用多副本,这将引起一些新问题:

  • 维持副本数:DFS管理的某个硬盘一旦损坏,我们必须尽快将数据迁移到其他空闲硬盘上,因为损坏硬盘上的存储的所有文件副本数都仅为两个。这也意味着我们需要实时监控硬盘状态,为了减少复杂度,我们为每个主机都提供一个额外的本地文件系统(LFS),LFS为DFS提供服务。由于每个文件都存储在三个主机上,因此我们利用主从模型为其上的三个LFS分配角色,一个为master,另外两个为slave。
  • 副本的一致性:数据的写入的处理,我们要保证一致性,即要么写入对所有副本都生效,要么写入就失败。但是写入操作应该是异步还是同步操作,就需要再行商榷。因此我们的DFS会提供两种写入方式,一种是同步写入(writeSync),一种是异步写入(writeAsync)。同步写入能保证在写入成功后才会返回,而异步写入则不作出保证,但是却不会阻塞。同步写入适用于重要的数据存储,而异步写入适用于日志等可丢失数据。
  • 文件并发写入:由于LFS是主从模型,一切写操作由主负责,因此我们可以保证对相同文件的写操作能被串行执行,而不会出现交错的情况。

我们利用zookeeper实现LFS的选主,以及LFS副本的转换。而DFS订阅zookeeper,来得到实时推送。

对于问题3,由于单机性能达到瓶颈,并且我们之前提供多副本的策略,那么我们可以将从节点上的LFS提供只读服务,从而优化读的性能。而不同文件的写发生在不同的LFS上,因此对不同的文件的写可以并发执行。

为了保证学习和重新选主的正常执行,我们为每个LFS维护一个序列号,在对其写入时,会增加序列号。而选主则选择序列号最大的副本,而每次主节点向从节点推送修改,需要带上版本号,如果从节点发现自己受到的版本号与主节点推送的并不连贯,那么,从节点会不接受该次修改,而选择学习整个主节点。