VFP 学习、开发漫谈 (20)
今天与大伙聊一聊有关数据的备份问题。对于一个新手来说,往往会忽视数据备份的重要性,可能会认为数据备份没有什么技术含量,在 Windows 资源管理器下使用“复制”、“粘贴”即可搞定。
如果你使用 Windows 的“复制”、“粘贴”功能对数据进行手动备份,主要存在以下问题:
1. 由于 VFP 的表字段都是定长的,数据存在很多冗余,直接复制则占用空间较多。
2. VFP 的数据文件众多,对于一个 dbf 文件来说,可能还存在索引文件(cdx)和备注文件(fpt)。将这么一大堆文件放在一个文件夹下,不易管理。
3. 当表独占打开时,是无法备份的;当表以共享方式打开时,才可以进行备份。但是,若备份过程中其他用户同时又在写入数据,则有可能造成备份的数据不完整,打开表时系统提示“表已损坏”。因此,在进行备份时,应该选择无用户写数据时进行。
4. 人都是有惰性的,况且备份本来就是“养兵千日,用兵一时”,时间一长,思想上一放松,难免出现“三天打渔,两天晒网”。如果有这么一天,服务器的硬盘坏了,碰巧你又未备份,那你可就“摊上大事儿了”。
为了解决以上问题,现提出如下解决方案:
1. 采用 WinRAR 压缩工具进行备份,将要备份的所有文件压缩成一个文件,既节约了空间,又方便管理。
2. 使用一个定时器,在深夜无人使用系统时,由系统自动执行备份。比如,将备份时间定在 23:00。
3. 备份文件应该保存到另一服务器的硬盘上,即通常所说的“异地备份”,以增加备份文件的安全性。若将备份文件与原数据放在同一块硬盘上,当硬盘损坏时,备份也随之“泡汤”,这就失去了备份的意义。
4. 对备份文件的命名,一般采取“日期 + 序号”的形式,如:20140429-1.ZIP,表示2014年4月29日的第一次备份。备份文件名由系统自动生成。
5. 由于定时器的时间间隔不可能设的太长(在我的系统中,设置 Timer.InterVal = 600000,10分钟),因此必须防止系统重复备份。通常,每天备份一次。
有一点,需要强调一下:数据备份主要是为了降低因服务器硬盘损坏造成的数据丢失。使用备份可将数据恢复到前一天,但当天发生的数据还是会丢失,需要重新录入。如果是因意外断电等原因,造成数据表逻辑损坏而无法打开,则可以使用 UltraEdit 对数据表进行修复,一般不会造成数据丢失,无需使用备份恢复数据。在下一课,我打算专门针对数据表的存储结构以及如何修复数据表问题,与大家交流。
根据以上思路,我设计了一个数据备份程序,主界面如下所示:
说明:
1. 上图中的“备份项目”源于 Sys_Set.dbf,结构如下:
● 项目(Item):用于标识要备份的应用系统。如:上图表示可对服务器上的“人事档案”和“物料管理”两个系统进行备份。
● 来源路径(Folder_Src):表示要备份的文件来自服务器的哪个文件夹。
● 备份文件(File_Bak):这是一个备注型字段,存放的是要对哪些文件进行备份,对应主窗口中的“备份文件”列表。
● 保存路径(Folder_Tgr):表示要将生成的压缩文件保存到什么位置。
● 定时(Time_Bak):表示在采用“定时备份”模式时,在什么时间开始备份。
● 生效(Valid):表示在采用“定时备份”模式时,是否对当前项目进行备份。
2. 为了掌握系统备份的执行情况,同时也为了防止在定时备份模式下重复备份,系统还建立了一个运行日志表 Run_Log.dbf,结构如下:
以第一条记录为例,说明系统在 2014年4月28日23点0分58秒时,对物料管理系统进行了备份,生成的备份文件放在 F:\Bak\Mis\20140428-1.ZIP 中。系统预先设定的备份时间是 23点0分0秒。通过比较约定时间和实际执行时间,可以判断出该项目当日是否已备份,防止重复处理。
3. 通过“新建项目”、“修改项目”和“删除项目”三个按钮,可以对备份项目进行维护:
4. 系统有两种备份模式,一种是“手动备份”,即选定备份项目后,单击“手动备份”按钮即可。另一种是“定时备份”,由系统根据选定的备份项目,在设定的时间点自动备份,每天一次。
在主表单上,添加有一个 Timer 定时器控件。以定时备份模式为例,在“定时备份”按钮的 Click 事件中输入以下代码,以启用定时器控件,使其每隔一定时间,检测并执行备份操作:
程序代码:
IF THIS.Caption = '定时备份' * 检测是否选择了生效的备份项目 SELECT sys_set LOCATE FOR valid IF !FOUND() MESSAGEBOX('请将要执行的备份项目设置为“生效”!',16,'提示') RETURN ENDIF * 启动定时器,禁用命令按钮 THIS.Caption = '停止备份' THISFORM.Timer1.Enabled = .t. THISFORM.SetAll('Enabled',.f.,'CommandButton') THIS.Enabled = .t. ELSE * 停止定时备份 THIS.Caption = '定时备份' THISFORM.Timer1.Enabled = .f. THISFORM.SetAll('Enabled',.t.,'CommandButton') ENDIF
在定时器的 Timer 事件中,输入以下代码,以执行备份操作:
程序代码:
LOCAL cDir_Src,cDir_Tgr,cFiles,i,nNum,cArchive,cCmdLine THIS.Enabled = .f. && 暂停计时器 SELECT sys_set SCAN FOR valid * 检查项目的定时备份是否已经运行过 SELECT run_log LOCATE FOR item_id = sys_set.id AND time = sys_set.time_bak AND DTOS(date) = DTOS(DATE()) * 当日未备份,且已到约定时间 IF !FOUND() AND TIME() >= sys_set.time_bak SELECT sys_set cDir_Src = ADDBS(ALLTRIM(folder_src)) && 来源文件夹 cDir_Tgr = ADDBS(ALLTRIM(folder_tgr)) && 目标文件夹 IF !DIRECTORY(cDir_Tgr) && 建立目标文件夹 MD (cDir_Tgr) ENDIF * 生成备份列表文件 backup.lst cFiles = '' FOR i = 1 TO MEMLINES(file_bak) cFiles = cFiles + IIF(i=1,'',CHR(13)+CHR(10))+cDir_Src+MLINE(file_bak,i) NEXT = STRTOFILE(cFiles,'c:\bak_tmp\backup.lst') * 获取备份文件名 cArchive nNum = ADIR(aFile,cDir_Tgr + DTOS(DATE()) + '*.zip') FOR i = 1 TO 999 cArchive = cDir_Tgr + DTOS(DATE()) + '-' + TRANS(i) + '.ZIP' IF EMPTY(nNum) OR ASCAN(aFile,JUSTFNAME(cArchive)) = 0 EXIT ENDIF NEXT * 执行备份 cCmdLine = 'winrar.exe a -dh -afZIP -ilogc:\bak_tmp\backup.log -inul &cArchive @c:\bak_tmp\backup.lst' RUN /N &cCmdLine * 更新日志文件 INSERT INTO run_log (item_id,item_desc,time,date,zip_file) VALUES (sys_set.id,sys_set.item,sys_set.time_bak,DATETIME(),cArchive) ENDIF ENDSCAN THIS.Enabled = .t. && 重启计时器
采用自动备份程序,可大大减轻数据备份的工作量。只需定期对生成的备份文件维护一下即可:保留当月的全部压缩包,以前月份的压缩包每月仅保留一个,一般保留每月最后一天的备份。
[ 本帖最后由 liuxingang28 于 2014-4-29 20:34 编辑 ]