操作系统版本 | CentOS7.6 |
数据库版本 | DM8_20240712 |
目录
1.问题与解决方案
1.1问题提出
数据库事务是一种单元操作,要么是全部操作都成功,要么全部失败。
我们可能会遇到这样的需求:在一个事务中,分步记录日志并将日志信息写入TABLE。然而,记录日志的代码与主代码在一个事务中,主代码的ROLLBACK会回滚日志DML操作,日志中的COMMIT,也会影响主代码。详见3.2节、3.3节的测试。
1.2 自治事务
在达梦数据库中,可以定义自治事务解决上面的问题。
通过将一个 DMSQL 语句块定义成自治事务,可以将该块中的 DML 语句和调用程序的
事务环境隔离开。如此一来,该语句块就成为一个由其他事务启动的独立的事务,前一个事
务被称为主事务。在自治事务块中,主事务是挂起的;等待自治事务完成后,会话自动切换
回主事务。
2.实现方法
定义自治事务,需要在 DMSQL 程序的声明部分添加如下语法的语句。
语法如下:
PRAGMA AUTONOMOUS_TRANSACTION;
自治事务的定义语句可以放在 DMSQL 程序声明部分的任何地方,但推荐放在数据结构
声明之前。
作为自治事务的 DMSQL 语句块可以是下面中的一种:
最顶层的(不是嵌套的)匿名 DMSQL 语句块
函数和过程,或者在一个包里定义或者是一个独立的程序
对象类型的方法
数据库触发器
嵌套子过程
实现方法举例:见3.4节与3.1节代码。
3.不同场景的测试
3.1 测试准备
(1)准备2张表:TB_TEST01存放数据、TB_LOG存放日志。
CREATE TABLE TB_TEST01(
C_DT DATETIME,
C_DATA VARCHAR(50),
C_SESSID BIGINT, -- 会话ID
C_TID BIGINT -- 事务ID
);
CREATE TABLE TB_LOG(
C_DT DATETIME,
C_LOG VARCHAR(50),
C_SESSID BIGINT, -- 会话ID
C_TID BIGINT -- 事务ID
);
(2)准备3个存储过程用来记录日志或错误信息
P_LOG_COMMIT //存过中使用COMMIT
P_LOG_UNCOMMIT //存过中不使用COMMIT也不使用ROLLBACK
P_LOG_AUTONOMOUS //自治事务
--存过中使用COMMIT
CREATE OR REPLACE PROCEDURE P_LOG_COMMIT(V_LOG VARCHAR(50))
IS
BEGIN
INSERT INTO TB_LOG(C_DT,C_LOG,C_SESSID,C_TID)
SELECT SYSDATE(),V_LOG,SESSID,ID FROM SYS.V$TRX T WHERE T.SESS_ID=SESSID;
COMMIT;
END;
--存过中不使用COMMIT也不使用ROLLBACK
CREATE OR REPLACE PROCEDURE P_LOG_UNCOMMIT(V_LOG VARCHAR(50))
IS
BEGIN
INSERT INTO TB_LOG(C_DT,C_LOG,C_SESSID,C_TID)
SELECT SYSDATE(),V_LOG,SESSID,ID FROM SYS.V$TRX T WHERE T.SESS_ID=SESSID;
END;
--自治事务
CREATE OR REPLACE PROCEDURE P_LOG_AUTONOMOUS(V_LOG VARCHAR(50))
IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO TB_LOG(C_DT,C_LOG,C_SESSID,C_TID)
SELECT SYSDATE(),V_LOG,SESSID,ID FROM SYS.V$TRX T WHERE T.SESS_ID=SESSID;
COMMIT;
END;
3.2 场景一:主程序的ROLLBACK会回滚日志
场景一使用存过P_LOG_UNCOMMIT记录日志,这个存过中没有COMMIT也没有ROLLBACK。
(1)清空数据
DELETE FROM TB_TEST01;
DELETE FROM TB_LOG;
COMMIT;
(2)执行代码块
BEGIN
INSERT INTO TB_TEST01(C_DT,C_DATA,C_SESSID,C_TID)
SELECT SYSDATE(),'主程序:主程序的ROLLBACK会回滚日志',SESSID,ID FROM SYS.V$TRX T WHERE T.SESS_ID=SESSID; -- 1.主程序写入一条数据
P_LOG_UNCOMMIT('日志:主程序的ROLLBACK会回滚日志'); -- 2.用存过记录日志
ROLLBACK; -- 3.回滚
END;
(3)查询结果
小结:日志写入失败。主程序的ROLLBACK回滚了存过P_LOG_UNCOMMIT中的INSERT操作。
3.3 场景二:日志中的COMMIT会提交主程序的事务
场景二使用存过P_LOG_COMMIT记录日志,这个存过中有COMMIT语句,详见3.1节存过代码。
(1)清空数据
DELETE FROM TB_TEST01;
DELETE FROM TB_LOG;
COMMIT;
(2)执行代码块
BEGIN
INSERT INTO TB_TEST01(C_DT,C_DATA,C_SESSID,C_TID)
SELECT SYSDATE(),'主程序1:日志中的COMMIT会提交主程序的事务',SESSID,ID FROM SYS.V$TRX T WHERE T.SESS_ID=SESSID; -- 1.主程序写入第1条数据
P_LOG_COMMIT('日志:日志中的COMMIT会提交主程序的事务'); --2.用存过记录日志
INSERT INTO TB_TEST01(C_DT,C_DATA,C_SESSID,C_TID)
SELECT SYSDATE(),'主程序2:日志中的COMMIT会提交主程序的事务',SESSID,ID FROM SYS.V$TRX T WHERE T.SESS_ID=SESSID; --3. 主程序写入第2条数据
ROLLBACK; --4.回滚
END;
(3)查询结果
SELECT * FROM TB_TEST01;
SELECT * FROM TB_LOG;
小结:主程序ROLLBACK失败。存过P_LOG_COMMIT中的COMMIT提交了第1个INSERT。从查询截图也可以看出,存过P_LOG_COMMIT的事务ID与主程序中第1条INSERT的事务ID是一样的。
3.4 场景三:自治事务
场景三使用存过P_LOG_AUTONOMOUS记录日志,这个存过定义了自治事务,详见3.1节存过代码。
(1)清空数据
DELETE FROM TB_TEST01;
DELETE FROM TB_LOG;
COMMIT;
(2)执行代码块
BEGIN
INSERT INTO TB_TEST01(C_DT,C_DATA,C_SESSID,C_TID)
SELECT SYSDATE(),'主程序:自治事务',SESSID,ID FROM SYS.V$TRX T WHERE T.SESS_ID=SESSID;
P_LOG_AUTONOMOUS('日志:自治事务');
ROLLBACK;
END;
(3)查询结果
SELECT * FROM TB_TEST01;
SELECT * FROM TB_LOG;
小结:日志写入成功,主程序ROLLBACK成功。从日志中还可以看出,会话134037224中,主程序与日志存过是2个不同事务,事务号分别是277067和277068。
4.注意事项
自治事务相对主事务是完全独立的。由于执行自治事务时主事务处于挂起状态,如果自
治事务需要的锁资源已经被主事务拥有则会产生死锁报错。