我已成功在 Windows 上使用 SQLServer OLEDB 驱动程序执行 XA 事务。现在我已经将 C++ 应用程序移植到 Linux 上。在 Linux 上,Microsoft 提供了 SQLServer 2019 ODBC 驱动程序,从该驱动程序的 17.3 版开始,据报道支持 XA。 Microsoft 提供了以下示例来说明如何实现 xa_* 函数:
这个例子本身是有效的。在其他上下文中使用该代码不起作用。对 SQLSetConnectAttr(..., SQL_ATTR_ENLIST_IN_XA, ...) 操作 OP_START 的调用失败,我没有通过 CheckRC() 获得有用的信息。
请与我们分享您的经验和细节。
SQLSetConnectAttr(..., SQL_ATTR_ENLIST_IN_XA, ...) 函数对于 XID 非常敏感。如果 XID 有分支 ID,则分支 ID 必须从 xid_t::data 的字节 64 开始。直接在一个序列中存储像“f9707929-a367-4e3a-9a80-3fbb3a23ab11”+分支 ID“1234”这样的全局 ID,并通过 xid_t::gtrid_length 和 xid_t::bqual_length 标识字符串布局将与其他 DB API 和 IBM MQ 一起使用,但失败并显示 SQL_ATTR_ENLIST_IN_XA SQLServer。
为了获得上述示例 XID 工作,UUID 必须存储在 xid_t::data 的开头(字节 0-36),并且分支 id 必须存储在字节(64-68)处。 xid_t 字段 gtrid_length 必须设置为 36,bqual_length 设置为 4。formatID 我设置为 1。
如果 XID 布局不适合 SQL_ATTR_ENLIST_IN_XA,且操作 OP_START 失败,并且 SQLGetDiagRec() 不会报告任何内容。
默认情况下,XA 事务在隔离级别“可序列化”下运行。 Microsoft 对此隔离级别的描述如下:
交易之间完全隔离的最高级别。 SQLServer 保留对选定数据获取的读锁和写锁,以便在事务结束时释放这些锁。当 SELECT 操作使用范围 WHERE 子句时,会获取范围锁,特别是为了避免幻读。
每次调用 xa_start 时,隔离级别都设置为“可串行化”。连接后使用 SQLSetConnectAttr(..., SQL_ATTR_TXN_ISOLATION, ...) 设置隔离级别没有帮助。您必须在 SQLSetConnectAttr(..., SQL_ATTR_ENLIST_IN_XA, OP_START, ...) 之后调用此函数。
这样做允许您将隔离级别设置为 SQL_TXN_READ_COMMITTED。还将考虑数据库选项 READ_COMMITTED_SNAPSHOT。这意味着将隔离级别设置为 SQL_TXN_READ_COMMITTED 并启用数据库选项 READ_COMMITTED_SNAPSHOT 会将隔离级别切换为“快照”。
命令“DBCC useroptions”可用于查询当前会话的隔离级别。
以下查询对于检查活动事务的隔离级别和状态也很有用:
SELECT tst.session_id, [database_name] = db_name(s.database_id)
, tat.transaction_begin_time
, transaction_duration_s = datediff(s, tat.transaction_begin_time, sysdatetime())
, transaction_type = CASE tat.transaction_type WHEN 1 THEN 'Read/write transaction'
WHEN 2 THEN 'Read-only transaction'
WHEN 3 THEN 'System transaction'
WHEN 4 THEN 'Distributed transaction' END
, input_buffer = ib.event_info, tat.transaction_uow
, transaction_state = CASE tat.transaction_state
WHEN 0 THEN 'The transaction has not been completely initialized yet.'
WHEN 1 THEN 'The transaction has been initialized but has not started.'
WHEN 2 THEN 'The transaction is active - has not been committed or rolled back.'
WHEN 3 THEN 'The transaction has ended. This is used for read-only transactions.'
WHEN 4 THEN 'The commit process has been initiated on the distributed transaction.'
WHEN 5 THEN 'The transaction is in a prepared state and waiting resolution.'
WHEN 6 THEN 'The transaction has been committed.'
WHEN 7 THEN 'The transaction is being rolled back.'
WHEN 8 THEN 'The transaction has been rolled back.' END
, trn_iso_level = CASE s.transaction_isolation_level
WHEN 0 THEN 'Unspecified'
WHEN 1 THEN 'ReadUncommitted'
WHEN 2 THEN 'ReadCommitted'
WHEN 3 THEN 'RepeatableRead'
WHEN 4 THEN 'Serializable'
WHEN 5 THEN 'Snapshot' END
, transaction_name = tat.name, request_status = r.status
, tst.is_user_transaction, tst.is_local
, session_open_transaction_count = tst.open_transaction_count
, s.host_name, s.program_name, s.client_interface_name, s.login_name, s.is_user_process
FROM sys.dm_tran_active_transactions tat
INNER JOIN sys.dm_tran_session_transactions tst on tat.transaction_id = tst.transaction_id
INNER JOIN Sys.dm_exec_sessions s on s.session_id = tst.session_id
LEFT OUTER JOIN sys.dm_exec_requests r on r.session_id = s.session_id
CROSS APPLY sys.dm_exec_input_buffer(s.session_id, null) AS ib;
使用OLEDB驱动程序和ITransactionJoin接口实现SQLServer XA,直接与本地分布式事务控制器(DTC)服务进行通信。如果 SQLServer 在另一台主机上运行,则涉及本地 DTC 和 SQLServer 主机上的 DTC。 DTC 服务必须通过网络进行通信。 RPC、动态端口范围、防火墙和安全设置通常使其很难正常工作。
使用新的 ODBC SQL_ATTR_ENLIST_IN_XA 接口,不再需要 DTC 到 DTC 的通信。该应用程序仅具有与 SQLServer 数据库实例的连接,并且在 SQLServer 主机上,DTC 服务必须运行,并且必须仅在此 DTC 中设置“XA 选项”。使用 SQL_ATTR_ENLIST_IN_XA 的应用程序不需要本地 DTC。