对操作系统进行备份和还原也许是最常用的。实际业务环境升级后,因多方面原因存在严重问题,这时很可能需要还原到升级前的状态。因此数据库建立备份 并能进行还原就很有意义。本文是一个使用c#(Visual Studio 2008)结合Oracle客户端完成还原点的 建立与恢复的完整例子。

  明确还原目标
  在建立还原点时,首先要明确还原对象,我们所提及的还原并不是简单地对数据库某个时间点整个数据库的备份与还原,因为在升级后发生的实际业务数 据是不能进行还原的。
  会引起重要问题的主要是程序,在数据库中体现在包、函数和存储过程以及与流程相关的参数、方案等核 心字典数据与界面层的一致性。而具体业务中产生的数据如收费项目、收费明细等,是不能进行还原的,明确还原目标后,问题的解决就有方向性了。
  要建立还原点,首先要了解数据库中关键对象的存放位置,对于包、函数等可以在Oracle的数据库视图 user_source中找到。
  备份:建立还原对象列表
  在建立C#的 工程之后,今天我们利用配置文件app.config.xml,设定了相应的“导出对象”,“Oracle连接”等内容,如图001:
  这样参数就可以在CONFIG中进行调整了。在进行备份时,我们首先要取得相关的程序列表,下面的语句可以找到我们需要的导出对象:
   selectdistinctus.name,us.typefromuser_sourceuswhereus.typein('PROCEDURE','FUNCTION','PACKAGE','PACKAGEBODY');
  可以使用 configurationAppSettings来取得配置文件中的设置,如导出对象、数据库连接、回滚目录,如:
  ls_configs=(string)(configurationAppSettings.GetValue("导出对 象",typeof(string))); 首先针对“导出对象”所定义的串进行拆分,ls_typeinfo存放需要导出的类型,并建立Oracle的数据库连接,再根据需要取得数据查询结果:
  ls_querysql="SelectdistinctUs.Name,
   us.typeFromUser_SourceUswhereus.typein("+ls_typeinfo+")orderbyus.Name,us.Type";
  OracleCommand1.CommandText=ls_querysql;
  OracleCommand1.CommandType=CommandType.Text;
  //如何解析commandText的值
   OracleDataReadermyReader=OracleCommand1.ExecuteReader(CommandBehavior.CloseConnection);
  while(myReader.Read())
  //使用OracleDateReader前进到下一条记录 通过循环,获得信息列表存放到对象列表listPrcInfo中,它包括两个项目,分别就是:对象名strName,对象类别strType。
  备份:有进度地产生各个对象体
  生成了对象列表后,再根据每个对象名和对象类别来读取内容,相对来说就比较简单,只要使用以下方法:
  ls_querysql = " Select us.line, us.text From User_Source Us where us.name = '" + listPrcInfo[i].strName +"' and us.type='"+listPrcInfo[i].strType+"' order by us.line ";  OracleCommand1.CommandText = ls_querysql;  OracleCommand1.CommandType = CommandType.Text;//如何解析commandText的值 来读取对象的程序内容,并按照我们在CONFIG中所对应的各种文件扩展名来保存文件:
 注意,由于我们数据库程序需要使用SQLPLUS来进行导入,因此最后一行我们需要写入结束标志符,即“/”符号,它的最后内容是这样的:
 由于表的导出我们依赖数据库的导出功能,只要使用exp命令导出指定的字典表即可。即与具体业务无关的核心字典表,如功能列表、参数列表、菜单项 目等,基本上使用类似于以下的命令结构即可完成 :
  exp.exe his3/his3@mytest file=d:\roll\roll.dat tables=(%1) 其中的%1所对应的项目,是我们需要导出的表项目,由于导入功能的特殊性,会在导入功能中着重介绍。
  还原:还原的流程
  前文可以看出,创建还原点的重点在于如何把所有数据库中的程序进行导出,对于关键字典的备份反而比较简单。接下来对数据进行还原,即关键字典数 据处理要加以注意。还原的处理流程是:
  1、 对于当前数据库中的字典数据再次创建备份,不过本次备份是在数据库中以表的方式进行备份(因为还原时关键字典表中的数据仍可能会有用到的数据,所以需要备 份),同时DROP掉相应的字典表;
  2、 利用Oracle的IMP命令,将数据备份的字典数据进入导入(对于表上的触发器,其实是表的附加属性,在导出时会自动备份,导入时也会自动加上)。
  3、 对于数据库中的程序,则利用SQLPLUS执行脚本的方法来完成,即建立相应的执行脚本(由于程序都是升级工具升级上来的,还原要再升级,其程序部分的升 级可在升级工具脚本中找到);
  还原:创建还原脚本
  还原脚本分为表的脚本和程序还原脚本,表处理部分,首先使用:
  ls_querysql = " Select to_char(sysdate,'yyyymmddhh24miss') from dual "; 取得时间戳,表示当前进行还原操作的时间点,再利用:
  ls_tables = (string)(configurationAppSettings.GetValue("导出表", typeof(string))); 取得还原时的表,最终生成类似于以下的脚本(tabbak.sql):
  Create table R20100213110513gy_canshu as select * from gy_canshu;
  drop table gy_canshu;
  Create table R20100213110513xt_selectsql1 as select * from xt_selectsql1;
  drop table xt_selectsql1;
  …… 通过 sqlplus his3/his3@mytest @tabbak.sql就可以执行上述脚本。对于数据库的程序体,也需建立相应脚本,考虑到我们建立的还原点数据可能会备份至新目录,所以我们利用在建立 还原点时的列表文件prc.lst,结合配置中设定的目录,重新生成新的执行脚本myprc.sql
  swImp.WriteLine("rem 执行导入所有的存储过程");
  swImp.WriteLine(ls_sqlplus + " " + ls_database + " @" + ls_path + "\\myPrc.sql");
  //执行导入所有的存储过程
  swProc.WriteLine( "set define off");
  swProc.WriteLine("spool " + ls_path + "\\update_log.txt");
  while ( (ls_read = srList.ReadLine() ) != null )
  {
  swProc.WriteLine("prompt "+ls_read );
  swProc.WriteLine("@"+ls_path+"\\"+ls_read);
  }
  swProc.WriteLine("exit"); 生成脚本目的就是把还原点的程序重新导入,因此生成的结果主要是以下的内容:
  set define off  spool d:\roll\update_log.txt  prompt COMPILE_ERROR.prc  @d:\roll\COMPILE_ERROR.prc  ......  exit 核心内容生成以后,再生成一个运行的批处理即可:
  …\sqlplus.exe his3/his3@mytest @d:\roll\tabbak.sql  …\imp.exe his3/his3@mytest file=d:\roll\roll.dat full=y …\sqlplus.exe his3/his3@mytest @d:\roll\myPrc.sql  del d:\roll\imptmp.file 并使用Process来完成它的调度:
  Process pro = new Process();  pro.StartInfo.FileName = ls_path + "\\myImp.bat";
  pro.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
  在调度前生成临时文件imptmp.file,批处理中完成后删除此文件,这样程序只要查看文件是否存在,来探知进程是否完成。
  while(File.Exists(ls_tmpfile))
  {
  ldt_end=DateTime.Now;  lb_name.Text=(ldt_end-ldt_start).ToString();
  Application.DoEvents();
  }
  一个完整样例我们就做成了,通过建立还原点处理数据备份、程序备份,然后再到还原时,备份字典表、删除表、表导入和数据库程序的导入(如图 004):
 更进一步
  还原工具完成了我们单次的备份与还原,实际上还有一些工作可以加强深入:
  1、 可以利用数据库时间作为还原点标记建立目录,方便建立多个还原点;
  2、 还原时,建立相应的日志,标记还原时间点,便于查看还原状况;
  3、 如果数据库中的程序如包、函数等可以查看最后的时间戳,查看是否在建立还原点之后发生变化,还原时只要还原时间戳有变化的程序,提高还原效率。
  由于我们是以还原点为概念,因此可以忽略升级过程中对数据处理的记录。因为要把升级过程的变化记录下来再进行逆操作,实际上是非常困难的。因此 在解决实际问题时,有时候找到合适的突破点,理解处理问题的核心要素非常重要,这样就有助于事半功倍的效果。