TFS没有采用目前流行的3副本策略,而是使用集群内配置2个副本,并把数据同步到一个辅集群,辅集群同样配置2个副本;这种方式对于实现异地机房容灾很方便,辅集群的数据同步是由主集群的数据服务器在后台发起的;目前主集群可读写,辅集群只读,双集群同时读写功能已开发完成,但没有在线上使用。

由于数据同步到辅集群是异步操作,当某次读文件的操作落到辅集群上时,可能该文件的同步尚未完成,此时在辅集群上读不到数据,这个问题目前是通过失败后在集群间重试来避免(迭代);还有一种方式就是当辅集群发现文件不存在时,主动去辅集群同步(递归),同步完成后向用户返回数据;这样做逻辑虽然正确,但存在几个问题:(1)在用户的一个请求中,递归的从主集群同步文件数据,如果文件较大,会导致用户请求超时;(2)将主辅集群间的同步协议复杂化了,当辅集群指定同步某个文件(而不是按主集群接收到请求的顺序),会打乱同步时序逻辑,从而爆发一些问题;比如文件是经过写(A)、更新(B)等操作才到达目前的状态,那么在同步到辅集群后,下次数据服务器(DS)重放日志同步时,写操作(A)实际上变成了更新操作;另外TFS还支持用户删除、隐藏文件,这些操作组合跟写组合到一起时,会产生更多微妙的问题。

主辅集群的所有副本,可能会发生一些数据不一致的问题,先讨论下集群内的不一致情况

  1. 某个数据服务器上内存、磁盘错误可能导致数据出现变化,这个问题目前通过写磁盘前检查文件crc来避免;
  2. 写多个副本只有master成功,其它slave副本都失败;由于TFS的集群内写操作是多个副本强一致性的(理论上),出现这种情况,会向用户返回失败信息,用户根本不知道这个文件的存在,这种不一致不会引发问题;
  3. TFS删除、隐藏操作在master副本上保证状态正确,在slave副本上的状态是不保证的;可能某个文件删除了,但其在某些副本上依然存在;由于文件的更新只会有master完成再同步到slave副本上,所以文件即使在slave上存在但不会被更改了,同时用户删除了文件通常不会再访问了,恶意用户因为不知道该文件对应的TFS文件名,所以也访问不到文件。
  4. 某个副本丢失,这个会有后台的复制任务来完成副本的复制,以保证数据的安全。

对于集群内的数据不一致,都有相应的处理措施,但集群间数据的维护是独立进行的,相互间没有影响,一旦出现主辅数据不一致的情况(出现几率较小),没有任何措施来处理。集群间数据对比工作主要目的在于发现集群间数据的不一致,并进行处理。

集群对比最简单的方式就是逐个block进行对比(逐个文件的方式不现实),目前最大的集群使用的block数约为850w,获取一个block上所有文件的信息(需要遍历block)约50ms,一次全量对比的时间约5天(持续不间断请求的理论值),如果每次都进行全量的对比显然是不能接受的,必须引入增量式的检查方案。

数据不一致主要出现在数据修改时,要实现增量式的对比,只需对比修改过的block即可,主要设计思想如下:

  1. DS记录修改的block。
  2. 增加checkserver收集DS修改的block信息并合并对比。
  3. 根据对比结果进行相应处理。

DS要记录修改的block,须在改变block内容的接口上增加hook,目前只在写(包括更新)操作、以及块复制、压缩完成时增加hook,将修改block的id和修改时间记录到一个map中(修改block的信息只记录在内存上,它在block副本对应的所有机器上都会记录一份,这些修改信息即使丢失也不会对系统有影响,故不需要进行持久化存储),当checkserver(简称CS)发送check请求时,DS将上次检查到这次检查间隔内修改的block信息(文件数量、文件总大小等)回复给CS,CS将所有主辅集群上相同block的信息进行汇总并对比。

理论上DS每次返回block信息后,就可以将该段时间内修改的block数据删掉,但当集群DS数量很多时,如果CS在检查过程中宕机,这个检查过程是不能重现的(除非CS持久化从DS获取到的block信息),即使block间有不一致的情况,也只能在block下次被修改的时候发现。据不完全统计,2T的盘约有2.3w左右的block,由于每个block的元数据只有12B,即使所有block都被修改,总的数据占用内存也不会超过1M,对系统内存使用的影响不大;故为了简化CS,使其无状态,DS hook的block修改数据是不删除的。

CS的检查是一个周期性执行的任务,CS每次检查从上次检查后到本次检查时间间隔内的block,为了避免异步同步的影响(主集群上的写,还没有同步到辅集群),对每次检查block的条件再加了一个限制,修改了并且超过指定时间(认为其已经稳定了,如5min,可配置)没有修改,CS的检查间隔、block的稳定时间都是在CS上配置的,检查的目标时间段也在CS上计算好传至DS,避免修改配置项需要重启DS。

CS收集到DS上修改的block信息后,对blockid相同的信息进行合并,最后从每个集群选择version最高的block用于对比,通过对比发现主辅集群上block数据不一致时就需要对该block进行同步(该任务与检查分开进行)。关键问题是如何认定主辅集群上block不同?最简单的方法是,直接对比block上的文件数和文件总大小,对于在某一个block上不存在的block,则需要从另一个集群同步过去。但这个策略存在很多问题:

  1. 可能某次检查,某个修改的block在主集群上稳定了,在被集群还没稳定(且很快就会变成稳定状态),这时CS就认为这个block需要同步到备集群,而这个同步过程显然是多余的。最开始跟@daoan讨论的方案是增加一个overlap的检查时间(2次检查有一段重叠时间),但这样就无法区分block需要同步还是刚好出现上述的临界状态。
  2. 针对某个DS的检查请求,由于网络等原因失败,但实际上一切正常,这时CS也会发现block在某个集群上不存在,认为其需要同步,而这个同步过程同样是多余的。
  3. block可能在某个集群上发生了压缩,或是某个文件在主集群上写后,在写同步前又删掉了,这时直接根据index中的文件数和文件总和来对比block是不准确的。

从上面的3点可以看出,通过获取hook的block,并从index获取文件信息是不准确;要想真正判定两个block数据不一致,必须排除掉block中被删除的文件,这就需要遍历整个block(开销比遍历index大很多);最终的实现方案是:首先CS执行一次检查,收集block信息,并对比进行一次初筛,对于数据可能不一致的block,将其加入到一个recheck list中,最后对于recheck list中的所有block,进行更细节的对比(排除删除文件的影响),如果发现block数据仍不同,则认为需要同步。

最后,集群同步工具根据CS的检查结果,对需要同步的block进行同步,使整个集群达到一致的状态。

ref:
2013-04-19 14:13
blog.yunnotes.net

0 回复
需要 登录 后方可回复, 如果你还没有账号你可以 注册 一个帐号。