我在我们的猎鹰产品中完成了一个边角料的独立模块,就是差异备份的实现,其实起初我不太赞成做这个模块,因为开源的程序多得是,都是关于备份的,然后总工非要做不可,人在屋檐下不能不低头啊,我还是很不爽的完成了他的要求,虽然带有明显的完任务的性质,但是做下来发现有些东西还真是要仔细考虑的,差异备份的需求是,用户可以随时进行差异备份,所谓差异备份就是将本次备份时刻的文件系统和上次备份时的差异部分进行备份,可想而知,差异备份依靠一个完全备份,一个备份的系统至少依赖一个完全备份作为还原基,每当用户执行备份恢复的时候,流程就是首先恢复恢复点之前最近一次的完全备份,然后将恢复点的差异备份覆盖于完全备份之上,这里面有几点要考虑,就是要体现修改的文件,增加的文件,以及删除的文件,也就是说修改的文件恢复后必须修改,增加的文件必须也要有,并且如果用户在完全备份和差异备份期间删除了一个文件,那么恢复之后这个被删除的文件将不能存在。

最显然的方案就是在做完全备份的时候保存所有文件的指纹,其实就是依靠文件名,文件大小,文件创建时间,最后修改时间等属性信息计算出来的一个值,在做差异备份的时候,首先读出这些指纹,然后一个一个和当前文件系统的每一个文件比对,如果发现有不同的,则备份,如果发现现在有的指纹而以前没有的,则备份,如果扫描了一遍,最终完全备份时的没有被访问到指纹所代表的文件视为已经被删除的文件,将这些文件记录,在恢复的时候,最后记住删除掉差异备份时记录的已经删除的文件列表中的文件就可以了。理论看起来很简单,但是实现起来还真是麻烦,第一,如何可以很高效的计算指纹以及遍历文件系统的文件以及比对指纹信息,这实际上归结为一个算法问题,另外就是如何组织数据结构也是很恼人的,这涉及到将来的扩展已经当下的排错等等。

以下是备份信息记录结构,这个结构是由所有的文件填充的,每一个文件对应一个以下的结构体,

typedef struct {

char strFileName[MAX_PATH]; //文件名

unsigned int iFileNo; //文件编码,由GetFncode函数给出

unsigned int iFileNameLen; //文件名长度

char flag; //文件备份时的访问标志:访问--1;未访问--0

char used; //本记录结构是否被使用:使用--1;未使用--0

unsigned int iLastWriteHigh; //最后一次修改时间

unsigned int iLastWriteLow; //最后一次修改时间

}BAK_FILE_INFO,*PBAK_FILE_INFO;

以下这个是获取文件编码的函数,所有的文件的信息在内存的组织类似于一条哈希链表,通过以下这个函数得出的一个编码用来定位该文件信息所在的行,同一行的所有的文件信息也就是BAK_FILE_INFO对应的文件拥有同一个文件编码值,这和哈希定位非常类似。

UINT GetFncode(char *str)//根据文件名称,获取文件名编码,这个函数可以随意定义,怎么高效就怎么来

{

CHAR lower_str[256];

CHAR *lp=lower_str;

UINT h=0;

memset(lower_str,0,256);

strcpy_s(lower_str,str);

while(*lp)

{

*lp=tolower(*lp);

h=(h+(int)*lp++)*2654435387u;

}

return h;

}

在详细描述过程之前有一些前置工作,这就是该备份程序如何得到指导信息,指导信息将指导该备份程序如何进行备份,指导信息是通过数据库中转的,客户程序将指导信息插入数据库的一个叫做bakup_parameter表,然后发送备份命令,备份程序接收到命令之后会主动查询数据库的bakup_parameter表,得到信息之后将指导信息存入一个内存中的结构体RESULT:

typedef struct

{

unsigned int iInterval_full; //自动完全备份的间隔时间,单位:分钟

unsigned int iInterval_sub; //自动差分备份的间隔时间,单位:分钟

char strRecover[13]; //恢复到的时间,格式:YYYYMMDDHHMM

char strFullBakupDir[MAX_PATH]; //完全备份的备份路径

char strSubBakupDir[MAX_PATH]; //差分备份的备份路径

char strStationDir[MAX_PATH]; //需要备份的源路径

char strRecoverDir[MAX_PATH]; //需要恢复到的路径

char strRecoverFull[13]; //恢复的完全备份时间,格式:YYYYMMDDHHMM

char strRecoverSub[13]; //恢复的差分备份时间,格式:YYYYMMDDHHMM

char strTimeTo[13]; //要恢复到的时间,格式:YYYYMMDDHHMM

char strStart[13]; //开始时间,格式:YYYYMMDDHHMM

char strLocalIP[15]; //本镜像引擎的IP

int iFull; //是否第一次强制全备份

}RESULT,*PRESULT;

然后备份程序就是依据这个结构体来进行备份的,数据库表的设计如下:

备份恢复设置表名:bakup_parameter

--create table bakup_parammeter(interval_f int,interval_s int,recover varchar(12),bakup_dir varchar(260),station_dir varchar(260),recover_dir varchar(260),start varchar(12),start_mode int,server_ip varchar(15))

格式:

interval_f (int) ---自动全备份间隔(单位:分钟)

interval_s (int) ---自动差分备份间隔(单位:分钟)

recover (char 12 ) ---恢复到的时间:YYYYMMDDHHMM格式

bakup_dir (char 260) ---备份路径(绝对路径)

station_dir (char 260) ---网站路径(绝对路径)

recover_dir (char 260) ---恢复网站路径(绝对路径)

start (char 12) ---自动备份开始时间:YYYYMMDDHHMM格式

start_mode (int) ---启动的类型:1-强制全备份;2-差分备份

server_ip (char 15) ---镜像引擎的IP(点分十进制)

说明:每次需要备份的时候就要insert上表的一个记录,如果当前记录没有用到表的某个字段,则:int型的置0,char型的置""

参数需求:

手动全备份: bakup_dir / station_dir / server_ip

手动差分备份: bakup_dir / station_dir / server_ip

备份恢复: recover / recover_dir / station_dir / server_ip

备份日至表名:bakup_info

--create table bakup_info(sub_time varchar(12),sub_bakup_dir varchar(260),full_time varchar(12),full_bakup_dir varchar(260), station_dir varchar(260),server_ip varchar(15))

格式:

sub_time (char 12) ---差分备份的时间,如果本次为完全备份则为完全备份的时间:YYYYMMDDHHMM格式

sub_bakup_dir (char 260)---差分备份的路径,如果本次为完全备份则为完全备份的路径

full_time (char 12) -本次差分备份对应的完全备份时间,如果本次就是完全备份则和sub_time相同

full_bakup_dir (char 260)---本次差分备份对应的完全备份的备份路径,如果本次就是完全备份则和sub_bakup_dir相同

station_dir (char 260)---网站的路径

server_ip (char 15) ---镜像引擎的IP(点分十进制)

说明:可以恢复的时间点为上表的sub_time字段,完全读取上表取sub_time字段就可以得到所有可恢复的点参数需求:本表只在查询恢复点时对于备份客户端有用,恢复网站时,备份客户端以目录全路径为查找键,查找出所有对应记录,然后取sub_time作为恢复时间列到用户界面列表控件中,sub_time要格式化为YYYY-MM-DD-HH:MM格式或者YYYY/MM/DD HH:MM格式或者一切可读的时间格式。