01 . 分布式存储之FastDFS简介及部署

分布式存储简介

现代的互联网已经进入大数据时代,天天都有数以万计的数据产生,这些数据的规模轻轻松松地能够达到几P的级别,传统的的单机存储早已捉襟见肘,根本没法知足大数据对存储系统的要求。这时,各类分布式系统才应运而生。html

其实分布式的概念,早在不少年前,就有人提出和进行相关研究,可是因为当时的网络数据不多,分布式无用武之地,一直不温不火,一至到大数据时代的来临,才陆陆续续被人们应用到工程实践中。node

概念

分布式存储系统,是将数据分散存储在多台独立的设备上。传统的网络存储系统采用集中的存储服务器存放全部数据,存储服务器成为系统性能的瓶颈,也是可靠性和安全性的焦点,不能知足大规模存储应用的须要。分布式网络存储系统采用可扩展的系统结构,利用多台存储服务器分担存储负荷,利用位置服务器定位存储信息,它不但提升了系统的可靠性、可用性和存取效率,还易于扩展。python

特性

可扩展nginx

分布式存储系统能够扩展到几百台甚至几千台的集群规模,并且随着集群规模的增加,系统总体性能表现为线性增加。c++

低成本git

分布式存储系统的自动容错、自动负载均衡机制使其能够构建在普通的PC机之上。另外,线性扩展能力也使得增长、减小机器很是方便,能够实现自动运维。github

高性能redis

不管针对整个集群仍是单台服务器,都要求分布式存储系统具有高性能算法

易用数据库

分布式存储系统须要可以提供易用的对外接口,另外,也要求具有完善的监控、运维工具,并可以文玩与其余系统集成.

技术难点

分布式存储系统的挑战主要在于数据、状态信息的持久化,要求在自动迁移、自动容错、并发读写的过程当中保证数据的一致性。分布式存储涉及的技术主要来自两个领域:分布式系统以及数据库。

数据分布

如何将数据分布到多台服务器才能保证数据分布均匀?数据分布到多台服务器后如何实现跨服务器读写操做?

分布式系统区别于传统单机系统在于可以将数据分布到多个节点,并在多个节点之间实现负载均衡。分布式存储系统的一个基本要求就是透明性,包括数据分布透明性, 数据迁移透明性,数据复制透明性还有数据故障透明性。

哈希分布

哈希分布的方法很常见,其方法是根据数据的某一特征计算哈希值,并将哈希值与集群中的服务器创建映射关系,从而将不一样哈希值的数据分布到不一样的服务器上。若是哈希函数的散列性很好,哈希方式能够将数据比较均匀地分布到集群中去,并且哈希方式须要记录的元信息也很简单,每一个节点只须要知道哈希函数的计算方式以及模的服务器的个数就能够计算出处理的数据应该属于哪台机器。

传统的哈希分布算法有一个问题,当服务器上线或者下线时,N值发生变化,数据映射会被打乱。为解决这个问题,一个办法是再也不简单地将哈希值和服务器个数作除法映射,而是将哈希值与服务器的对应关系做为元数据,交给专门的元数据服务器来管理。还有一个办法是采用一致性哈希算法。

一致性哈希(Distributed Hash Table,DHT)算法。算法思想:给系统中每一个节点分配一个随机token,这些token构成一个哈希环。执行数据存放操做时,先计算Key的哈希值,而后存放到顺时针方向第一个大于或者等于该哈希值的token 所在的节点。一致性哈希值的优势在于加入或删除时只会影响到在哈希环中相除的节点,而对其它节点没影响。

哈希存储引擎是哈希表的持久化实现,支持增、删、改,以及随机读取操做,但不支持顺序扫描,对应的存储系统为键值(Key-Value)存储系统,如 Bitcask。它仅支持追加操做,删除也只是经过标识 value 为特殊值,经过按期合并(Compaction)实现垃圾回收。

顺序分布

哈希散列破坏了数据的有序性,只支持随机读取操做,不能支持顺序扫描。并且这种方式可能出现某些用户的数据量太大的问题,因为用户的数据限定在一个存储节点,没法发挥分布式存储系统的多机并行处理能力。

一致性

若是将数据的多个副本复制到多台服务器,即便在异常状况下,也可以保证不一样副本之间的数据一致性。同一份数据的多个副本每每有一个副本为主副本,其余副本为备副本,由主副本将数据复制到备份副本。复制协议分为两种,强同步复制以及异步复制,两者的区别在于用户的读写请求是否须要同步到备副本才能够返回成功。假如备份副本不止一个,复制协议还会要求写请求至少须要同步到几个备副本。当主副本出现故障时,分布式存储系统可以将服务自动切换到某个备副本,实现自动容错。

一致性和可用性是矛盾的,强同步复制协议能够保证主备副本之间的一致性,可是当备副本出现故障时,也可能阻塞存储系统的正常写服务,系统的总体可用性受到影响;异步复制协议的可用性相对较好,可是一致性得不到保障,主副本出现故障时还有数据丢失的可能。

强复制与异步复制

分布式存储系统中数据保存多个副本,通常来讲,其中一个副本为主副本,其余副本为备副本,常见的作法是数据写入到主副本,由主副本肯定操做的顺序并复制到其余副本。

客户端将写请求发送给主副本,主副本将写请求复制到其余备副本,常见的作法是同步操做日志(Commit log)。主副本首先将操做日志同步到备副本上备副本回放操做日志,完成后通知主副本。接着主副本修改本机,等到全部的操做都完成后再通知客户端写成功。这里要求主备同步成功后才能够返回客户端写成功,这种协议称为强同步协议。强同步协议提供了强一致性,可是,若是备副本出现问题将阻塞写操做,系统可用性较差。

与强同步对应的复制方式是异步复制。在异步模式下,主副本不须要等待备副本的回应,只须要本地修改为功就能够告知客户端写操做成功。另外,主副本经过异步机制,好比单独的复制线程将客户端修改操做推送到其余副本。异步复制的好处在于系统可用性好,可是一致性差,若是主副本发生不可恢复故障,可能丢失最后一部分更新操做。

强同步复制和异步复制都是将主副本的数据以某种形式发送到其余副本,这种复制协议称为基于主副本的复制协议。这种方法要求在任什么时候刻只能有一个副本为主副本,由它来肯定写操做之间的顺序。若是主副本出现故障,须要选举一个备副本成为新的主副本,这步操做称为选举,如Paxos协议。除此外还有基于多个存储节点的复制协议(比较少见)。

操做日志的原理很简单:为了利用好磁盘的顺序读写特性,将客户端的写操做先顺序写入到磁盘中,而后应用到内存中,因为内存是随机读写设备,能够很容易经过各类数据结构,好比B+树将数据有效地组织起来.当服务器宕机重启时,只须要积攒必定的操做日志再批量写入到磁盘中,这种技术通常称为成组提交。

CAP原理

CAP即一致性(Consistency)、可用性(Availablility)以及分区可容忍性(Tolerance of network Partition)三者不能同时知足。

一致性:读操做老是能读取到以前完成的写操做结果,知足这个条件的系统称为强一致性系统,这里的“以前”通常对同一个客户端而言。

可用性:读写操做在单台机器发生故障的状况下仍然可以正执行,而不须要等待发生故障重启或者其上的服务迁移到其余机器。

分区可容忍性:机器故障、网络故障、机房停电等异常状况下仍然可以知足一致性和可用性。

分布式存储系统要求可以自动容错,分区容忍性老是要知足的,所以,一致性和写操做的可用性不能同时知足。存储系统设计时须要在一致性和可用性之间权衡,在某些场景下,不容许丢失数据,在另一些场景下,极小几率丢失部分数据是容许的,可用性更加剧要。

容错

如何检测到服务器故障?如何自动将出现故障的服务器上的数据和服务器迁移到集群中的其它服务器?

随着集群规模变得愈来愈大,故障发生的几率也愈来愈大,大规模集群天天都有故障发生。容错是分布式存储系统设计的重要目标,只在实现了自动化容错,才能减小人工成本,实现分布式存储的规模效应。

故障检测

单台服务器故障的几率是不高的,然而,只要集群的规模足够大, 天天都有机器故障发生,系统须要可以自动处理。首先,分布式存储系统须要可以检测到机器故障,在分布式系统中,故障检测每每经过租约(Lease)协议实现。接着,须要可以将服务器复制或者迁移到集群中的其余正常服务的存储节点。

租约机制就是带有超时间的一种受权。假设机器A须要检测机器B 是否发生故障,机器A 能够给机器B 发放租约, 机器B 持有的租约在有效期内才容许提供服务,不然主动中止服务。机器B 的租约快要到期时向机器A 从新申请租约。正常状况下,机器B 经过不断申请租约来延长有效期,当机器B 出现故障或者与机器A 之初蝗网络发生故障时,机器B 租约将过时,从而机器A 可以确保机器B 再也不提供服务,机器B 的服务能够被安全地迁移到其余服务器。

故障恢复

单层结构和双层结构的故障恢复机制有所不一样。

单层结构。单层结构的分布式存储系统维护了多个副本,主备副本之间经过操做日志同步。节点下线分为两种状况:一种是临时故障,节点过一段时间将从新上线;另外一种状况是永久性故障,好比硬盘损坏。

双层结构。双层结构的分布式存储系统会将全部的数据持久化写入底层的分布式文件系统,每一个数据片同一时刻只有一个提供服务的节点。

节点故障会影响系统服务,在故障检测以及故障恢复的过程当中,不能提供写服务以及强一致性读服务。停服务时间包含两个部分,故障检测时间故障恢复时间。故障检测时间通常在几秒到十几秒, 这和集群规模密切相关,集群规模越大,故障检测对总控节点形成的压力就越大,故障检测时间就越长。故障恢复时间通常很短,单层结构的备副和主副本之间保持实时同步,切换为主副本的时间很短;双层结构故障恢复每每实现成只须要将数据的索引,而不是全部的数据加载到内存中。

总控节点自身也可能出现故障,为了实现总控节点的高可用性,总控节点的状态也将实时同步到备机,当故障发生时, 能够经过外部服务选举某个备机做为新的总控节点,而这个外部服务也必须是高可用的。为了进行选主或者维护系统中重要的全局信息,能够维护一套经过Paxos 协议实现的分布式锁服务, 如Zookeeper。

负载均衡

新增服务器和集群正常运行过程当中如何实现自动负载均衡?数据迁移过程当中如何保证不影响已有服务?

分布式存储系统的每一个集群中通常有一个总控节点,其余节点为工做节点,由总控节点根据全局负载信息进行总体调度。工做节点刚上线时,总控节点须要将数据迁移到该节点上,另外,系统运行过程当中也须要不断地执行迁移任务,将数据从负载较高的工做节点迁移到负载较低的工做节点。

工做节点经过心跳包,将节点负载相关的信息,如CPU、内存、磁盘及网络等资源使用率,读写次数及读写数据量等发给主控节点。主控节点计算出工做节点的负载信息以及须要迁移的数据,生成迁移任务放入迁移队列中等待执行。

负载均衡须要执行数据迁移操做。在分布式存储系统中每每会存储数据的多个副本,一个为主副本,其余为备副本,由主副本对外提供服务。迁移备副本不会对服务形成影响,迁移主副本也能够首先将数据的读写服务切换到其余备副本。整个迁移过程能够作到无缝,对用户彻底透明。

分布式协议

分布式系统涉及的协议不少,例如租约、复制协议、一致性协议等等,其中以两阶段提交协议和Paxos 协议最具备表明性。两阶段提交协议用于保证跨多个节点操做的原子性,即跨多个节点的操做要么在全部节点上所有执行成功,要么所有失败。Paxos 协议用于确保多个节点对某个投票(如选取某个节点为主节点)达成一致。

两阶段提交协议

两阶段提交协议(Two-phase Commit,2PC)常常用来实现分布式事务,在两阶段协议中,系统通常包含两类节点:一类为协调者(coordinator),一般一个系统中只有一个;另外一类为事务参与者(participants,cohorts或者workers),通常包含多个。协议中假设每一个节点都会记录操做日志并持久化到非易失性存储介质,即便节点发生故障日志也不会丢失。执行过程以下:

  • 第一阶段,准备阶段。协调者通知事务参与者准备提交或者取消事务,而后进入表决过程。在表决过程当中,参与者将告知协调者本身的决策,赞成(事务参与者本地执行成功)或者取消(事务参与者本地执行失败)。能够进一步将准备阶段分为如下三个步骤:

    • 协调者节点向全部参与者节点询问是否能够执行提交操做(vote),并开始等待各参与者节点的响应。
    • 参与者节点执行询问发起为止的全部事务操做,并将Undo信息和Redo信息写入日志。(注意:若成功这里其实每一个参与者已经执行了事务操做)
    • 各参与者节点响应协调者节点发起的询问。若是参与者节点的事务操做实际执行成功,则它返回一个”赞成”消息;若是参与者节点的事务操做实际执行失败,则它返回一个”停止”消息。
  • 第二阶段,提交阶段。协调者将基于第一个阶段的投票结果进行决策,提交或者取消。当且仅当全部的参与者赞成提交事务协调者才通知全部的参与者提交事务,不然协调者通知全部的参与者取消事务。参与者在接收到协调者发来的的消息后将执行相应的操做。

    提交:

    • 调者节点向全部参与者节点发出”正式提交(commit)”的请求。
    • 参与者节点正式完成操做,并释放在整个事务期间内占用的资源。
    • 参与者节点向协调者节点发送”完成”消息。
    • 协调者节点受到全部参与者节点反馈的”完成”消息后,完成事务。

    取消:

    • 协调者节点向全部参与者节点发出”回滚操做(rollback)”的请求。
    • 参与者节点利用以前写入的Undo信息执行回滚,并释放在整个事务期间内占用的资源。
    • 参与者节点向协调者节点发送”回滚完成”消息。
    • 协调者节点受到全部参与者节点反馈的”回滚完成”消息后,取消事务。

二阶段提交看起来确实可以提供原子性的操做,可是不幸的事,二阶段提交仍是有几个缺点的:

  • 同步阻塞问题。执行过程当中,全部参与节点都是事务阻塞型的。当参与者占有公共资源时,其余第三方节点访问公共资源不得不处于阻塞状态。
  • 单点故障。因为协调者的重要性,一旦协调者发生故障。参与者会一直阻塞下去。尤为在第二阶段,协调者发生故障,那么全部的参与者还都处于锁定事务资源的状态中,而没法继续完成事务操做。(若是是协调者挂掉,能够从新选举一个协调者,可是没法解决由于协调者宕机致使的参与者处于阻塞状态的问题)
  • 数据不一致。在二阶段提交的阶段二中,当协调者向参与者发送commit请求以后,发生了局部网络异常或者在发送commit请求过程当中协调者发生了故障,这回致使只有一部分参与者接受到了commit请求。而在这部分参与者接到commit请求以后就会执行commit操做。可是其余部分未接到commit请求的机器则没法执行事务提交。因而整个分布式系统便出现了数据部一致性的现象。
  • 二阶段没法解决的问题:协调者再发出commit消息以后宕机,而惟一接收到这条消息的参与者同时也宕机了。那么即便协调者经过选举协议产生了新的协调者,这条事务的状态也是不肯定的,没人知道事务是否被已经提交。
    因为二阶段提交存在着诸如同步阻塞、单点问题、脑裂等缺陷,因此,研究者们在二阶段提交的基础上作了改进,提出了三阶段提交(三阶段提交也是不完美的,这里再也不赘述)。
Paxos协议

Paxos 协议用于解决多个节点之间的一致性问题。多个节点之间经过操做日志同步数据,若是只有一个节点为主节点,那么,很容易确保多个节点之间操做日志的一致性。只要保证了多个节点之间操做日志的一致性,就可以在这些节点上构建高可用的全局服务,如分布式锁服务,全局命名和配置服务等。

为了实现高可用性,主节点每每将数据以操做日志的形式同步到备节点。若是主节点发生故障,备节点会提议本身成为主节点。Paxos 协议保证,即便同时存在多个proposer,也能保证全部节点最终达到一致,选举出惟一的主节点。下面引用网上一篇博客的内容来说述Paxos协议。

在paxos算法中,分为4种角色:Proposer(提议者)、Acceptor(决策者)、Client(产生议题者)和Learner(最终决策学习者)。其中提议者和决策者是很重要的,其余的2个角色在整个算法中应该算作打酱油的,Proposer就像Client的使者,由Proposer使者拿着Client的议题去向Acceptor提议,让Acceptor来决策。这里上面出现了个新名词:最终决策。如今来系统的介绍一下paxos算法中全部的行为:

  • Proposer提出议题
  • Acceptor初步接受 或者 Acceptor初步不接受
  • 若是上一步Acceptor初步接受则Proposer再次向Acceptor确认是否最终接受
  • Acceptor 最终接受 或者Acceptor 最终不接受

上面Learner最终学习的目标是Acceptor们最终接受了什么议题?注意,这里是向全部Acceptor学习,若是有多数派个Acceptor最终接受了某提议,那就获得了最终的结果,算法的目的就达到了。过程以下图:

如今经过一则故事来学习paxos的算法的流程(2阶段提交),有2个Client(老板,老板之间是竞争关系)和3个Acceptor(政府官员)。

第一阶段:

  • 如今须要对一项议题来进行paxos过程,议题是“A项目我要中标!”,这里的“我”指每一个带着他的秘书Proposer的Client老板。
  • Proposer固然听老板的话了,赶忙带着议题和现金去找Acceptor政府官员。
  • 做为政府官员,固然想谁给的钱多就把项目给谁。
  • Proposer-1小姐带着现金同时找到了Acceptor-1~Acceptor-3官员,1与2号官员分别收取了10比特币,找到第3号官员时,没想到遭到了3号官员的鄙视,3号官员告诉她,Proposer-2给了11比特币。不过不要紧,Proposer-1已经获得了1,2两个官员的承认,造成了多数派(若是没有造成多数派,Proposer-1会去银行提款在来找官员们给每人20比特币,这个过程一直重复每次+10比特币,直到多数派的造成),满意的找老板复命去了,可是此时Proposer-2保镖找到了1,2号官员,分别给了他们11比特币,1,2号官员的态度马上转变,都说Proposer-2的老板懂事,这下子Proposer-2放心了,搞定了3个官员,找老板复命去了,固然这个过程是第一阶段提交,只是官员们初步接受贿赂而已。故事中的比特币是编号,议题是value(这个过程保证了在某一时刻,某一个proposer的议题会造成一个多数派进行初步支持)。

如今进入第二阶段提交:

  • 如今proposer-1小姐使用分身术(多线程并发)分了3个本身分别去找3位官员,最早找到了1号官员签合同,遭到了1号官员的鄙视,1号官员告诉他proposer-2先生给了他11比特币,由于上一条规则的性质proposer-1小姐知道proposer-2第一阶段在她以后又造成了多数派(至少有2位官员的赃款被更新了);此时她赶忙去提款准备从新贿赂这3个官员(从新进入第一阶段),每人20比特币。刚给1号官员20比特币, 1号官员很高兴初步接受了议题,还没来得及见到2,3号官员的时候。
  • 这时proposer-2先生也使用分身术分别找3位官员(注意这里是proposer-2的第二阶段),被第1号官员拒绝了告诉他收到了20比特币,第2,3号官员顺利签了合同,这时2,3号官员记录client-2老板用了11比特币中标,由于造成了多数派,因此最终接受了Client2老板中标这个议题,对于proposer-2先生已经出色的完成了工做;
  • 这时proposer-1小姐找到了2号官员,官员告诉她合同已经签了,将合同给她看,proposer-1小姐是一个没有什么职业操守的聪明人,以为跟Client1老板混没什么前途,因此将本身的议题修改成“Client2老板中标”,而且给了2号官员20比特币,这样造成了一个多数派。顺利的再次进入第二阶段。因为此时没有人竞争了,顺利的找3位官员签合同,3位官员看到议题与上次一次的合同是一致的,因此最终接受了,造成了多数派,proposer-1小姐跳槽到Client2老板的公司去了。

Paxos过程结束了,这样,一致性获得了保证,算法运行到最后全部的proposer都投“client2中标”全部的acceptor都接受这个议题,也就是说在最初的第二阶段,议题是先入为主的,谁先占了先机,后面的proposer在第一阶段就会学习到这个议题而修改本身自己的议题,由于这样没职业操守,才能让一致性获得保证,这就是paxos算法的一个过程。原来paxos算法里的角色都是这样的不靠谱,不过不要紧,结果靠谱就能够了。该算法就是为了追求结果的一致性

易用

如何设计对外接口使得系统容易使用?如何设计监控系统并将系统的内部状态以方便的形式暴露给运维人员?

压缩与解压缩

如何根据数据的特色设计合理的压缩与解压缩算法?如何平衡压缩算法节省的存储空间和消耗的CPU计算资源。

分布式系统分类

分布式存储系统须要存储的数据多种多样,大体上可分为:非结构化数据,如文本文件、图片、视频和音频等格式;结构化数据,通常存在关系数据库中,能够用二维关系表结构来表示,模式与内容是分开的;半结构化数据,如HTML文档,模式结构与内容是放在一块儿的。

不一样的分布式存储系统适合存储不一样的数据

分布式文件系统

互联网应用须要存储大量的图片、照片和视频等非结构化数据对象,这类数据以对象的形式组织,对象之间没有关联,这样的数据通常称为Blob(Binary large object)数据。

分布式文件系统适用于存储 Blob 对象,典型的如谷歌的GFS以及它的开源实现HDFS。在系统实现层面,分布式文件系统内部按照数据块(chunk)来组织数据,每一个数据块的大小相同,每一个数据块能够包含我个Blob 对象或者定长块,一个大文件也能够拆分红多个数据块。

典型的系统有 Facebook Haystack 以及 Taobao File System(TFS)。分布式文件系统是分布式的基石,一般做为上层系统的底层存储。

整体上看,分布式文件系统存储三种类型的数据 :Blob 对象、定长块以及大文件。在系统实现层面,分布式文件系统内部按照数据块(chunk)来组织数据,每一个 chunk 的大小大体相同,每一个 chunk 能够包含多个 Blob 对象或者定长块,一个大文件也能够拆分为多个 chunk 。

分布式键值系统

分布式键值系统用于存储关系简单的半结构化数据,它只提供基于主键的 CRUD(Create/Read/Update/Delete)功能。

如Dynamo、Redis和Memcache。从数据结构来看,分布式键值系统与传统的哈希表比较相似,不一样的是,分布式系统支持将数据分布到集群中的多个存储结点。分布式键值系统是分布式表格系统的一种简化实现,通常用做缓存。一致性哈希是分布式键值系统中经常使用的数据分布技术。

典型的系统有 Amazon Dynamo 以及 Taobao Tair。从数据结构的角度看,分布式键值系统与传统的哈希表比较相似,不一样的是,分布式键值系统支持将数据分布到集群中的多个存储节点。分布式键值系统是分布式表格系统的一种简化实现,通常用做缓存,好比淘宝 Tair 以及 Memcache。一致性哈希是分布式键值系统中经常使用的数据分布技术。

分布式表格系统

分布式表格系统用于存储关系较为复杂的半结构化数据,与分布式键值系统相比,分布式表格系统不只仅支持简单的CRUD 操做,并且支持扫描某个主键范围。分布式表格系统以表格为单位组织数据,每一个表格包括不少行,经过主键标识一行,支持根据主键的CRUD功能以及范围查找功能。

分布式表格系统借鉴了不少关系数据库的技术,例如支持某种程度上的事务。典型的系统如Bigtable、HBase和DynamoDB。与分布式数据库相比,分布式表格系统主要针对单张表格的操做,不支持一些特别复杂的操做,好比多表关联、多表链接、嵌套子查询。并且在分布式表格系统中,同一个表格的多个数据行也不要求包含相同类型的列,适合半结构化数据。分布式表格系统是一种很好的权衡,这类系统能够作到超大规模,并且支持较多的功能,但实现每每比较复杂。

分布式表格系统用于存储关系较为复杂的半结构化数据。分布式表格系统以表格为单位组织数据,每一个表格包括不少行,经过主键标识一行,支持根据主键的 CRUD 功能以及范围查找功能。

典型的系统包括 Google Bigtable 以及 Megastore,Microsoft Azure Table Storage,Amazon DynamoDB 等。在分布式表格系统中,同一个表格的多个数据行也不要求包含相同类型的列,适合半结构化数据。

分布式数据库

分布式数据库通常是从单机关系数据库扩展而来,用于存储结构化数据。分布式数据库采用二维表格组织数据,提供 SQL 关系查询语言,支持多表关联,嵌套子查询等复杂操做,并提供数据库事务以及并发控制。

典型的系统包括 MySQL 数据库分片(MySQL Sharding)集群,Amazon RDS 以及Microsoft SQL Azure。分布式数据库支持的功能最为丰富,符合用户使用习惯,但可扩展性每每受到限制。固然,这一点并非绝对的。Google Spanner 的扩展性就达到了全球级,它不只支持丰富的关系数据库功能,还能扩展到多个数据中心的成千上万台机器。除此以外,阿里巴巴 OceanBase 也是一个支持自动扩展的分布式关系数据库。

FastDFS简介

FastDFS 是一个 C 语言实现的开源轻量级分布式文件系统,做者余庆(happyfish100),支持 Linux、 FreeBSD、 AID 等 Unix 系统,解决了大数据存储和读写负载均衡等问题,适合存储 4KB~500MB 之间的小文件,如图片网站、短视频网站、文档、app 下载站等,UC、京东、支付宝、迅雷、酷狗 等都有使用,其中 UC 基于 FastDFS 向用户提供网盘、广告和应用下载的业务的存储服务, FastDFS 与 MogileFS、HDFS、TFS 等都不是系统级的分布式文件系统,而是应用级的分布式文件存储服务.

FastDFS原理架构

FastDFS 服务有三个角色

# 跟踪服务器(tracker server)
# 存储服务器(storage server)
# 客户端(client)

tracker server:跟踪服务器,主要作调度工做,起到均衡的做用;负责管理全部的 storage server 和 group,每一个 storage 在启动后会链接 Tracker, 告知本身所属 group 等信息,并保持周期性心跳, Tracker 根据 storage 心跳信息,创建 group--->[storage server list]的映射表;tracker 管理 的元数据不多,会直接存放在内存;tracker 上的元信息都是由 storage 汇报的信息生成的,自己 不须要持久化任何数据,tracker 之间是对等关系,所以扩展 tracker 服务很是容易,之间增长 tracker 服务器便可,全部 tracker 都接受 stroage 心跳信息,生成元数据信息来提供读写服务(与 其余 Master-Slave 架构的优点是没有单点,tracker 也不会成为瓶颈,最终数据是和一个可用的 Storage Server 进行传输的)

storage server:存储服务器,主要提供容量和备份服务;以 group 为单位,每一个 group 内能够包 含多台 storage server,数据互为备份,存储容量空间以 group 内容量最小的 storage 为准;建 议 group 内的 storage server 配置相同;以 group 为单位组织存储可以方便的进行应用隔离、负 载均衡和副本数定制;缺点是 group 的容量受单机存储容量的限制,同时 group 内机器坏掉,数据 恢复只能依赖 group 内其余机器从新同步(坏盘替换,从新挂载重启 fdfs_storaged 便可)

多个 group 之间的存储方式有 3 种策略:round robin(轮询)、load balance(选择最大剩余空 间的组上传文件)、specify group(指定 group 上传)

group中 storage 存储依赖本地文件系统,storage 可配置多个数据存储目录, 磁盘不作 raid, 直接分别挂载到多个目录,将这些目录配置为 storage 的数据目录便可

storage`接受写请求时,会根据配置好的规则,选择其中一个存储目录来存储文件;为避免单 个目录下的文件过多,storage 第一次启时,会在每一个数据存储目录里建立 2 级子目录,每级 256 个,总共 65536 个,新写的文件会以 hash 的方式被路由到其中某个子目录下,而后将文件数据直 接做为一个本地文件存储到该目录中

总结:1.高可靠性:无单点故障 2.高吞吐性:只要 Group 足够多,数据流量是足够分散的

FastDFS 提供基本的文件访问接口,如 upload、download、append、delete 等

选择 tracker server

集群中 tracker 之间是对等关系,客户端在上传文件时可用任意选择一个 tracker

选择存储 group

当 tracker 接收到 upload file 的请求时,会为该文件分配一个能够存储文件的 group,目前 支持选择 group 的规则为:

# Round robin,全部 group 轮询使用
# Specified group,指定某个肯定的 group
# Load balance,剩余存储空间较多的 group 优先
选择 storage server

当选定 group 后,tracker 会在 group 内选择一个 storage server 给客户端,目前支持选择 server 的规则为:

# Round robin,全部 server 轮询使用(默认)
# 根据 IP 地址进行排序选择第一个服务器(IP 地址最小者)
# 根据优先级进行排序(上传优先级由 storage server 来设置,参数为 upload_priority)
选择 storage path(磁盘或者挂载点)

当分配好 storage server 后,客户端将向 storage 发送写文件请求,storage 会将文件分配一 个数据存储目录,目前支持选择存储路径的规则为:

# round robin,轮询(默认)
# load balance,选择使用剩余空间最大的存储路径
选择下载服务器

目前支持的规则为:

# 轮询方式,能够下载当前文件的任一 storage server
# 从源 storage server 下载
生成 file_id

选择存储目录后,storage 会生成一个 file_id,采用 Base64 编码,包含字段包括:storage server ip、文件建立时间、文件大小、文件 CRC32 校验码和随机数;每一个存储目录下有两个 256*256 个子目录,storage 会按文件 file_id 进行两次 hash,路由到其中一个子目录,,而后将文件已 file_id 为文件名存储到该子目录下,最后生成文件路径:group 名称、虚拟磁盘路径、数据两级 目录、file_id

其中,组名:文件上传后所在的存储组的名称,在文件上传成功后由存储服务器返回,须要客户端 自行保存

虚拟磁盘路径:

存储服务器配置的虚拟路径,与磁盘选项store_path参数对应

数据两级目录

存储服务器在每一个虚拟磁盘路径下建立的两级目录,用于存储数据文件

同步机制
新增 tracker 服务器数据同步问题

因为 storage server 上配置了全部的 tracker server,storage server 和 tracker server 之间的通讯是由 storage server 主动发起的,storage server 为每一个 tracker server 启动一个线程进行通讯;在通讯过程当中,若发现该 tracker server 返回的本组 storage server 列表比本机记录少,就会将该 tracker server 上没有的 storage server 同步给该 tracker,这样的机制使得 tracker 之间是对等关系,数据保持一致

新增 storage 服务器数据同步问题

若新增 storage server 或者其状态发生变化,tracker server 都会将 storage server 列表同步给该组内全部 storage server;以新增 storage server 为例,由于新加入的 storage server 会主动链接 tracker server,tracker server 发现有新的 storage server 加入,就会将该组内全部的 storage server 返回给新加入的 storage server,并从新将 该组的 storage server 列表返回给该组内的其余 storage server;

组内 storage 数据同步问题

组内 storage server 之间是对等的, 文件上传、 删除等操做能够在组内任意一台 storage server 上进行。文件同步只能在同组内的 storage server 之间进行,采用 push 方式, 即源服务器同步到目标服务器

# 只在同组内的 storage server 之间进行同步
# 源数据才须要同步,备份数据再也不同步
# 特例:新增 storage server 时,由其中一台将已有全部数据(包括源数据和备份数据)同步到新增服务器

storage server 的 7 种状态: 经过命令 fdfs_monitor /etc/fdfs/client.conf 能够查看 ip_addr 选项显示 storage server当前状态 INIT: 初始化,还没有获得同步已有数据的源服务器 WAIT_SYNC :等待同步,已获得同步已有数据的源服务器 SYNCING : 同步中 DELETED : 已删除,该服务器从本组中摘除 OFFLINE : 离线 ONLINE : 在线,尚不能提供服务 ACTIVE : 在线,能够提供服务

组内增长 storage serverA 状态变化过程

storage server A主动链接 tracker server,此时 tracker server 将 storage serverA 状态设置为 INIT

storage server A 向 tracker server 询问追加同步的源服务器和追加同步截止时间点(当前时间),若组内只有 storage server A 或者上传文件数为 0,则告诉新机器不须要数据同步,storage server A 状态设置为 ONLINE ;若组内没有 active状态机器,就返回错误给新机器,新机器睡眠尝试;不然 tracker 将其状态设置为WAIT_SYNC

假如分配了 storage server B 为同步源服务器和截至时间点,那么 storage server B 会将截至时间点以前的全部数据同步给 storage server A,并请求 tracker 设置 storage server A 状态为 SYNCING;到了截至时间点后, storage server B 向 storage server A 的同步将由追加同步切换为正常 binlog 增量同步,当取不到更多的 binlog 时,请求 tracker 将 storage server A 设置为 OFFLINE 状态,此时源同步完成

storage server B 向 storage server A 同步完全部数据,暂时没有数据要同步时,storage server B 请求 tracker server 将 storage server A 的状态设置为 ONLINE

当 storage server A 向 tracker server 发起心跳时,tracker sercer 将其状态更改成 ACTIVE,以后就是增量同步(binlog)

注释

# 整个源同步过程是源机器启动一个同步线程,将数据 push 到新机器,最大达到一个磁盘的 IO,不能并发
# 因为源同步截止条件是取不到 binlog,系统繁忙,不断有新数据写入的状况,将会致使一直没法完成源同步过程
下载

client 发送下载请求给某个 tracker,必须带上文件名信息,tracker 从文件名中解析出文件的 group、大小、建立时间等信息,而后为该请求选择一个 storage 用于读请求;因为 group 内的文 件同步在后台是异步进行的,可能出现文件没有同步到其余 storage server 上或者延迟的问题, 后面咱们在使用 nginx_fastdfs_module 模块能够很好解决这一问题

文件合并原理

小文件合并存储主要解决的问题:

# 本地文件系统 inode 数量有限,存储小文件的数量受到限制
# 多级目录+目录里不少文件,致使访问文件的开销很大(可能致使不少次 IO)
# 按小文件存储,备份和恢复效率低

海量小文件存储问题请参考:

地址1 地址2

FastDFS 提供合并存储功能,默认建立的大文件为 64MB,而后在该大文件中存储不少小文件; 大文件中容纳一个小文件的空间称做一个 Slot,规定 Slot 最小值为 256 字节,最大为 16MB,即小于 256 字节的文件也要占用 256 字节,超过 16MB 的文件独立存储;

为了支持文件合并机制, FastDFS 生成的文件 file_id 须要额外增长 16 个字节; 每一个 trunk file 由一个 id 惟一标识,trunk file 由 group 内的 trunk server 负责建立(trunk server 是 tracker 选出来的),并同步到 group 内其余的 storage,文件存储合并存储到 trunk file 后,根据其文件 偏移量就能从 trunk file 中读取文件

FastDFS 单节点部署

环境
[Fastdfs-Server]
	主机名 = fastdfs-1
	系统 = CentOS7.6.1810
	地址 = 121.36.43.223
	软件 = libfastcommon-master
    nginx-1.14.0.tar.gz
    fastdfs-master.zip
    fastdfs-nginx-module-master.zip
节点名 IP 软件版本 硬件 网络 说明
redis-master 192.168.171.139 list 里面都有 2C4G Nat,内网 测试环境
安装相关工具和依赖
yum -y install  unzip gcc-c++ perl

# 安装libfastcommon
wget https://github.com/happyfish100/libfastcommon/archive/master.zip
解压编译安装
unzip libfastcommon-master.zip 
cd libfastcommon-master/
./make.sh 
./make.sh install
下载安装FastDFS
unzip fastdfs-master.zip
cd fastdfs-master/
./make.sh && ./make.sh install
修改配置文件
cd /etc/fdfs/
ls
client.conf.sample   storage_ids.conf.sample
storage.conf.sample  tracker.conf.sample

# 去掉全部的.sample后缀
mv client.conf.sample client.conf
mv storage.conf.sample storage.conf
mv storage_ids.conf.sample storage_ids.conf
mv tracker.conf.sample tracker.conf

ls
client.conf  storage.conf  storage_ids.conf  tracker.conf

# 建立文件夹
mkdir -p /home/python/fastdfs/tracker
mkdir /home/python/fastdfs/storage -p

# 首先修改clietn.conf,11行和14行
base_path =  /home/python/fastdfs/tracker
tracker_server = 192.168.171.140:22122
# 此处测试环境tracker和storage在同一台服务器启动,根据实际状况修改


# 接下来修改storage.conf,41行和11行
base_path = /home/python/fastdfs/storage
store_path0 = /home/python/fastdfs/storage
tracker_server = 192.168.171.140:22122


# 接下来修改tracker.conf文件,22行和54行    
base_path = /home/python/fastdfs/tracker
store_group = group1
启动tracker与storage
# 启动tracker
fdfs_trackerd /etc/fdfs/tracker.conf start
or 
service fdfs_trackerd start

# 启动storage
fdfs_storaged /etc/fdfs/storage.conf start
or 
service fdfs_storaged start

# 验证端口
ss -tnl
State      Recv-Q Send-Q Local Address:Port               Peer Address:Port 
LISTEN     0      128         *:22122                   *:*                 
LISTEN     0      128         *:23000                   *:*                

# 查看tracker和storage是否是在通讯       
fdfs_monitor /etc/fdfs/storage.conf |grep ACTIVE
[2020-05-11 01:16:07] DEBUG - base_path=/home/python/fastdfs/storage, connect_timeout=5, network_timeout=60, tracker_server_count=1, anti_steal_token=0, anti_steal_secret_key length=0, use_connection_pool=1, g_connection_pool_max_idle_time=3600s, use_storage_id=0, storage server id count: 0

		ip_addr = 192.168.171.140  ACTIVE

# 此处出现ACTIVE说明两者都正常,能够上传文件测试了

# 测试下是否启动成功,咱们尝试上传文件,从/root/目录上传一张图片试试
fdfs_upload_file /etc/fdfs/client.conf /tmp/test.jpg 
group1/M00/00/00/wKirjF64Ny6Acd86AAGgIaqSzTc703.jpg

# 或者使用下面这种

fdfs_test /etc/fdfs/client.conf upload /tmp/test.jpg 
This is FastDFS client test program v6.06

Copyright (C) 2008, Happy Fish / YuQing

FastDFS may be copied only under the terms of the GNU General
Public License V3, which may be found in the FastDFS source kit.
Please visit the FastDFS Home Page http://www.fastken.com/ 
for more detail.

[2020-05-11 01:18:24] DEBUG - base_path=/home/python/fastdfs/tracker, connect_timeout=5, network_timeout=60, tracker_server_count=1, anti_steal_token=0, anti_steal_secret_key length=0, use_connection_pool=0, g_connection_pool_max_idle_time=3600s, use_storage_id=0, storage server id count: 0

tracker_query_storage_store_list_without_group: 
	server 1. group_name=, ip_addr=192.168.171.140, port=23000

group_name=group1, ip_addr=192.168.171.140, port=23000
storage_upload_by_filename
group_name=group1, remote_filename=M00/00/00/wKirjF64N2CAZZODAAGgIaqSzTc877.jpg
source ip address: 192.168.171.140
file timestamp=2020-05-11 01:18:24
file size=106529
file crc32=2861747511
example file url: http://192.168.171.140/group1/M00/00/00/wKirjF64N2CAZZODAAGgIaqSzTc877.jpg
storage_upload_slave_by_filename
group_name=group1, remote_filename=M00/00/00/wKirjF64N2CAZZODAAGgIaqSzTc877_big.jpg
source ip address: 192.168.171.140
file timestamp=2020-05-11 01:18:24
file size=106529
file crc32=2861747511
example file url: http://192.168.171.140/group1/M00/00/00/wKirjF64N2CAZZODAAGgIaqSzTc877_big.jpg
        
# 出现最后的一个url说明上传成功
安装Nginx
# 安装编译工具和依赖
yum install gcc-c++ pcre pcre-devel zlib zlib-devel openssl openssl-devel

# 下载nginx安装包
wget http://nginx.org/download/nginx-1.14.0.tar.gz

# 解压安装包
tar xvf nginx-1.14.0.tar.gz

# 下载Nginx的fastdfs模块
wget https://github.com/happyfish100/fastdfs-nginx-module/archive/master.zip
unzip fastdfs-nginx-module-master.zip     
mv fastdfs-nginx-module-master /home/python/

# 配置检测并添加模块
cd nginx-1.14.0/
./configure --prefix=/usr/local/nginx --add-module=/home/python/fastdfs-nginx-module-master/src

make && make install 

cp /root/fastdfs-master/conf/http.conf /etc/fdfs/
cp /root/fastdfs-master/conf/mime.types /etc/fdfs/

cp /home/python/fastdfs-nginx-module-master/src/mod_fastdfs.conf /etc/fdfs/
初始化配置
vim /etc/fdfs/mod_fastdfs.conf
# 40,53,62行
tracker_server=192.168.171.140:22122
url_have_group_name = true
store_path0= /home/python/fastdfs/storage

vim /usr/local/nginx/conf/nginx.conf
# 修改nginx配置文件
 43         location ~/group[0-9] {
 44            # root   html;
 45            # index  index.html index.htm;
 46            ngx_fastdfs_module;
 47         }

    
cd /home/software/nginx/sbin/   
/usr/local/nginx/sbin/nginx  -t -c /usr/local/nginx/conf/nginx.conf
ngx_http_fastdfs_set pid=5820
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
上传文件测试
fdfs_upload_file /etc/fdfs/client.conf /root/1.jpg 
group1/M00/00/00/wKirjF64PdmAWTJBAACrRBA5oYI661.jpg

# 浏览器输入IP:8888/(此处复制上面出现的一个路径)