加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

oracle_针对二阶段锁(Two-Phase-Commit(2PC))的处理方法

(2018-11-01 20:12:58)
标签:

2pc

two-phase

prepare

commit

forget

分类: oracle


   最近在处理2pc问题时,进行了些许研究,想在这里记录总结一下,也是好久没有更新博客, 一直也想不起来进行总结,以后希望能够坚持写作,不往同志们的关注。

   大家先通过之前的一篇文章来对2PC、XA、DTP与两阶段提交 进行简单了解,      http://blog.sina.com.cn/s/blog_8317516b01013y2p.html

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;

 

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有