增量备份与恢复
增量在线备份可以在正常数据库活动不间断进行的情况下执行。虽然库级备份是推荐的实现方法,但SmartEDB运行时导出低级api,以允许应用程序提供自定义备份实现。
库级备份
库级别是将数据库备份过程集成到 C 应用程序中的便捷方式。此方法假定备份是备份到文件系统。备份和恢复功能由 mcobackup
库提供。有关备份过程如何执行的详细说明,
请参阅增量备份实现页面以及SDK 示例 /samples/core/18-backup_online 以获取实现细节。
为了执行完整备份或增量备份,应用程序必须首先为备份文件定义文件名,并为备份“标签”定义标签,然后在传递给 mco_db_open_dev()
的 mco_db_params_
t 结构中设置适当的参数。例如:
char * filename = "MyDb.bak";
char * label_full = "Full";
char * label_inc1 = "Inc1";
char * label_inc2 = "Inc2";
db_params.backup_map_size = 4 * DATABASE_SEGMENT_SIZE / DISK_PAGE_SIZE / 16;
db_params.backup_max_passes = 10;
db_params.backup_min_pages = 10;
sprintf(db_params.backup_map_filename, "%s","Backup_map");
db_params.mode_mask = MCO_DB_INCREMENTAL_BACKUP;
rc = mco_db_open_dev(db_name, MyDb_get_dictionary(), &dev, 1, &db_params );
备份映射用于跟踪备份算法处理过的数据库页面。对于持久性数据库,当调用 mco_db_close()
API 时,备份映射会被写入文件。如果未指定 backup_map_filename
,则使用默认名称 <数据库名称>.bm
。
备份是一个由两个条件 backup_max_passes
和 backup_min_pages
控制的迭代过程。
- backup_map_size:备份计数器数组的大小(以字节为单位)。它一定是2的幂。该参数将被忽略并自动计算
disk_max_database_size
已定义(非零) - backup_min_pages:执行增量备份所需的修改页面的最小数量。当剩余页面数小于backup_min_pages时,备份进程独占地锁定数据库并完成备份。如果修改的页面数量很少,则将它们写出来并完成备份所需的时间非常短。(请注意,值为0将禁用备份。)
- backup_max_passes:与backup_min_pages无关,该参数表示执行增量备份之前的最大循环数。
单个备份文件可以包含多个独立的备份,这些备份可能是不同类型的(完整备份或增量备份),或者带有不同的标签或时间戳。每个备份都由一个头和尾“框定”为备份记录。
要创建备份文件,应用程序调用 mco_backup_create()
函数指定file_name
、label
以及备份类型 MCO_BACKUP_TYPE_SNAPSHOT
。这会创建初始备份记录。然后,通过调用 mco_backup_create()
并指定备份类型 MCO_BACKUP_TYPE_INCREMENTAL
,可以依次添加后续的增量备份记录。或者,可以指定备份类型 MCO_BACKUP_TYPE_AUTO
,这将根据备份文件的内容自动选择创建快照记录还是增量记录;如果文件中尚未有快照记录,则备份过程会创建一个;否则,将创建一个部分(增量)备份记录。
恢复或验证备份
一旦创建,备份文件就可以通过调用带有适当标签的 mco_backup_restore()
API 从特定的备份记录中恢复数据库。要查看备份文件中的记录,请调用 mco_backup_list()
API,它会显示备份记录的name
,type
,label
和其他属性。有时可能需要在不执行恢复的情况下,通过调用 mco_backup_verify()
函数检查备份文件的一致性。
以下 C 应用程序代码片段展示了如何使用这些 API。首先,备份数据库:
{
mco_db_h con;
MCO_RET rc;
char * filename = “backup.bak”;
char * label = “a label”;
/* 数据库连接 */
rc = mco_db_connect( db_name, &con );
/* 备份数据库 */
rc = mco_backup_create( con, filename, label,
MCO_BACKUP_TYPE_AUTO, 1, 0, 0);
}
然后从备份文件中恢复数据库:
{
mco_db_h con;
MCO_RET rc;
char * filename = “backup.bak”;
char * label = “a label”;
/* 数据库连接 */
rc = mco_db_connect( db_name, &con );
/* 恢复数据库 */
rc = mco_backup_restore( con, filename, label, 0, 0);
}
库级API参考
有关库级 API 的详细说明和示例,请参阅增量备份 C API页面。
低级C API备份功能
可以通过自定义回调函数实现覆盖SmartEDB备份服务。此外,还可以通过内部运行时配置文件target/mcolib/mcocfg.h
来打开/关闭服务。默认情况下,备份服务处于开启状态,导出的备份相关接口如下:
typedef MCO_RET (*mco_backup_info_proc_t)(
uint4 phase,
const mco_backup_info_t * info,
void * param
);
typedef MCO_RET (*mco_backup_chunk_proc_t)(
uint4 chunk_id,
const char * mem,
uint4 mem_sz,
void * param
);
typedef MCO_RET (*mco_restore_chunk_proc_t)(
uint4* chunk_id,
char ** mem,
uint4 * mem_sz,
void * param
);
内部的 SmartEDB 运行时函数 mco_backup()
会调用 mco_backup_info_proc_t()
和 mco_backup_chunk_proc_t()
回调函数。mco_backup_info_proc_t()
用于存储界定备份记录的信息性标题,而 mco_backup_chunk_proc_t()
则用于存储数据块。同样地,内部的 SmartEDB 运行时函数 mco_restore()
会调用 mco_restore_chunk_proc_t()
回调函数,以从备份存储中恢复数据块。这些回调 API 是由系统内部调用的,应用程序不能直接调用它们。
此核心级增量备份和恢复 API 不限制应用程序使用文件系统或任何其他特定类型的存储方式。可以根据需要选择最适合的存储方法。在内部,运行时函数 mco_backup()
和 mco_restore()
确保了数据块的完整性。此外,您还可以选择对数据进行加密或压缩,以增强备份的安全性和效率。
还原过程要求数据库具有相同的属性(如大小、内存页大小等),并用从备份记录中读取的数据页覆盖现有内容。与库级备份类似,要成功还原数据库,必须至少有一个快照(完整备份记录),以及任意数量的增量记录。
以下代码片段展示了这些回调 API 的一种可能的简单实现方式:
/* 备份回调函数 */
MCO_RET mco_backup_info(uint4 phase, const mco_backup_info_t* info, void* param)
{
mco_backup_ctx_t *ctx = (mco_backup_ctx_t *)param;
mco_backup_info_t hdr = *info;
switch (phase)
{
case MCO_BACKUP_PHASE_1:
…
/* write backup header */
if (fwrite(&hdr, sizeof(hdr), 1, ctx->f) != 1)
{
return MCO_E_DISK_WRITE;
}
...
break;
case MCO_BACKUP_PHASE_2:
...
/* 备份页脚 */
if (fwrite(&hdr, sizeof(hdr), 1, ctx->f) != 1)
{
return MCO_E_DISK_WRITE;
}
...
break;
default:
return MCO_E_ILLEGAL_PARAM;
}
return MCO_S_OK;
}
MCO_RET mco_backup_chunk(uint4 chunk_id, const char* chunk, uint4 chunk_size,
void* param )
{
mco_backup_ctx_t *ctx = (mco_backup_ctx_t *)param;
...
/* 写数据块 */
if (fwrite( chunk, chunk_size, 1, ctx->f) != 1)
{
return MCO_E_DISK_WRITE;
}
...
return MCO_S_OK;
}
MCO_RET mco_backup_create(mco_db_h con, char const* file_name, char const* label,
mco_backup_type type, char* err_buf, unsigned int * err_buf_sz)
{
MCO_RET rc;
mco_backup_ctx_t ctx;
...
/* 打开一个备份文件 */
ctx.f = fopen(file_name, "rb+”);
...
/* 运行备份程序并传入两个回调函数 */
rc = mco_backup(con, label, type, mco_backup_info, mco_backup_chunk, &ctx);
...
/* 关闭备份文件 */
fclose(ctx.f);
...
return rc;
}
MCO_RET mco_restore_chunk(uint4* chunk_id, char** pchunk, uint4* pchunk_size, void * param)
{
mco_backup_ctx_t *ctx = (mco_backup_ctx_t *) param;
...
/* 将数据块读入缓冲区,并返回读取的大小和内容。 */
...
if ( ((*pchunk_size)=fread( ((*pchunk)=buf), size, 1, ctx->f)) != 1)
{
return MCO_E_DISK_READ;
}
...
return MCO_S_OK;
}
MCO_RET mco_backup_restore(mco_db_h con, char const* file_name, char const* label,
char const* cipher, char* err_buf, unsigned int * err_buf_sz)
{
MCO_RET rc;
mco_backup_ctx_t ctx;
mco_backup_info_t info;
...
/* 打开一个备份文件 */
ctx.f = fopen(file_name, "rb+”);
...
/* 读取备份的页眉和页脚 */
fread( &info, sizeof(info), ctx.f );
...
/* 从传入的分块读取回调函数和备份信息(头和尾)应用备份文件 */
rc = mco_restore(con, &info, mco_restore_chunk, &ctx);
...
/* 关闭备份文件 */
fclose(ctx.f);
...
return rc;
}