一、什么是事务
事务(Transaction)是一组逻辑上相关联的操作,这些操作要么全都成功执行,要么全都不执行。事务所含的操作可以分布在不同的程序甚至不同的机器上。
事务处理必须具备四个基本属性原子性、一致性、独立性、耐久性(Atomic、Consistent、 Isolated、Durable也叫ACID属性):
n
原子性(Atomic)要求一个事务是一个最小的不可分割的单位,以银行转帐事务为例,一个完整的事务处理需要改变转出和转入两个帐户的资金,只改变一个帐户是不对的。
n
一致性(Consistent)意味着事务处理正确地转换了系统状态。要么是资金离开一个帐户进入另一个帐户,要么是都保持不变---只有这两种可能的状态。如果系统没有得到转换后状态,就回到没有转换时的状态而不会停在中间。
n
独立性(Isolated)保证任何其他的事务都不能看到一个处在不完整状态下的事务处理。尽管实际上在钱款被提走而还没有存入另一个帐号期间有一个远远小于一秒的间隔,系统中的其他事务也不会知道的。
n
耐久性(Durable)表示事务处理能在系统失败时保存完好。一个事务处理应当能够承受所有的失败,包括服务器、进程、通信以及媒体失败等等。
二、事务处理系统的X/Open DTP 模式
由于分布式事务处理中的操作可能位于许多不同的平台和数据库产品上,为了协调和控制这些不同的事务操作的行为,必须有一个事务管理进程,而X/Open标准正是为此而定义了一个分布式事务处理(Distributed Transaction Processing, DTP)的模式以及用于事务管理进程和资源管理器交互的XA接口。
X/Open DTP模式的结构图:
DTP模式有三个模块:
1)
应用程序(Application Program)通过TX接口向事务管理器定义一个事务边界,然后向资源管理器提交事务操作;
2)
资源管理器(Resource Manager, RM)是支持XA接口并提供对共享资源访问接口的管理程序。最常见的RM是数据库,但象消息队列或打印机缓冲池等也可以作为RM,只要它们支持XA接口。
3)
事务管理器(Transaction Manager, TM)为事务处理编号,监控事务处理全过程并负责事务执行或错误恢复。
Tuxedo系统提供了用于创建事务管理器的组件和工具(buildtms)。资源管理器厂商(Oracle, Infomix等)必须提供兼容XA接口的函数库,这些函数库用来和Tuxedo的组件及工具一同编译生成一个Tuxedo的事务管理器。
XA接口及两步提交协议
DTP模式中事务管理器和资源管理器之间通过XA接口进行通信,XA接口定义了关于如何协调、执行及恢复事务的一个协议规范。两步提交协议(Two Phase Commit protocol, 2PC)是XA规范的一部分。
在两步提交协议中的第一步,TM询问参与一项事务处理的所有RM是否都准备好并能够执行事务操作。
第二步中,TM检查RM的应答中是否有不能执行事务操作的应答,若有则通知所有参与事务处理的RM回滚它们所做的事务操作,若没有则通知RM执行事务操作。
三、Tuxedo服务程序和RM的连接
一个Tuxedo服务程序连接RM是通过调用tpopen()函数实现的,该函数只能在服务程序初始化时(tpsvrinit)调用一次。要关闭和RM的连接,则必须在服务程序终止前调用tpclose()函数。
一个Tuxedo服务程序中不应该执行CONNECT、COMMIT、ROLLBACK、SAVEPOINT和SET TRANSACTION这些SQL语句,因为它们会改变事务处理的状态。同样,服务程序也不应该执行CREATE、ALTER和RENAME等SQL数据定义语句,因为这些语句隐式地执行了COMMIT语句。
四、Tuxedo事务管理器
Tuxedo事务管理器(TMS)必须跟踪分布式事务处理的整个流程,记录足够的信息以便在任何时候进行回滚,因此TMS使用事务日志文件(TLOG)来记录跟踪信息,同时为了区别系统中同时进行的不同事务处理流程,TMS又为不同的事务处理分配了一个全局事务编号(GTRIDs)。在事务处理的不同阶段,TMS将执行不同的动作如下表:
阶段TMS动作
应用程序启动一项事务处理
为事务处理分配一个全局事务编号(GTRIDs)
启动事务处理的进程与其它进程通信
跟踪这些参与事务处理的进程
事务处理访问RM
将相应的GTRIDs传递给RM,这样RM就可以监控哪些数据库记录被该事务处理存取
应用程序标记一项事务处理将被执行
按两步提交协议执行事务
应用程序取消事务处理
执行回滚操作
有错误发生
执行回滚操作
五、事务处理应用程序开发及管理
开发环境:Linux + Tuxedo + Oracle 1、开发流程图
2、创建Oracle数据库及表
先设置Oracle环境变量:
ORACLE_HOME=/home/oracle
ORACLE_SID=linuxdb
PATH=$PATH:$ORACLE_HOME:$ORACLE_HOME/bin:$ORACLE_HOME/dbs
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$TUXDIR/lib:/home/oracle/lib
再编写Sql脚本在数据库中创建表空间、用户及数据表:
创建表空间CREATE TABLESPACE
创建用户CREATE USER
创建表CREATE TABLE
运行Oracle的SQL*PLUS,执行Start命令运行Sql脚本。
3、创建Oracle的TMS
检查$TUXDIR/udataobj目录下的RM文件是否有对Oracle8的RM描述
Oracle_XA:xaosw:-L${ORACLE_HOME}/lib -lclntsh
此描述将做为编译TMS和服务端程序的编译选项。
执行Buildtms命令编译Oracle的TMS服务程序,命令格式如下:
buildtms –o tms_name –r rm_name
-o tms_name: 编译生成的TMS程序名
-r rm_name: RM描述名
例如:
buildtms –o TMS_ORA –r Oracle_XA
4、客户端程序
客户端程序通常作为一项事务处理的发起者(initiator)调用tpbegin函数,声明如下: int tpbegin(unsigned long timeout, long flags)
参数:
unsigned long timeout 事务处理超时时间(秒),若为0则为系统最大超时时间
long flags 未定义,应为0
返回值:
若为-1则代表出错,tperrorno为错误代码。
客户端程序在调用tpbegin之后可通过tpcall、tpacall、tpconnect等来请求服务端程序,所有被请求的服务将作为事务处理的参与者被TMS跟踪记录,若任何服务请求返回失败(TPFAIL)则客户端程序需调用tpabort函数终止当前事务处理,tpabort函数声明如下:
int tpabort(long flags)
参数:
long flags 未定义,应为0
返回值:
若为-1则代表出错,tperrorno为错误代码。
若所有请求都成功(TPSUCCESS)则调用tpcommit函数执行事务处理,声明如下:
int tpcommit(long flags)
参数:
long flags 未定义,应为0
返回值:
若为-1则代表出错,tperrorno为错误代码。
客户端程序例子:
/* Begin a Global transaction */
if (tpbegin(30, 0)== -1)
{
printf("ERROR: tpbegin failed (%s)\n", tpstrerror(tperrno));
goto finish;
}
/* send a request to the OPEN_ACCT service and get the reply */
if (tpcall("OPEN_ACCT", (char *)fbfr, 0, (char**)&fbfr, &len, 0) == -1)
{
if (tperrno == TPESVCFAIL &&
fbfr != NULL &&
(server_status=Ffind32(fbfr,STATLIN,0,0)) != 0)
{
/* Server returned failure */
printf("OPEN_ACCT returns failure (%s)\n", server_status);
}
else
{
printf("ERROR: OPEN_ACCT failed (%s)\n", tpstrerror(tperrno));
}
/* Abort the transaction */
tpabort(0);
goto finish;
}
/* Commit the transaction */
if (tpcommit(0) < 0) {
printf("ERROR: tpcommit failed (%s)\n", tpstrerror(tperrno));
goto finish;
}
注意:
1) 只有事务处理的发起者才能调用tpabort或tpcommit函数终止或执行一项事务处理;
2) Tuxedo不支持嵌套事务处理,即发起者在调用tpbegin和tpabort或tpcommit之间不能再调用tpbegin开始一个新的事务处理。
5、服务端程序
服务端程序负责和RM的连接(tpopen,tpclose),同时作为事务处理的参与者通过Embeded SQL存取Oracle RM中的数据。
服务程序在启动(tpsvrinit)时调用一次tpopen函数连接到RM,函数声明如下:
int tpopen(void)
参数:无
返回值:
-1代表出错
服务程序在退出之前(tpsvrdone)必须调用tpclose函数关闭和RM的连接,函数声明如下:
int tpclose(void)
参数:无
返回值:
-1代表出错
Oracle的Embeded SQL语言是Pro*C,具体编程方法可参考《PRO_C程序设计和ORACLE调用》一书。
注意:
1) 在用BuildServer编译服务端程序时需加上-r rm_name选项指明服务程序所用的RM名称如-r Oracle_XA
6、配置文件
在Tuxedo应用程序的配置文件中主要修改以下四个部分:
n RESOURCES 设置最大事务处理个数及执行控制标志
n MACHINES 每台机器的TLOG配置
n GROUPS 配置RM和TMS的信息
n SERVICES 设置自动事务处理标志
RESOURCES部分中的参数:
MAXGTT – 限制在一台机器上某一时刻分配GTRIDs总数,最大值2048,缺省100,可在MACHINES中为单独的一台机器改写。
CMTRET – 事务执行控制标志,有两个值LOGGED和COMPLETE, LOGGED指明tpcommit将在所有事务参与者都准备好执行事务后返回,而COMPLETE指明tpcommit将在所有事务参与者都执行完事务后返回。
MACHINES部分中的参数:
参与事务处理的每台机器都必须有一个TLOG, TLOG配置参数如下:
TLOGNAME – TLOG名称
TLOGDEVICE – TLOG文件名(绝对路径)
TLOGSIZE – TLOG大小,以物理页为单位,1 – 2048,缺省为100(可选)
TLOGOFFSET – TLOG在TLOGDEVICE中偏移量位置(可选)
GROUPS部分中的参数:
TMSNAME – TMS文件名,TMS由buildtms生成, 如TMS_ORA
TMSCOUNT – TMS个数,2 – 10, 缺省为3
OPENINFO – 连接RM所需要的信息,此信息由RM厂商提供,如Oracle的OPENINFO是"Oracle_XA:Oracle_XA+Acc=P/user1/passwd1+SesTm=0+LogDir=."
CLOSEINFO – 关闭RM所需要的信息(可选)
SERVICES部分中的参数:
AUTOTRAN – 若是Y则service将自动成为一项事务的发起者,但若在这之前事务已存在则不会开始一项新的事务。缺省值是N
TRANTIME – 为AUTOTRAN的事务处理设定超时时间
7、TLOG文件
TLOG文件记录事务处理流程以便在错误发生时回滚事务操作,必须为参与事务处理的每台机器创建一个TLOG文件。创建TLOG时先进入tmadmin界面,用crdl命令创建设备文件,命令格式为:
Crdl –b blocks –z config
-b blocks 为设备文件的大小,以块(block)为单位
-z config 为设备文件名,应和配置文件中TLOGDEVICE相同
然后执行crlog命令创建TLOG, 命令格式为:
crlog –m machine
-m machine 为机器名
8、启动应用程序
用tmboot命令启动应用程序,但在启动之前要注意:
对Oracle8.0以上数据库,在启动TMS_ORA之前,先用Oracle的sys用户(口令:change_on_install)进入Sql*Plus,将对 DBA_PENDING_TRANSACTIONS表的Select权限赋予所有用户,命令如下:grant select on DBA_PENDING_TRANSACTIONS to public
对Oracle7.0数据库,在启动TMS_ORA之前,先用 Oracle的sys用户(口令:change_on_install)进入Sql*Plus,执行${ORACLE_HOME}/RDBMS /ADMIN/XAVIEW.sql脚本,然后将对v$XAVIEW$视图的Select权限赋予所有用户,命令如下:grant select on v$XAVIEW$ to public