最近在处理2pc问题时,进行了些许研究,想在这里记录总结一下,也是好久没有更新博客,
一直也想不起来进行总结,以后希望能够坚持写作,不往同志们的关注。
ORACLE分布式事务
数据库分布式事务是对两个或多个数据库进行修改,包含了跨越多个节点的DML语句。分布式事务确保原子性,事务内的所有操作不能一起提交就只能一起回滚。
分布式事务和远程事务是不同的,远程事务包括一个或多个DML操作,只是操作一个相同的数据库节点,
例如:
- 分布式事务:
insert
into table@remotesite;
insert
into mytable: --local table
commit;
- 远程事务:
insert
into table@remotesite;
commit;
ORACLE分布式事务使用DATABASE
LINK实现分布式事务,可以通过同义词,数据库视图或PLSQL程序实现透明包装。
ORACLE分布式事务的实现独立于操作系统版本和数据库版本,只要得到认证和支持,可以跨越不同的操作系统和数据库版本(主要还是兼容性考虑)。
全局数据库名
数据库的全局名由DB_NAME.DB_DOMAIN组成,DB_DOMAIN指网络域名.
在一个分布式环境中,数据库的全局名必须是唯一的,所以每个数据库必须有一个明确的名称定义.
Note: DB_DOMAIN
参数在建库时定义,后期在参数文件中的修改将不起作用.
以下为查看和修改global_name命令:
sqlplus> select * from
global_name;
ORCL.US.ORACLE.COM;
sqlplus> ALTER DATABASE RENAME GLOBAL_NAME TO
orcl.net;
sqlplus> select * from
global_name;
ORCL.NET;
两阶段提交(2PC)
两阶段提交机制用于在分布式事务中保证数据的一致性.该功能自动启用,原子性保证多个库间的提交和回滚操作.
2PC简单描述成三个阶段:
PREPARE 准备阶段:初始节点(commit point site,主节点)
发出请求询问参与节点能够保证发起提交或回滚操作且何时具备,参与节点将刷新日志到日志文件,
在节点数据中将锁切换成in-doubt状态记录到视图中,将节点中最高SCN发送给初始节点.
COMMIT
提交阶段:初始节点提交事务且将提交SCN的日志写入日志文件,数据块锁释放.
FORGET
清理阶段:将pending事务相关的视图记录进行清理(dba_2pc_pending/dba_2pc_neighbors).
以上步骤对发起事务的应用来说是快速和透明的.
NOTE:
在PREPARE阶段发送失败将导致回滚,因为节点间没有达成一致,也就是没有发送事务.
在COMMIT阶段发送失败将导致提交或回滚,事务已经发送回滚和提交均可.
参与两阶段提交操作的节点,将会担任以下一个或多个角色:
客户端:从其他数据库请求数据的数据库节点;
服务端;从参与分布式事务的其他数据库接受数据的数据库节点中;
全局协调者:分布式事务的发起节点,负责发送信息给其他节点进行prepare和commit(或者rollback).
本地协调者:为完成事务的部分操作,需要访问其他节点的数据,节点也负责通过直接沟通来协调节点间的事务.
提交点节点:分布式事务中,第一个提交或回滚事务的节点,通常是全局协调者角色,该节点决定事务的输出结果.且从来不会进入prepare状态.
(角色选定通过参与节点参数COMMIT_POINT_STRENGTH最大值决定,通常都是1.)
会话树:数据库工作的树形通信拓扑图.由于分布式事务中的语句被发出,作为会话树具有一定数据量的递归继承节点.会话树使用继承模型描述分布式事务中节点会话和角色的关系.
关于2pc协议中事务管理,会话树中节点会话将会呈现不同的角色功能.
事务和两阶段提交
In-Doubt(未确定)事务
如果一个分布式事务失败,将会留下未确定事务在一个或多个数据库中.
正常情况下,未确定事务会在节点数据库或网络恢复后自动被数据库后台进程RECO进行解决并清理.
当两阶段提交被中断时,不管任何形式的系统或网络失败,未确定分布式事务就会产生.例如,两个数据库报告协调者将要进行准备提交信息,但是协调者实例当接受到信息后立即失败了.
报告提交的两个数据库现在被挂起等待协调者结果的通知.
Recoverer Process (RECO)
恢复进程
RECO进程是强制的后台进程,在分布式数据中,自动处理失败的分布式事务.
进程自动连接其他参与未确定事务的数据库节点,使用以后的连接或重新发起一个新的连接,当连接被建立起来,
自动解决未确定事务,并且移除在pending视图表中的相关记录.
在时间间隔内,RECO后台进程不断去尝试处理未确定事务的本地事务部分.
通过参数文件中的distributed_transactions和open_links去控制后台RECO的启动,RECO进程只服务于分布式事务.
可以通过ALERT SYSTEM命令 ENABLE/DISABLE DISTRIBUTED
RECOVERY来启动和关闭RECO进程的恢复功能.可以在MOUNT阶段进行启动和关闭.
select
dbid,name,log_mode,open_mode from v$database;
select
instance_number,instance_name,status from v$instance;
启动禁用RECO分布式事务恢复
-禁用RECO:
ALTER
SYSTEM DISABLE DISTRIBUTED RECOVERY
-启动RECO以便自动处理未确定事务
ALTER
SYSTEM ENABLE DISTRIBUTED RECOVERY;
数据库参数
GLOBAL_NAMES:(默认是false):建议建议将该参数设为true,在网络中强制数据库名称唯一.设成true后将强制数据库链接名字与要连接数据库的global_name相同.从false设置成true后,之前创建的数据库链接将不可用.
OPEN_LINKS: (默认是4)
说明一个会话中最大数量的并发打开的远程连接.包括数据库链接以及外部过程,每一个使用一个单独的进程.数量值根据需要连接的数据库决定.
DISTRIBUTED_LOCK_TIMEOUT:
(默认是60秒):定义分布式事务等待锁的超时时间,如果发生超时,将会报错ora-2049.
COMMIT_POINT_STRENGTH (默认是1),取值范围在0到255.分布式事务中用来决定提交点节点.
数据库视图
DBA_2PC_PENDING
DBA_2PC_PENDING提供分布式事务等待恢复的信息。比如未确定事务, 相关列信息如下:
LOCAL_TRAN_ID
Local Id
of the transaction,in the form of n.n.n; where n is a number
GLOBAL_TRAN_ID
Global
Transaction ID, unique to all sites
STATE:
One of the
following: Collecting/Prepared/Committed/Forced Commit/Forced
Rollback
MIXED:
YES
indicates that portions of the transaction have been committed and
portions rolled back (forcibly)
ADVICE
C
indicates Commit
R
indicates Rollback
This field
is populated only if the application has issued one of the
following statements prior to beginning the distributed
transaction:
ALTER SESSION ADVICE COMMIT
or ALTER SESSION ADVICE
ROLLBACK
TRAN_COMMENT
Commit
comment text. This column is populated only if the application has
issued a COMMIT with a comment, for example:
COMMIT
COMMENT 'comment detail'
FAIL_TIME
Time that the row has been
inserted in the view
FORCE_TIME
If the transaction has been
forced the time will be displayed; NULL otherwise
RETRY_TIME
Time that the RECO process
last attempted to resolve the transaction
OS_USER
OS USerID of the local user
that created the transaction
OS_TERMINAL
Terminal from where the local
portion of the transaction originated
HOST
Name of the machine where the
local transaction originated
DB_USER
Database username who
originated the distributed transaction
COMMIT#
If the transaction is
committed this column represents the global commit number
DBA_2PC_NEIGHBORS
DBA_2PC_NEIGHBORS
分布式事务中描述incoming和outgoing的未确定信息,相关列信息如下:
LOCAL_TRAN_ID
Local
identifier of the transaction
IN_OUT
Connection Type:
IN for incoming
OUT for outgoing
DATABASE
For
incoming connections, this is the client db global_name
For
outgoing connections, this value represents the database link
DBUSER_OWNER
For
incoming connections, theOracleUsername
For
outgoing connections, the owner of the database link
INTERFACE
Used to
locate the Global Commit Point site:
For
incoming links, C indicates that this site or one of the
descendants on an outgoing link is the commit point site.
For
outgoing links, C indicates that the destination database DBID is
the commit point site.
If we are
in-doubt, INTERFACE is N and then the top-level database either is
the commit point site or can locate the commit point site.
DBID
The global
name of the remote database
SESS#
Local
session number for the connection at this database. Sessions are
numbered consequently, starting by 1
BRANCH
Transaction branch ID of the connection at this database. Branch
IDs for incoming connections are two byte hexadecimal numbers; the
first byte is the remote parent's session ID, and the second byte
is its branch ID.
V$DBLINK 和 GV$DBLINK
V$DBLINK
列出当前会话中所有打开的数据库链接,数据库链接的IN_TRANSACTION列设置为YES表示当前处于事务中。
GV$DBLINK
列出当前会话中所有打开的数据库链接以及他们对应的实例。视图存在于RAC环境中。
视图可以查看当前会话中打开的数据库链接,在SYS用户下无法查询到视图会话信息,只有在当前连接的工作用户中查询.
V$GLOBAL_TRANSACTION
V$GLOBAL_TRANSACTION
查询当前活动的分布式事务.
测试恢复失败的分布式事务
数据库提供了强制处理分布式事务失败的手段,所以可以根据故障点测试和验证分布式事务的恢复过程.
执行一个提交的注释比如ORA-2PC-CRASH-TEST-n,n从1到10.可以根据N的值来测试不同的场景.
1 Crash commit point after
collect
2 Crash non-commit-point site
after collect
3 Crash before prepare
(non-commit-point site)
4 Crash after prepare
(non-commit-point site)
5 Crash commit point site
before commit
6 Crash commit point site
after commit
7 Crash non-commit-point site
before commit
8 Crash non-commit-point site
after commit
9 Crash commit point site
before forget
10 Crash non-commit-point site
before forget
例如,如果本地comit_point_strength大于远程comit_point_strength下面语句返回下面的信息,且两个节点都已更新.
COMMIT COMMENT
'ORA-2PC-CRASH-TEST-7';
ORA-02054: transaction 1.93.29
in-doubt
ORA-02059: ORA_CRASH_TEST_7 in commit
comment
此时,未确定事务出现在视图DBA_2PC_PENDING中.RECO进程会自动处理这个事务.
Refer to Note 126069.1
Manually Resolving In-Doubt Transactions: Different Scenarios, for
detailed test results regarding each of the situations above.
分布式事务中的失败
分布式事务的失败信息的错误号会在ORA-02040 -
ORA-02099中,一般,发生2PC的不正常的条件是由网络或连接节点的失败导致,同时事务处于prepare和commit阶段.
同时在分布式事务失败后,alert.log中会打印错误信息:
ORA-02053: transaction committed, some remote
DBs may be in-doubt
事务已经本地提交,然而与其他lc节点的连接已经丢失.
ORA-02054: transaction in-doubt
事务本地既没有提交也没回滚,丢失与GC的连接
ORA-02050: transaction rolled back, some remote
DBs may be in-doubt
暗示在两阶段提交时发生通信错误.
ORA-01591: lock held by in-doubt distributed
transaction
遇到上面的错误,用户/应用无法继续处理这个事实,在这种情况下,数据库自动回滚用户的事务请求并且DBA需要手工提交或回滚未确定事务.
注意:读取将会被阻塞,因为直到事务被处理,Oracle不会处理相关数据的版本返回给查询用户.
已知的BUG信息和troubleshootora-1591错误详见:
Note 18989.1 OERR: ORA-1591 "lock held by
in-doubt distributed transaction %s"
Reference
分布式事务等待请求锁或者锁住时间较久超时,如果超过参数DISTRIBUTED_LOCK_TIMEOUT时间后,不能获取请求的锁,会报错:
ORA-02049 timeout: distributed transaction
waiting for lock
处理未确定事务的步骤
1- 报告问题:
查询数据表遇到下面的报错: ORA-01591: lock held by in-doubt
distributed transaction 10.24.340
日志alert.log打印 :
DISTRIB
TRAN DB102D.UK.ORACLE.COM.ea27958e.9.46.338
is local
tran 10.24.340 (hex=0a.18.154)
insert
pending prepared tran, scn=5017243 (hex=0.004c8e9b)
通过日志得知本地事务号 ID,10.24.340,
查询DBA_2PC_PENDING视图:
2-
查询视图DBA_2PC_PENDING
select * from dba_2pc_pending where
local_tran_id='10.24.340';
LOCAL_TRAN_ID
:
10.24.340
GLOBAL_TRAN_ID
:
DB102D.UK.ORACLE.COM.ea27958e.9.46.338
STATE
:
prepared
MIXED
: no
ADVICE
:
TRAN_COMMENT
:
FAIL_TIME
: 02-nov-2010 02:06:02
FORCE_TIME
:
RETRY_TIME
: 02-nov-2010 02:06:02
OS_USER
: NT AUTHORITY\SYSTEM
OS_TERMINAL
: USER33-lap
HOST
: USER33-lap
DB_USER
: TESTUSER
COMMIT#
: 5017243
当前节点不是GC节点,因为local_tran_id与Global_Tran_ID的最后事务号不相同.也就是当前节点不是事务的发起节点.GLOBAL_TRANSACTION_ID信息是通用的信息,在每个分布式事务的参与节点中都将相同.信息global_database_name.hhhhhhhh.local_transaction_id中的global_database_name是GC节点的数据库名称,hhhhhhhh输出是GC中表示唯一的16进制内部标识.当前节点事务处于PREPARE阶段,列ADVICE和TRAN_COMMENT如果有任何输出,帮助你决定本地事务部分需要什么操作.
(COMMIT COMMENT... or SET TRANSACTION... NAME populates
tran_comment column)
3 - 查询视图DBA_2PC_NEIGHBORS
抓取会话树知道我们找到GC信息,可以找到协调者以至于我们能够给解决事务.如果不能我们需要根据树信息找到提交节点,将会彻底解决未确定事务.
查询每个节点视图DBA_2PC_NEIGHBORS找到会话树信息.
-
本地节点:
select * from dba_2pc_neighbors;
LOCAL_TRAN_ID
:
10.24.340
IN_OUT
: in
DATABASE
: DB102D.UK.ORACLE.COM
DBUSER_OWNER
: TESTUSER
INTERFACE
: N
DBID
: ea27958e
SESS#
: 1
BRANCH
:
09002E00520100000104
视图DBA_2PC_NEIGHBORS可以提供未确定事务的连接关联信息,视图信息在每个节点都会不同,根据连接的类型
inbound (IN_OUT = in) 或者 outbound (IN_OUT = out).在我们的实例中IN_OUT =
in,表示我们的数据库是db102c是服务端,而DB102D是客户端.DBUSER_OWNER列表上连接的用户,INTERFACE =
N表示节点DB102C和其它依赖的节点都不是提交节点。
-
远程节点关于事务的信息:
select * from dba_2pc_neighbors;
LOCAL_TRAN_ID : 9.46.338
IN_OUT :
in
DATABASE
:
DBUSER_OWNER : TESTUSER
INTERFACE
: N
DBID
:
SESS# :
1
BRANCH :
0000
-----------------
LOCAL_TRAN_ID : 9.46.338
IN_OUT :
out
DATABASE :
DB102C.UK.ORACLE.COM
DBUSER_OWNER : TESTUSER
INTERFACE
: N
DBID :
bae6b90f
SESS# :
1
BRANCH :
4
For the OUT connection :
列DBUSER_OWNER表上DBLINK的用户,列DATABASE表示远程服务端db102c的名字.local txn
ID与global_transaction_id的结尾部分相同,表示当前节点是GC节点即事务的发起端,dba_2pc_pending视图表示本地事务部分已经提交.
select * from
dba_2pc_pending@db102d:
LOCAL_TRAN_ID : 9.46.338
GLOBAL_TRAN_ID :
DB102D.UK.ORACLE.COM.ea27958e.9.46.338
STATE : committed
MIXED : no
ADVICE :
TRAN_COMMENT :
FAIL_TIME : 02-nov-2010
02:06:02
FORCE_TIME :
RETRY_TIME : 02-nov-2010
02:06:02
OS_USER :
testuser-lap\testuser
OS_TERMINAL :
testuser-lap
HOST : GB-ORACLE\hostEDN
DB_USER : TESTUSER COMMIT# : 5017245
讨论: 本节点已经提交了本地事务部分,之后失败留下GC一直等待相应提交点节点的'commit'返回.
4 - 未确定事务解决
提交点节点事务状态已经是提交,那么我们来手工处理其他LC节点DB102C的事务提交.
COMMIT FORCE
'DB102D.UK.ORACLE.COM.ea27958e.9.46.338','5017245';
-OR-
COMMIT FORCE
'10.24.340','5017245';
使用命令COMMIT
FORCE手工提交未确定的分布式事务.事务使用包含本地事务IO或全局事务ID进行标示.使用整数说明分配事务的SCN.如果不说明SCN,使用系统当前的SCN.
为了确定全局整数SCN,可以使用提交点节点的commit#(最高的全局commit#).
当强制进行事务提交未了说明事务的SCN,当在其他节点上提交时,未确定事务根据分配的SCN提交.
因此,我们可以维护分布式事务的同步提交时间即使是事务失败了.
只有当我们能够确定在另一个节点已经提交的同一事务的SCN时,才指定SCN.
我们可以使用ROLLBACK FORCE命令控制未确定事务进行回滚.
5 - 清理视图信息
在未确定事务被解决后,程序DBMS_TRANSACTION.PURGE_LOST_DB_ENTRY用于手工清理未确定事务在视图中的信息.
execute DBMS_TRANSACTION.PURGE_LOST_DB_ENTRY
('10.24.340');
对于混合事务,一旦数据库部分的被提交或被提交使用过程PURGE_MIXED进行清理操作,
execute DBMS_TRANSACTION.PURGE_MIXED
('txnID');
注意:
但参数UNDO_MANAGEMENT为auto时,操作DBMS_TRANSACTION.PURGE_LOST_DB_ENTRY会失败报错:
ORA-30019: Illegal
rollback Segment operation in Automatic Undo mode, use the
使用下面命令进行处理:
-- 检查_smu_debug_mode当前的值
(default 0):
column Parameter format
a35
column "Session Value"
format a15
column "Instance Value"
format a15
SELECT a.ksppinm
"Parameter", b.ksppstvl "Session Value", c.ksppstvl "Instance
Value"
FROM x$ksppi a, x$ksppcv
b, x$ksppsv c
WHERE a.indx = b.indx
AND a.indx = c.indx AND a.ksppinm LIKE '/_%' escape '/' AND
a.ksppinm like '%smu_debug_mode%' /
-- 临时设置为 4:
SQL> alter system set
"_smu_debug_mode" = 4;
SQL> commit;
SQL>execute
DBMS_TRANSACTION.PURGE_LOST_DB_ENTRY('local_tran_id');
SQL>
commit;
SQL> alter system set
"_smu_debug_mode" = <旧的值>;
SQL> commit;
SCN恢复步骤:
完全恢复:
1. At the
down site recover completely if possible (treat as regular
recovery).
2. SCN
will appear in the alert.log after recover is done on the crashed
Node.
不完全恢复:
1. If
time-based or cancel-based recovery was used on the crashed node,
other sites must be placed back to the same point in time for
global consistency. Get last SCN from the alert.log of the crashed
node.
2. At each
node perform a shutdown normal or immediate.
3. Take a
cold backup.
4. Restore
the control file if necessary.
5. Restore
the last backup of all datafiles along with archived redo
logs.
6. Choose
which tool to use to perform SCN recovery - either SQLPLUS or
RMAN.
7. Connect
as sysdba, startup mount, check status from v$datafile to make sure
all datafiles are online. Issue 'Alter database datafile '?/?/?'
online;', for each datafile with status offline, to bring it
online.
8. Issue
the following command using the latest SCN from alert.log on the
Node that had to be recovered:
RECOVER DATABASE UNTIL CHANGE
;
注意: 在某些情况下(比如执行强制提交命令),自动恢复(RECO进程)需要被禁用.
ALTER SYSTEM DISABLE DISTRIBUTED
RECOVERY;
恢复启动RECO进程:
ALTER SYSTEM ENABLE DISTRIBUTED RECOVERY;
加载中,请稍候......