大量小文件的实时同步的解决方案分析

传统的文件同步方案有rsync(单向) 和 unison(双向)等,它们需要扫描所有文件后进行比对,差量传输。如果文件数量达到了百万甚至千万量级,扫描所有文件将非常耗时。而且正在发生变化的往往是其中很少的一部分,这是非常低效的方式。

之前看了Amazon的Dynamo的设计文档,它们每个节点的数据是通过Hash Tree来实现同步,既有通过日志来同步的软实时特点(msyql, bdb等),也可以保证最终数据的一致性(rsync, unison等)。Hash Tree的大体思路是将所有数据存储成树状结构,每个节点的Hash是其所有子节点的Hash的Hash,叶子节点的Hash是其内容的Hash。这样一旦某个节点发生变化,其Hash的变化会迅速传播到根节点。需要同步的系统只需要不断查询跟节点的hash,一旦有变化,顺着树状结构就能够在logN级别的时间找到发生变化的内容,马上同步。

文件系统天然的是树状结构,尽管不是平衡的数。如果文件的修改时间是可靠的,可以表征文件的变化,那就可以用它作为文件的Hash值。另一方面,文件的修改通常是按顺序执行的,后修改的文件比早修改的文件具有更大的修改时间,这样就可以把一个目录内的最大修改时间作为它的修改时间,以实现Hash Tree。这样,一旦某个文件被修改,修改时间的信息就会迅速传播到根目录。

一般的文件系统都不是这样做的,目录的修改时间表示的是目录结构最后发生变化的时间,不包括子目录,否则会不堪重负。因为我们需要自己实现这个功能,利用Linux 2.6内核的新特性inotify获得某个目录内文件发生变化的信息,并把其修改时间传播到它的上级目录(以及再上级目录)。Python 有 pyinotify,watch.py的代码如下:

复制代码

代码如下:

#!/usr/bin/python
from pyinotify import *
import os, os.path
flags = IN_CLOSE_WRITE|IN_CREATE|IN_Q_OVERFLOW
dirs = {}
base = '/log/lighttpd/cache/images/icon/u241'
base = 'tmp'
class UpdateParentDir(ProcessEvent):
def process_IN_CLOSE_WRITE(self, event):
print 'modify', event.pathname
mtime = os.path.getmtime(event.pathname)
p = event.path
while p.startswith(base):
m = os.path.getmtime(p)
if m < mtime:
print 'update', p
os.utime(p, (mtime,mtime))
elif m > mtime:
mtime = m
p = os.path.dirname(p)
process_IN_MODIFY = process_IN_CLOSE_WRITE
def process_IN_Q_OVERFLOW(self, event):
print 'over flow'
max_queued_events.value *= 2
def process_default(self, event):
pass
wm = WatchManager()
notifier = Notifier(wm, UpdateParentDir())
dirs.update(wm.add_watch(base, flags, rec=True, auto_add=True))
notifier.loop()

在已经有Hash Tree的时候,同步就比较简单了,不停地获取根目录的修改时间并顺着目录结构往下找即可。需要注意的是,在更新完文件后,需要设置修改时间为原文件的修改时间,目录也是,保证Hash Tree的一致性,否则没法同步。mirror.py的代码如下

复制代码

代码如下:

#!/usr/bin/python
import sys,time,re,urllib
import os,os.path
from os.path import exists, isdir, getmtime
src = sys.argv[1]
dst = sys.argv[2]
def local_mirror(src, dst):
if exists(dst) and mtime == getmtime(dst):
return
if not isdir(src):
print 'update:', dst
open(dst,'wb').write(open(src).read())
else:
if not exists(dst):
os.makedirs(dst)
for filename in os.listdir(src):
local_mirror(os.path.join(src,filename), os.path.join(dst,filename))
os.utime(dst, (mtime,mtime))
def get_info(path):
f = urllib.urlopen(path)
mtime = f.headers.get('Last-Modified')
if mtime:
mtime = time.mktime(time.strptime(mtime, '%a, %d %b %Y %H:%M:%S %Z'))
content = f.read()
f.close()
return int(mtime), content
p = re.compile(r'([/d.]+?) +([/w/]+)')
def remote_mirror(src, dst):
mtime, content = get_info(src)
if exists(dst) and mtime == int(getmtime(dst)):
return
print 'update:', dst, src
if not src.endswith('/'):
open(dst,'wb').write(content)
else:
if not exists(dst):
os.makedirs(dst)
for mt,filename in p.findall(content):
mt = int(float(mt))
lpath = dst+filename
if not exists(lpath) or int(getmtime(lpath)) != mt:
remote_mirror(src+filename, lpath)
os.utime(dst, (mtime,mtime))
if src.startswith('http://'):
mirror = remote_mirror
else:
mirror = local_mirror
while True:
mirror(src, dst)
time.sleep(1)

如果源文件不在同一台机器上,可以通过NFS等共享过来。或者可以通过支持列目录的HTTP服务器来访问远程目录,mirror.py 已经支持这种访问方式。server.py 是用webpy做的一个简单的只是列目录的文件服务器。由于瓶颈在IO上,它的性能不是关键。server.py的代码如下:

复制代码

代码如下:

#!/usr/bin/python
import os,os.path
import web
import time
root = 'tmp'
HTTP_HEADER_TIME = '%a, %d %b %Y %H:%M:%S %Z'
class FileServer:
def GET(self, path):
path = root + path
if not os.path.exists(path):
return 404
mtime = time.localtime(os.path.getmtime(path))
web.header('Last-Modified', time.strftime(HTTP_HEADER_TIME, mtime))
if os.path.isdir(path):
for file in os.listdir(path):
if file.startswith('.'): continue
p = os.path.join(path,file)
m = os.path.getmtime(p)
if os.path.isdir(p):
file += '/'
print m, file
else:
print open(path,'rb').read()
urls = (
"(/.*)", "FileServer",
)
if __name__ == '__main__':
web.run(urls, globals())

为了获得更好性能,以达到更好的实时性,Hash Tree最好是平衡的,比如BTree。如果一个文件发生变化,同步它需要进行的IO操作为N*M,其中N为数的层数,M为每层的文件数目。现在我们N为2,M最大为10000,适当减少它可以获得更好的性能,比如N为4,M为100。在以后创建目录结构时,最好能够考虑这方面的因素。

之前hongqn推荐过一个利用inotify的文件同步方案,同步方式类似于mysql和bdb等,由于过于复杂导致不可靠而没有采用。上面这个方案只用了一百多行Python代码就基本解决问题了,是不是很帅?:-)

(0)

相关推荐

  • rsync+inotify实现文件的实时同步

    工作中的一个需求,生产环境测试通过,可以实现文件的实时同步,希望对大家有帮助,希望看到的同志,一定要顶,这样才是好同志嘛!呵呵rsync+inotify实现文件的实时同步环境描述:现有两台服务器,要实 ...

  • Win10 Mobile/PC应用连续实时同步功能现身

    在今年4月份,消息称微软将为Win10系统设备加入Continue连续应用体验,允许Windows10 PC用户可以与Win10 Mobile或其他Win10设备进行应用文件资料实时同步.不过到目前为 ...

  • linux系统安装rsync和sersync实现数据实时同步详细步骤(rsync实时同步)

    一、为什么要用Rsync+sersync架构? 1、sersync是基于Inotify开发的,类似于Inotify-tools的工具 2、sersync可以记录下被监听目录中发生变化的(包括增加、删除 ...

  • Google 文档图表小工具自动实时更新技巧

    对于很多做财经工作或者是撰写财经类博客网站,而且经常出差的朋友来说,在外面更新以图表形式存在的博客(或者单位网站协同办公系统)内容并不容易:缺少专业软件大大制约了他们的工作。 如果图表能实时同步自动更 ...

  • Google文档图表小工具自动实时更新技巧

    如果图表能实时同步自动更新,那该多好!这时想到了正在网络疯狂流行的在线办公系统,经过一番琢磨发现"Google文档"完全能够实现此功能. 步骤/方法 01 第一步:在IE浏览器中打 ...

  • 电脑文件如何云同步网络中?以360云盘同步文件为例

    文件同步到网络中,无论你身处何地只要有电脑有网络就可以轻松的实现办公需求,那么文件如何实现云同步呢,以下小编以360云盘为例教你实现文件同步网络中。 首先,如果你没有安装360云盘可到登录官网下载,大 ...

  • Win7系统打开CDR文件是空白的完美解决方案

    使用电脑的用户对绘图软件有所了解的都知道,关于CDR格式就是著名绘图软件CorelDRAW的专用图形文件格式,因为CorelDRAW是矢量图形绘制软件,所以CDR才可以记录文件的属性.位置和分页等.可 ...

  • 如何利用fastcopy快速拷贝数量众多小文件?

    在我们的电脑里存放数量众多的小文件,这些小文件加起来体积也非常庞大.有时想要把这些文件移动到别的地方或者备者,我们就很抓狂:拷贝这些文件需要漫长的时间.要想解决这个问题只靠windows自带的复制工具 ...

  • 使用WinRAR压缩软件 把大文件分割为几个小文件的方法[图文]

    我电脑中有一个虚拟机软件Virtual PC,文件名是Microsoft_ Virtual_PC_ 2007_ SP1_32bits.exe,文件大小为31.7MB。我想把它上传到csdn的资源空间中 ...