分步提交
某些应用程序需要对事务提交处理进行更精细的控制;具体来说,分两步(阶段)提交事务。第一步将数据写入数据库,将新数据插入索引并检查索引限制(唯一性)(统称为“预提交”),然后将控制权交还给应用程序。第二步完成提交。
此类应用的一个实例是,多个 SmartEDB 数据库需要在单一事务内同步所执行的更新操作。另一个实例可能是 SmartEDB 事务提交包含在涉及其他数据库系统或外部存储的全局事务中。在这种情况下,应用程序会在第一阶段和第二阶段之间协调 SmartEDB 事务与全局事务。
两阶段提交的基本特征在于,只有在未检测到错误条件的情况下,提交的第二阶段才会成功。以下示例代码演示了一个多任务应用程序如何在向单个数据库插入记录时处理重复键的情况,即在检测到错误时回滚事务:
char * dbName1 = "ph2_1";
char * dbName2 = "ph2_2";
using namespace McoSql;
McoSqlEngine engine1, engine2;
McoSqlOpenParameters params;
// Define the structure correponding to database table T
struct _T
{
uint4 IntKey;
};
int main()
{
uint4 i, key;
int nCommitsAttempted = 0;
int nCommitsSucceded = 0;
params.databaseName = dbName1;
params.dictionary = ph2commitdb_get_dictionary();
params.mainMemoryDatabaseSize = DATABASE_SIZE;
params.mainMemoryPageSize = MEMORY_PAGE_SIZE;
engine1.open(params);
params.databaseName = dbName2;
params.flags &= ~(McoSqlOpenParameters::START_MCO_RUNTIME); // don't restart runtime
engine2.open(params);
/* fill database 1 */
printf("\n\tFill database ph2_%d ...\n", 1 );
for (i = 0; i < nRecords; i++)
{
key = i * 3 + 1;
engine1.executeStatement("insert into T (intKey) values (%u)", key);
printf("\t\tInsert into T key = %u\n", key );
}
/* fill database 2 */
printf("\n\tFill database ph2_%d ...\n", 2 );
for (i = 0; i < nRecords; i++)
{
key = i * 3 + 2;
engine2.executeStatement("insert into T (intKey) values (%u)", key);
printf("\t\tInsert into T key = %u\n", key );
}
printf("\n\n\tBegin 2-phase commits for key values %d to %d ...\n\n", 0,
nRecords*3-1 );
for (i = 0; i < nRecords * 3; ++i)
{
Transaction* trans1 = engine1.database()->beginTransaction(Transaction::ReadWrite);
Transaction* trans2 = engine2.database()->beginTransaction(Transaction::ReadWrite);
key = i;
engine1.executeStatement(trans1, "INSERT INTO T (intKey) VALUES(%u)", key);
engine2.executeStatement(trans2, "INSERT INTO T (intKey) VALUES(%u)", key);
printf("\t key = %u\n", key );
try
{
nCommitsAttempted++;
printf("\t\tCommit phase 1 to ph2_1 for key = %u\n", key );
trans1->commit(1);
nCommitsSucceded++;
nCommitsAttempted++;
printf("\t\tCommit phase 1 to ph2_2 for key = %u\n", key );
trans2->commit(1);
nCommitsSucceded++;
// Both phase-1 commits succeded, Commit phase 2 of both transactions
nCommitsAttempted++;
printf("\t\tCommit phase 2 to ph2_1 for key = %u\n", key );
trans1->commit(2);
nCommitsSucceded++;
nCommitsAttempted++;
printf("\t\tCommit phase 2 to ph2_2 for key = %u\n", key );
trans2->commit(2);
nCommitsSucceded++;
}
catch (NotUnique &)
{
printf("\tError committing transactions for key = %u ...\n", key );
trans1->rollback();
trans2->rollback();
}
// Release the transactions
trans1->release();
trans2->release();
}
printf("\t%d 2-phase commits attempted, %d succeded\n\n\tFinal results:\n",
nCommitsAttempted, nCommitsSucceded );
showRecords( 1 );
showRecords( 2 );
engine2.close();
engine1.close();
sample_pause_end("\n\nPress any key to continue . . . ");
return 0;
}