了解 TPS 组件

使用内核事务管理器 (KTM) 和通用日志文件系统 (CLFS) 的任何事务处理系统 (TPS) 都应包含以下重要组件:

  • 事务 管理器 (KTM)

    KTM 跟踪每个事务的状态,并在系统崩溃后协调恢复操作。

  • 一个或多个 资源管理器

    你提供的资源管理器管理与每个事务关联的数据。

  • 一个或多个 CLFS 日志流

    事务管理器和资源管理器使用 CLFS 日志流来记录可用于提交、回滚或恢复事务的信息。

  • 一个或多个 事务客户端

    通常,TPS 的每个事务客户端都可以创建一个事务,对事务上下文中的数据执行操作,然后为事务启动提交或回滚操作。

本主题介绍一个包含一个资源管理器的 简单 TPS 、包含 多个资源管理器的更复杂的 TPS 以及 一些其他 TPS 方案

使用 KTM 部分提供了有关如何使用 KTM 创建 TPS 组件的详细信息。

简单 TPS

简单的 TPS 可能包含 KTM、一个资源管理器和 CLFS。 事务客户端可以通过资源管理器提供的接口与资源管理器通信。

例如,假设要创建数据库管理系统。 希望系统的客户端通过打开数据库对象的句柄、对对象执行读取和写入操作,然后关闭对象句柄来访问数据库。

现在假设你希望一组读取和写入操作以原子方式进行,以便系统的其他用户只能看到最终结果。 可以通过设计一个 TPS 来实现该目标,使客户端能够将数据库操作集绑定到事务。

系统应包含一个资源管理器,用于管理数据库中的数据,以响应来自客户端的读取和写入请求。 此资源管理器可以导出应用程序编程接口 (API) ,使客户端能够将事务与一组读取和写入操作相关联。

加载资源管理器时,它必须通过调用 ZwCreateTransactionManagerZwCreateResourceManager 向 KTM 注册自己。 然后,资源管理器可以参与事务。

你可能希望资源管理器支持一组函数,使客户端能够创建数据对象、读取和写入与数据对象关联的数据以及关闭数据对象。 以下伪代码显示了来自客户端的示例代码序列。

CreateDataObject (IN TransactionID, OUT DataHandle);
ReadData (IN DataHandle, OUT Data);
WriteData (IN DataHandle, IN Data);
WriteData (IN DataHandle, IN Data);
WriteData (IN DataHandle, IN Data);
CloseDataObject (IN DataHandle);

在客户端可以调用资源管理器的 CreateDataObject 例程之前,客户端必须通过调用 KTM 的 ZwCreateTransaction 例程创建事务对象,并通过调用 ZwQueryInformationTransaction 获取事务对象的标识符。

当客户端调用资源管理器的 CreateDataObject 例程时,客户端会将事务对象的标识符传递给资源管理器。 资源管理器可以调用 ZwOpenTransaction 来获取事务对象的句柄,然后可以调用 ZwCreateEnlistment 来注册其在事务中的参与。

此时,客户端可以开始对数据对象执行操作。 由于客户端在创建数据对象时提供了事务标识符,因此资源管理器可以将所有读取和写入操作分配给该事务。

资源管理器必须记录客户端指定的所有数据操作结果,而不使结果永久化。 通常,资源管理器使用 CLFS 在事务日志流中记录操作结果。

当客户端完成调用资源管理器以执行事务操作时,它会调用 KTM 的 ZwCommitTransaction 例程。 此时,KTM 会通知 资源管理器,它应使操作永久化。 然后,资源管理器将操作结果从日志流移动到数据的永久存储介质。 最后,资源管理器调用 ZwCommitComplete 以通知 KTM 提交操作已完成。

如果资源管理器报告客户端对 ReadDataWriteData 的调用之一出错,会发生什么情况? 客户端可以调用 ZwRollbackTransaction 来回滚事务。 由于该调用,KTM 会通知资源管理器,它应将数据还原到原始状态。 然后,客户端可以为相同的操作创建新事务,也可以选择不继续。

以下伪代码显示了客户端事务操作的更详细序列的示例。

    ZwCreateTransaction (&TransactionHandle, ...);
    ZwQueryInformationTransaction (TransactionHandle, ...);
    CreateDataObject (TransactionID, &DataHandle);
    Status = ReadData (DataHandle, &Data1);
    if (Status == Error) goto ErrorRollback;
    Status = WriteData (DataHandle, Data2);
    if (Status == Error) goto ErrorRollback;
    Status = WriteData (DataHandle, Data3);
    if (Status == Error) goto ErrorRollback;
    Status = WriteData (DataHandle, Data4);
    if (Status == Error) goto ErrorRollback;
    ZwCommitTransaction (TransactionHandle, ...);
    goto Leave;
ErrorRollback:
    ZwRollbackTransaction (TransactionHandle, ...);
Leave:
    ZwClose (TransactionHandle);
    return;

如果在创建事务后但在提交或回滚之前系统崩溃,会发生什么情况? 每次加载资源管理器时,它都应调用 ZwRecoverTransactionManagerZwRecoverResourceManager。 调用 ZwRecoverTransactionManager 会导致 KTM 打开其日志流并读取事务历史记录。 调用 ZwRecoverResourceManager 会使 KTM 在崩溃之前通知资源管理器任何正在进行的已登记事务,以及因此资源管理器必须恢复的事务。

如果事务客户端在崩溃前为事务调用 了 ZwCommitTransaction ,并开始处理事务的提交操作,则资源管理器必须能够将事务的状态还原到崩溃前的点。 如果客户端在崩溃前尚未准备好提交事务,则资源管理器可以放弃数据并回滚事务。

有关如何编写事务客户端的详细信息,请参阅 创建事务性客户端

有关如何编写资源管理器的详细信息,请参阅创建资源管理器

TPS 中的多个资源管理器

现在假设 TPS 允许客户端修改单个事务中两个不同数据库中的信息,以便仅当两个数据库的修改成功时,事务才会成功。

在这种情况下,TPS 可以有两个资源管理器,每个数据库一个。 每个资源管理器都可以导出客户端可用于访问资源管理器数据库的 API。

以下伪代码演示了客户端如何创建包含两个资源管理器支持的两个数据库上的操作的单个事务。

在此示例中,客户端从第一个数据库读取数据并将其写入第二个数据库。 然后,客户端从第二个数据库读取数据并将其写入第一个数据库。 (第一个资源管理器导出以 Rm1 开头的函数,第二个资源管理器导出以 Rm2.)

    ZwCreateTransaction (&TransactionHandle, ...);
    ZwQueryInformationTransaction (TransactionHandle, ...);
    Rm1CreateDataObject (TransactionID, &Rm1DataHandle);
    Rm2CreateDataObject (TransactionID, &Rm2DataHandle);
    Status = Rm1ReadData (Rm1DataHandle, &Rm1Data);
    if (Status == Error) goto ErrorRollback;
    Status = Rm2WriteData (Rm2DataHandle, Rm1Data);
    if (Status == Error) goto ErrorRollback;
    Status = Rm2ReadData (Rm2DataHandle, &Rm2Data);
    if (Status == Error) goto ErrorRollback;
    Status = Rm1WriteData (Rm1DataHandle, Rm2Data);
    if (Status == Error) goto ErrorRollback;
    ZwCommitTransaction (TransactionHandle, ...);
    goto Leave;
ErrorRollback:
    ZwRollbackTransaction (TransactionHandle, ...);
Leave:
    ZwClose (TransactionHandle);
    return;

由于客户端将相同的事务标识符传递给两个资源管理器,因此两个资源管理器都可以调用 ZwOpenTransactionZwCreateEnlistment 以在事务中登记。 当客户端最终调用 ZwCommitTransaction 时,KTM 会通知 每个资源管理器,管理器应使操作永久化,并且每个资源管理器在完成后调用 ZwCommitComplete

其他 TPS 方案

KTM 支持其他 TPS 方案。 例如,以下方案描述了 TPS 可能包含的组件:

  • 一个管理多个数据库的资源管理器。

    资源管理器的 API 可以让客户端一次打开和访问多个数据库,并且客户端可以在单个事务中合并对多个数据库的访问。

  • 一个具有客户端调用的 API 的资源管理器,以及具有第一个资源管理器调用的 API 的其他资源管理器。

    客户端仅与第一个资源管理器通信。 当资源管理器处理来自客户端的请求时,它可以根据需要访问其他资源管理器来处理客户端的请求。 例如,资源管理器管理客户端可访问的数据库,该数据库需要从客户端不可用的第二个资源管理器执行备份或数据验证操作。

  • 不使用 KTM 的现有客户端和资源管理器,与使用 KTM 的其他一组资源管理器集成。

    在这种情况下,通常需要修改现有资源管理器,使其成为与 KTM 通信的 上级事务管理器