你的Oracle 12C Transaction Guard已到账

前 言

前篇讲到TAF各种故障切换场景,

温故通道:《抬头MOS看文献,埋头机房做实验》

但是对于事务来说,发生异常宕机,应用程序无法感知事务当前状态,很容易发生多次提交等不可控情况,transaction guard事务卫士因此而生。

什么是transaction guard事务卫士?

Oracle 12c Transaction Guard是TAF的下一个发展,它涵盖了TAF的一些限制,例如使用OCI。

Transaction Guard为应用程序提供了一个通用工具,在发生计划内和计划外停机还有重复提交时用于”最多执行一次”(保持事务一致性),

应用程序使用称为逻辑事务ID(LTXID)的新概念来确定停机后数据库会话中打开的最后一个事务的结果。

如果不使用Transaction Guard,尝试在中断后重试操作的应用程序可能会通过提交重复事务来导致逻辑损坏。

简单说来:

当我们进行故障检测和故障恢复的时候,需要考虑一个问题:数据库到应用的响应是很短暂的,因此在实例发生问题时,commit的响应可能会丢失,比如程序做了dml操作,并向db发出commit提交请求。

如果此时实例故障,应用程序没有从数据库那里得到任何反馈响应,此时应用不知道事务状态,会发生什么情况?

可能会发生的2种情况!

1.数据库后台提交了修改,但前台应用程序未收到成功的数据库后台提交了修改,但前台应用程序未收到成功的反馈响应反馈响应

2.在成功提交操作之前,数据库已经出现故障了,所以应用程序必须重新运行并提交

这两种情况下,应用程序都必须知道事务的状态以避免数据库损坏。TG孕育而生,其提供了一种机制来跟踪交易状态,甚至可以在数据库出现故障之后进行跟踪。

Transaction Guard原理是什么?

TG引入逻辑标识ID(LTXID)的概念,对于数据库连接的会话来说,一个逻辑事务ID是唯一的,并且逻辑事务标识符与数据库的事务ID相映射。数据库会跟踪逻辑事务ID的结果,如果数据库发生故障,那么应用程序连接将可以使用LTXID来查询事务状态,以此确定事务是否被提交。

Transaction guard是如何工作的?

在标准的提交场景中,数据库提交一个事务并且得到一个成功的信息返回给客户端。

在上图中,客户端提交了一个commit语句,并收到一个通信失败的消息,这类错误可能有很多种原因,比如实例宕了,网络通信失败等,在这个场景下,客户端是不知道事务状态的。

通信失败后,数据库可能仍在运行提交,并且不知道客户端已断开连接。检查事务状态并不保证活动事务在检查后是否会提交,如果客户端因为此过期信息而重新发送提交,那么数据库可能会重复该事务,从而导致逻辑损坏。

Logical Transaction ID

数据库通过使用称为 logical transaction ID.的全局唯一标识符来解决通信故障,此ID包含会话第一次连接时分配的逻辑会话编号,以及每次会话提交或回滚时更新的正在运行的提交编号,从应用程序的角度来看,逻辑事务ID唯一标识了失败会话上提交的最后一个数据库事务。对于一个事务多次往返客户端服务器的提交,数据库始终只保留一个逻辑事务ID。

最多一次协议(at-most-once)通过要求数据库执行以下操作来启用对提交结果的访问:

1 维护同意重试的保留期的逻辑交易ID

2 在提交时保留逻辑事务ID

在应用程序可以确定最后一个事务发生可恢复错误后的结果之前,应用程序(Java,OCI,OCCI或ODP.Net API)可以获取客户端保存的逻辑事务ID。应用程序可以使用逻辑事务ID调用PL / SQL过程DBMS_APP_CONT.GET_LTXID_OUTCOME来确定最后提交的结果:提交(true或false)和用户调用完成(true或false)。

使用Transaction Guard时,当错误已经恢复并且最后一个会话的事务未提交,应用程序可以自己控制重新执行事务,应用程序可以在最后一个事务提交并且用户调用完成时继续。应用程序可以使用Transaction Guard将已知结果返回给客户端,以便客户端可以决定下一个要采取的操作。

Transaction guard事例

在上图中,数据库通知应用程序是否提交事务以及是否完成最后一次用户调用。 应用程序然后可以将结果返回给最终用户。

可能性如下:

1. 如果事务提交并且用户调用已完成,则应用程序可以将结果返回给最终用户并继续。

2. 如果事务已提交但用户调用未完成,则应用程序可以将结果返回给最终用户并发出警告。 示例包括丢失的绑定或丢失的行数。 一些应用程序依赖于额外的信息,而另一些则不需要。

3. 如果用户调用未提交,则应用程序可以将此信息返回给最终用户,或安全地重新提交。 该协议是有保证的。 当提交状态返回false时,最后一次提交被阻止提交。

RAC架构下,TAF和TG的集成

当启用TAF时,开发人员不得直接使用Transaction Guard。如果TAF已启用或可启用,则在为ODP.NET或OCI开发Transaction Guard时,必须使用与“ODP.NET with TAF”中提供的示例类似的代码。

故障时,12c中的TAF获取新连接, 并且启用TG时调用Transaction Guard来强制提交结果。

此行为适用于TAF BASIC和TAF SELECT模式。如果TG返回提交并完成,则TAF将继续并且应用程序视为没有发现错误。如果TG返回未提交或已提交但未完成,则TAF向应用程序返回TAF错误。 TAF维护新的连接。

当同时使用TAF和Transaction Guard时,开发人员可以使用TAF错误代码(ORA-25402,ORA-25408,ORA-25405)来决定安全地重新提交事务,或者向用户返回指示未提交事务的消息。这是最为安全的提交方式。如果只启用TAF,重新提交或返回未提交是不安全的。

重新提交要求在TAF调用中建立正确的环境,并且整个事务回滚并重新提交。如下例所示,可以使用布尔变量来跟踪启用了Transaction Guard。这个变量必须在TAF调用中被使用。

注意:在会话失败时不会调用TAF(这包括操作系统级别的'kill -9'或更改系统kill会话)。在INSTANCE失败或NODE DOWN事件和关机时调用TAF,并断开POST_TRANSACTION。

如下提供ODP.Net的代码示例。(注意数据库和客户端版本都必须在12.1.0.1以上版本)

using System;

using Oracle.DataAccess.Client;

class TransactionGuardSample

{

static void Main()

{

bool bReadyToCommit = false;

// This code is needed to test whether Transaction Guard is enabled.

bool bTGEnabled = false;

string constr = "user id=hr;password=hr;data source=oracle";

OracleConnection con = new OracleConnection(constr);

OracleTransaction txn = null;

OracleCommand cmd = null;

try

{

string sql = " update employees set salary=10000 where employee_id=103";

con.Open();

// This bTGEnabled boolean variable is needed to check whether TG is enabled.

// Set this variable immediately after connecting and again in the TAF callback

bTGEnabled = con.OracleLogicalTransaction != null;

txn = con.BeginTransaction();

cmd = new OracleCommand(con, sql);

cmd.ExecuteNonQuery();

bReadyToCommit = true;

}

catch (Exception ex)

{

// rollback here as the SQL execution is unsuccessful

txn.Rollback();

Console.WriteLine(ex.ToString());

}

try // progress to here as TX is successful

{

if (bReadyToCommit)

txn.Commit();

}

catch (Exception ex)

{

//ONLY handle the listed TAF errors and ONLY when Transaction Guard is enabled

if (bTGEnabled && (ex.Number == 25402 || ex.Number == 25408 || ex.Number == 25405 ))

{

// application may cleanup, then rollback and re-submit the current transaction

}

else

{

// handle the error as before; do not resubmit

}

}

finally

{

// dispose all objects

txn.Dispose();

cmd.Dispose();

con.Dispose(); // place the connection back to the connection pool

}

}

}

那些年,我们一起啃过的文档:

Transaction Guard (TG) Integration with Transparent Application Failover (TAF) (文档 ID 2011697.1)

不想走丢的话,请关注公众号 恒生DBA公社:hs_dba

让时代更多元,让工作更高效

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180328G07YH600?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券