备份和还原功能是确保任何数据库管理系统的数据可恢复性的关键。 应用程序可能崩溃,磁盘可能发生故障,并且用户经常会犯一些危及数据的错误。 由于这些原因,至关重要的是,您必须了解备份和恢复功能如何用于数据库管理系统,并且必须实施计划周密的备份策略。
注: IBM Cloudscape是开源Apache Derby关系数据库的商业版本。 当本文提到“ Derby”时,它是指Cloudscape或Apache Derby。
Derby提供两种类型的还原机制:
- 第一个使用数据库的完整备份将数据库恢复到进行备份时的状态。
- 第二种使用完全备份加上前滚事务日志(称为前滚恢复)的组合来还原到最新状态。
可以使用系统过程在线执行数据库备份,也可以使用操作系统复制命令离线执行数据库备份。 可通过连接URL属性进行还原操作。
您可以从ij (Derby命令行工具)或Java™数据库连接(JDBC)应用程序启动备份或还原。 嵌入式和网络服务器配置均支持备份和还原操作。 如果要从网络服务器客户端执行备份/还原,请确保可以从网络服务器计算机访问指定的备份位置。
在线备份
当数据库系统正在使用SYSCS_UTIL.SYSCS_BACKUP_DATABASE(IN BACKUPDIR VARCHAR(32762))
过程运行时,将执行联机备份。 执行备份过程需要与要备份的数据库建立连接。
在Derby中,在进行备份时,将阻止执行需要写入磁盘的语句(如插入,删除和更新)。 仅要求从磁盘读取的语句(例如select)将不受阻碍地继续进行。 一旦备份完成,系统将让所有被阻止的语句完成其执行。 联机备份可能会降低系统吞吐量和用户响应时间,因此,如果更新活动较少,则可以计划备份。
清单1:通过IJ执行salesdb数据库的备份
ij>connect 'jdbc:derby:salesdb';
ij>CALL SYSCS_UTIL.SYSCS_BACKUP_DATABASE('D:/dbbackups/');
嵌入式应用程序可以使用JDBC API执行备份。 应用程序客户端不需要管理备份。 在Derby中,所有系统过程也可以通过JDBC API调用。
清单2:使用JDBC调用执行备份
private void backUpDatabase(Connection conn) throws SQLException
{
String sqlstmt = "CALL SYSCS_UTIL.SYSCS_BACKUP_DATABASE(?)";
CallableStatement cs = conn.prepareCall(sqlstmt);
cs.setString(1,"D:/dbbackups/");
cs.execute();
cs.close();
}
备份过程将在指定的备份位置创建一个与数据库名称相同的目录。 该目录包含数据库目录中的所有文件/目录。 数据库目录通常具有一个数据目录( seg0 ),用于存储表和索引的所有数据文件;一个事务日志目录( log ),用于存储所有事务日志文件;以及一个service.properties文件,其中包含用于引导数据库的信息。数据库。
离线备份
当系统脱机时,可以使用操作系统命令复制整个数据库目录。 如果事务日志目录与数据库目录不同,请确保也复制了事务日志目录。 在还原操作期间,必须使用事务日志才能使数据库进入一致状态。
通过在复制数据库目录时冻结系统(阻止所有写操作),还可以在不使数据库系统脱机的情况下执行脱机备份。 这样做的系统过程是SYSCS_UTIL.SYSCS_FREEZE_DATABASE()
。 复制完成后,使用SYSCS_UTIL.SYSCS_UNFREEZE_DATABASE()
过程取消冻结(允许写操作)系统; 此过程调用会将系统设置回正常状态。
从备份还原
数据库的联机/脱机备份映像用于将数据库还原到获取备份映像时所处的状态。 通过在与数据库的第一个连接上指定连接URL属性restoreFrom=<backup path>
,可以从备份中还原数据库。 请注意,备份路径必须在备份中包括数据库名称,而不仅仅是备份位置。
如果未在连接URL上指定路径,则数据库将还原到连接URL上指定的位置或当前derby.system.home
位置。 如果当前数据库位置上存在同名数据库,则将其首先删除,然后从备份中还原。
清单3:从备份还原salesdb数据库:
String dbURL = "jdbc:derby:salesdb;restoreFrom=D:/dbbackups/salesdb";
Connection conn = DriverManager.getConnection(dbURL);
默认情况下,事务日志将被复制回拍摄备份映像时所在的位置。 如果具有事务日志的磁盘出现问题,则可以在还原期间更改事务日志的位置。 为此,请指定连接URL属性logDevice=<log dir>
。
清单4:从备份还原salesdb数据库并将事务日志放置在其他位置:
String dbURL = "jdbc:derby:salesdb;restoreFrom=D:/dbbackups/saleddb;logDevice=E:/salesdbLog";
Connection conn = DriverManager.getConnection(dbURL);
移动或克隆数据库
在Derby中,将数据库从一个系统迁移到另一个系统,克隆数据库以进行测试以及对数据库进行增强是容易的任务。 可以通过在与数据库的第一个连接上使用连接URL属性createFrom=<backup path>
从备份中重新创建数据库。 如果未在连接URL上指定路径,则将在连接URL上指定的位置或当前derby.system.home
位置上创建数据库。 默认情况下,事务日志存储在数据库目录下,可以通过在连接URL上指定logDevice=<log dir >
属性来更改事务日志的位置。
清单5:使用备份映像克隆salesdb数据库
String dbURL = "jdbc:derby:salesdb;createFrom=D:/dbbackups/salesdb;logDevice=E:/salesdbLog";
Connection conn = DriverManager.getConnection(dbURL);
使用Derby,可以在支持Java的任何操作系统或硬件上通过备份重新创建数据库。 新系统不必与进行备份的数据库具有相同的配置。
前滚恢复
仅使用在线/离线备份映像,就可以将数据库还原到进行备份时的状态。 但是,使用前滚恢复功能可以将数据库还原到最新状态。 前滚恢复首先从备份进行还原,然后在创建备份映像后应用所有事务日志记录,这使数据库进入其最新状态。 执行前滚恢复所需的信息是:
- 完整的在线备用图片
- 备份后的所有事务日志文件
默认情况下,只有系统保留的事务日志文件才是从电源故障或应用程序崩溃中恢复所需的文件,在成功执行检查点操作后,所有其他事务日志文件都将被删除。 为了选择执行前滚恢复,需要将备份后生成的所有事务日志文件存档。 您可以通过在备份中启用日志归档模式来做到这一点。 为此的系统过程是:
SYSCS_UTIL.SYSCS_BACKUP_DATABASE_AND_ENABLE_LOG_ARCHIVE_MODE(IN BACKUPDIR VARCHAR(32762), IN SMALLINT DELETE_ARCHIVED_LOG_FILES)
此过程将执行备份,启用事务日志归档模式,并且如果将非零值作为参数传递给DELETE_ARCHIVED_LOG_FILES参数,它还将删除已归档以从以前的备份还原的事务日志文件。
所有已归档的事务日志文件也都存储在数据库事务日志目录中。 当前,Derby不支持将存档的事务日志发送到其他位置。 重要的是要确保使用某种类似容错机制的镜像来很好地保护包含事务日志目录的磁盘。
清单6:执行备份,启用事务日志归档模式,并在此备份之前保留已归档的事务日志
public void backupAndEnableLogArchiveMode(Connection conn) throws SQLException
{
String sqlstmt = "CALL SYSCS_UTIL.SYSCS_BACKUP_DATABASE_AND_ENABLE_LOG_ARCHIVE_MODE(?, ?)";
CallableStatement cs = conn.prepareCall(sqlstmt);
cs.setString(1, "D:/dbbackups/");
cs.setInt(2, 0);
cs.execute();
cs.close();
}
清单7:执行备份,启用事务日志归档模式,并在此备份之前删除已归档的事务日志
public void backupAndEnableLogArchiveMode(Connection conn) throws SQLException
{
String sqlstmt = "CALL SYSCS_UTIL.SYSCS_BACKUP_DATABASE_AND_ENABLE_LOG_ARCHIVE_MODE(?, ?)";
CallableStatement cs = conn.prepareCall(sqlstmt);
cs.setString(1, "D:/dbbackups/");
cs.setInt(2, 1);
cs.execute();
cs.close();
}
使用前滚恢复还原数据库
通过在到数据库的第一个连接上指定连接URL属性rollForwardRecoveryFrom=<backup path>
,可以通过前滚恢复从备份中还原数据库。 请注意,备份路径必须在备份中包括数据库名称,而不仅仅是备份位置。
如果未在连接URL上指定路径,则数据库将还原到连接URL上指定的位置或当前derby.system.home
位置。 默认情况下,前滚恢复假定此位置是数据库以前存在的位置。 将数据库还原到新位置时,请使用logDevice=<log dir>
属性指定事务日志位置,该位置应与之前存储事务日志的位置相同。
清单8:通过前滚恢复从备份还原salesdb :
String dbURL = "jdbc:derby:salesdb;rollForwardRecoveryFrom=D:/dbbackups/salesdb";
Connection conn = DriverManager.getConnection(dbURL);
禁用事务日志归档模式
使事务日志归档模式具有在发生故障的情况下执行前滚恢复的选项非常好,但是,如果系统用完了事务日志存储空间,则最好暂时禁用事务日志归档模式,而不是让系统退出离线。 禁用事务日志归档的系统过程是SYSCS_UTIL.SYSCS_DISABLE_LOG_ARCHIVE_MODE(IN SMALLINT DELETE_ARCHIVED_LOG_FILES )
。
启用事务日志归档模式的一个副作用是,所有数据更改(插入)都将被记录,而默认情况下,在以下两种情况下不记录它们:
- 在包含数据的表上创建索引
- 使用导入过程将外部数据导入到空表中
如果在上述情况下性能很重要,则最好在执行期间禁用事务日志归档模式。
清单9:禁用事务日志归档模式,并删除所有已归档的事务日志文件
public void disableLogArchiveMode(Connection conn) throws SQLException
{
String sqlstmt = "CALL SYSCS_UTIL.SYSCS_DISABLE_LOG_ARCHIVE_MODE(?)";
CallableStatement cs = conn.prepareCall(sqlstmt);
cs.setInt(1, 1);
cs.execute();
cs.close();
}
检查事务日志归档模式是否打开
您可以通过检查数据库属性derby.storage.logArchiveMode来确定是否为数据库启用了事务日志归档模式。 如果启用了事务日志归档模式,那么此属性将设置为true 。
清单10:检查事务日志归档模式状态吗?
private boolean isLogArchiveModeOn(Connection conn) throws SQLException
{
Statement stmt = conn.createStatement();
String sqlstmt =
"VALUES SYSCS_UTIL.SYSCS_GET_DATABASE_PROPERTY('derby.storage.logArchiveMode')";
ResultSet rs = stmt.executeQuery(sqlstmt);
rs.next();
boolean logArchiveStatus = rs.getBoolean(1);
rs.close();
stmt.close();
return logArchiveStatus;
}
备份排程器
以临时方式拍摄备份映像适用于数据更改频率较低的应用程序。 如果您要更频繁地更改数据库,则最好采用某种自动机制来定期安排备份时间。 如果应用程序已经执行了定时任务,则可以将备份添加到现有列表中。 否则,重要的是要实现一些机制,该机制将定期安排数据库备份。 以下是示例代码,您可以在应用程序代码中使用该代码每天安排一次备份:
清单11:BackupScheduler.java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.CallableStatement;
import java.sql.SQLException;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Calendar;
import java.util.Date;
import java.text.SimpleDateFormat;
/*
* This class implements a backup scheduler to schedule database backups once a
* day at a specified time after a day it is started.
*
* Example usage in an application:
* BackupScheduler backups = new BackupScheduler("jdbc:derby:salesdb", "D:/backups");
*
* backups.start(23) //run database backups at 11PM everyday.
*
* @see java.util.Timer
* @see java.util.TimerTask
*/
public class BackupScheduler extends TimerTask
{
private final static long BACKUP_INTERVAL = 1000*60*60*24; //once a day
private String dbURL;
private String backupPath;
private Timer timer;
/* Constructor for Backup Scheduler
* @parm dbURL connection URL that should be used to connect to the database.
* @param backUpPath Location where the backup should be placed.
*/
public BackupScheduler(String dbURL, String backupPath) {
this.dbURL = dbURL;
this.backupPath = backupPath;
}
/**
* start the backup scheduler.
* @param backupTime the time of the date at which to start the backup
* in 24-hour clock notation.(eg: 1PM 13))
*/
public void start(int backupTime)
{
timer = new Timer();
//schedule a backup task everyday at the specified time, starting tomorrow.
timer.scheduleAtFixedRate(this,
getTomorrowTime(backupTime),
BACKUP_INTERVAL);
}
/**
* Implements TimerTask's run method to perform backups.
*/
public void run(){
try{
new org.apache.derby.jdbc.EmbeddedDriver();
Connection conn = DriverManager.getConnection(dbURL);
backupDatabase(conn);
conn.close();
}catch(Exception ex)
{
System.out.println("backup failed on:" + dbURL);
ex.printStackTrace();
}
}
/**
* Performs back up of the database
* @param conn Connnection to the database that is to be backed up.
*/
private void backupDatabase(Connection conn) throws SQLException
{
SimpleDateFormat dateFormat = new SimpleDateFormat("MM.dd.yy");
String backupDirectory = backupPath + "/" + dateFormat.format(new Date());
String sqlstmt = "CALL SYSCS_UTIL.SYSCS_BACKUP_DATABASE(?)";
CallableStatement cs = conn.prepareCall(sqlstmt);
cs.setString(1, backupDirectory);
cs.execute();
cs.close();
}
/**
* Gets the tomorrows Calenders time at the speficied hour.
* @param hourOfDay hour the time to be calculated.
* @return returns tomorrow time.
*/
private Date getTomorrowTime(int hourOfDay){
Calendar tomorrow = Calendar.getInstance();
tomorrow.roll(Calendar.DATE, true);
tomorrow.set(Calendar.HOUR_OF_DAY, hourOfDay);
tomorrow.set(Calendar.MINUTE, 0);
tomorrow.set(Calendar.SECOND , 0);
tomorrow.set(Calendar.MILLISECOND, 0);
return tomorrow.getTime();
}
}
结论
总之,IBM Cloudscape / Apache Derby使执行备份和还原功能非常容易。 通过使用以下任一配置来设置系统,可以根据资源和应用程序在不同程度上保护数据库免受磁盘故障的影响:
- 使用此配置数据库,可以定期将计划的联机/脱机备份恢复到上一次成功备份的状态。
- 定期启用事务日志归档模式的联机备份和事务日志存储在容错磁盘上。 使用此配置,可以使用前滚恢复将数据库还原到最新状态。
- 硬件/软件容错技术,例如镜像,以保护存储数据库和事务日志信息的磁盘。 在这种情况下,由于磁盘受到了很好的保护,因此无需备份/还原。
翻译自: https://www.ibm.com/developerworks/data/library/techarticle/dm-0502thalamati/index.html