TL 应用
为了使用 SmartEDB 事务日志记录,C/C++ 应用程序必须在通过 mco_db_open_dev()
打开数据库之前,先通过调用 mco_translog_init()
初始化 TL 子系统。Java 和 C# 应用程序中,当实例化数据库时,数据库构造函数会初始化 TL 子系统。
在事务日志初始化之后,C/C++ 应用程序像在任何 SmartEDB 应用程序中那样通过调用 mco_db_connect()
或 mco_db_connect_ctx()
与数据库建立连接,而 Java 和 C# 应用程序只需实例化一个连接即可。
此时,应在开始日志记录之前通过调用 mco_db_save()
C/C++ 函数,或在 Java 和 C# 应用程序中为 Connection.SaveSnapshot()
方法创建一个保存点。这为后续所有已记录的事务确立了起始点。
Java 和 C# 应用程序可以从保存的映像和日志文件中恢复数据库,如以下从 \samples\csharp\tl\TLogBasicCS 中的代码片段所示:
const string TL_LOG_FILE = "tlogbasic_tl_cs.log";
const String DBIMAGE_FILE = "tlogbasicdb_cs.bak";
Database.Parameters parameters = new Database.Parameters();
if (File.Exists(DBIMAGE_FILE))
{
// set database image filename
parameters.DatabaseSnapshotFilePath = DBIMAGE_FILE;
loadedSnapshot = true;
}
// create Database object
db = new Database(new ExtremedbWrapper(),
Database.Mode.TransactionLoggingSupport);
db.Open("tlogbasicdb", parameters, devs); // open database.
if (loadedSnapshot)
{
// Apply stored log file to databas
LogReader logr = new LogReader(con, TL_LOG_FILE);
LogReader.LogInfo linfo = new LogReader.LogInfo();
logr.Apply();
}
con.SaveSnapshot(DBIMAGE_FILE);
}
在 C/C++ 应用程序中,可以对日志文件执行以下额外的日志文件操作:
可以通过调用 mco_translog_label()
函数在日志文件的任意位置添加标签,这使得能够将日志恢复到该标签处。此外,还可以通过 mco_translog_truncate()
函数截断并重新开始日志文件。请注意,在截断日志文件之前创建数据库保存点是明智之举,否则事务将无法恢复。另外,明智的做法是将保存点创建到不同于先前保存点的新文件中。如果覆盖现有的保存点且在完成新的保存点之前系统崩溃,则无法恢复。因此,为了安全起见,应遵循以下顺序:
创建一个新的保存点;
截断日志文件;
删除之前的保存点文件。
如果事务日志记录不是同步的,则可以通过调用函数 mco_translog_flush()
在任何时候将日志文件刷新到磁盘,以强制立即刷新与日志文件相关的文件系统缓冲区。但是,在启动事务日志记录时,可以指定自动刷新策略,从而消除应用程序显式刷新日志文件的需要。
在日志记录过程中,可以通过调用函数 mco_translog_get_info()
收集有关当前日志文件的信息(当前日志大小、存储的事务数量以及其他信息)。或者,可以通过调用 mco_translog_query_info()
获取有关持久介质上特定日志文件的信息(应用或追加到该日志的能力、日志文件大小以及其他信息)。
可以通过调用函数 mco_translog_apply()
从兼容的日志文件中恢复数据库。请注意,应通过 mco_db_load()
恢复适当的保存点,即先前存储的数据库映像,或者在应用日志文件之前打开一个新的空数据库。然后,调用 mco_translog_apply()
以重放该数据库映像保存后提交的所有事务。在将日志应用到数据库之前,TL 运行时首先检查字典兼容性、运行时模式和数据大小兼容性,最后验证数据库当前的事务计数值与日志中存储的计数值是否相同。如果一切正常,mco_translog_apply()
将应用日志文件中存储的所有事务,之后调用 mco_translog_start()
开始为新的数据库会话记录事务。
数据库恢复
从保存点或空数据库作为起始点,并使用两个或多个连续的日志文件来恢复数据库的顺序如下所示:
事务日志记录
char buff[64];
mco_TL_start_data_t log_parms;
log_parms.flags = 0;
sprintf(buff, "transactions_%d.log", 0);
CHECK(mco_translog_start (db, buff, &log_parms));
log_parms.flags |= MCO_TRANSLOG_RESTART;
for (i = 1; i < NPARTS; i++)
{
/* do database activity here */
use_database();
/* under some condition, e.g. log file size, restart log with new file */
if( /* some condition */ )
{
sprintf(buff, "transactions_%d.log", i);
CHECK(mco_translog_start (db, buff, &log_parms));
}
}
CHECK(mco_translog_stop(db));
数据恢复
for (i = 0; i < NPARTS; i++)
{
char buff[64];
mco_TL_log_info_t info;
/* load next log file */
sprintf(buff, "transactions_%d.log", i);
CHECK(mco_translog_query_info(db, buff, 0, 0, &info));
ASSERT(info.transaction_apply_compat, MCO_YES);
CHECK(mco_translog_apply(db, buff, MCO_TRANSLOG_ALL_LABELS));
}
此序列将完全恢复数据库。
在正常情况下,数据库将从有序关闭期间保存的最后一个保存点映像中恢复,且没有事务日志。在异常终止的情况下,将从日志文件中恢复事务,但由于应用程序在未调用 mco_translog_stop()
的情况下终止,因此在(最后一个)日志文件的末尾没有正确的文件结束标记,mco_translog_apply()
将返回 MCO_S_TL_INVDATA
。在这种情况下,您可以调用 mco_translog_query_info()
(它也将返回 MCO_S_TL_INVDATA
),实际由 mco_translog_apply()
恢复的事务数量将返回在 info.stored_trans_count
中。 (最后恢复的事务是否为应用程序崩溃前实际执行的最后一个事务,将取决于所采用的刷新策略,如果采用的是同步日志记录策略以外的任何策略,那么还要看运气)。
应用程序完成后,通过调用 mco_translog_stop()
函数停止事务日志记录。然后,通过调用 mco_db_disconnect()
和 mco_db_close()
函数执行正常的应用程序终止操作。以下伪代码片段展示了应用程序如何执行“恢复并继续”场景。
有关完整实现,请参阅 samples/tl/tlogbasic:
mco_runtime_start();
mco_translog_init();
...
mco_db_load();
mco_db_connect();
mco_translog_query_info(&info);
if (info.stored_transactions > 0)
{
mco_translog_apply();
/* Create initial save-point */
mco_db_save();
}
mco_translog_start();
while (run) /* Normal database usage */
{
...
if (some_condition ) /* periodically create save-point */
{
mco_db_save();
/* Truncate the log file */
mco_translog_truncate();
}
}
mco_db_save(); /* create the last save-point */
mco_translog_stop();
/* delete the log file here; it’s no longer needed */
mco_db_disconnect();
技巧
在以下三种情况下使用 TL 是合理的:
当数据库仅由临时(内存中)对象组成,或者由临时对象和持久对象共同组成(即所谓的“混合”数据库),并且您需要为持久数据和临时数据都提供持久性时。
要将交易导出到外部系统(例如另一个数据库管理系统),我们将其称为“数据中继”。
为了创建一个持久化的事件队列,从而实现比通过“事件”DDL 标记以及相关的同步和异步事件处理程序所能实现的更强大的事件处理功能。
对于仅支持持久化的数据库,切勿使用事务日志(TL)来添加持久性;SmartEDB 运行时自身针对持久化数据库具备健全且高效的数据库恢复设施。务必不要将 SmartEDB 事务日志记录 API 的使用与 SmartEDB 运行时针对持久化数据库的常规日志记录相混淆。
由于将日志应用于数据库比从保存点加载数据库(通过函数 mco_db_load()
)要慢,因此您仅应在应用程序崩溃或需要将日志应用到特定标签的情况下使用 TL 函数 mco_translog_apply()
。所以应定期创建数据库保存点,然后当应用程序正常结束时,可以应用如上所示的“恢复并继续”方案。
数据定义语言要求
使用事务日志记录的数据库模式必须包含 auto_oid 声明。
declare auto_oid [ESTIMATED_NUMBER_OF_OBJECTS];
数据库是否实际使用事务日志记录是在运行时确定的。当数据定义被编译为事务日志记录时,SmartEDBDDL 编译器会在每个对象中插入一个 8 字节的唯一标识符(称为 auto_OID,不要与字段类型 autoid 混淆)。