达梦数据库的事务管理与Oracle类似,是自动开启的。理解其工作机制,关键是掌握事务的隐式开启、显式结束以及自动提交模式。
原子性 (Atomicity):事务中的所有操作要么全部成功,要么全部失败。
一致性 (Consistency):事务执行前后,数据库从一个一致状态变到另一个一致状态。
隔离性 (Isolation):多个并发事务之间互不干扰。
持久性 (Durability):一旦事务提交,对数据的修改就是永久性的。
事务的开始:隐式开启
在达梦中,通常不需要显式地“开启”一个事务。事务会在执行第一条可执行的SQL语句(如INSERT, UPDATE, DELETE)时自动隐式开始
事务的结束:显式提交或回滚
事务必须通过以下命令之一来显式结束:
COMMIT; 提交事务,将当前事务的所有更改永久保存到数据库。
ROLLBACK; 回滚事务,撤销当前事务的所有未提交更改,数据库恢复到事务开始前的状态
AUTOCOMMIT = OFF(默认):需要手动执行COMMIT来提交,或ROLLBACK来撤销更改
AUTOCOMMIT = ON:每条SQL语句执行后都会自动提交,无法回滚。
查看当前状态
SHOW AUTOCOMMIT;
修改
--关闭自动提交(进入手动提交模式)
SET AUTOCOMMIT OFF;
--开启自动提交
SET AUTOCOMMIT ON;
隐式提交的情况
无论AUTOCOMMIT如何设置,执行CREATE、ALTER、DROP、TRUNCATE、GRANT、REVOKE等DDL语句时,都会触发隐式提交
在复杂事务中,可以使用保存点实现部分回滚。
创建保存点:SAVEPOINT savepoint_name;
回滚到保存点:ROLLBACK TO SAVEPOINT savepoint_name;
释放保存点:RELEASE SAVEPOINT savepoint_name;
SET AUTOCOMMIT OFF;
INSERT INTO products (id, name) VALUES (1, 'Product A');
SAVEPOINT before_insert_b; -- 设置保存点
INSERT INTO products (id, name) VALUES (2, 'Product B');
--发现 Product B 插入有误,回滚到保存点
ROLLBACK TO SAVEPOINT before_insert_b;
COMMIT; -- 提交后,数据库中只有 Product A
DM数据库支持三种事务隔离级别:读未提交、读提交和串行化,(另外 DM 数据库还支持只读事务,只读事务只能访问数据,但不能修改数据)读提交是 DM 数据库默认使用的事务隔离级别。用户在事务开始时,使用以下语句可设定事务隔离级别。
查询事务隔离级别
select isolation from v$trx;--0:读未提交,1:读提交(默认)、2:可重复读、3:串行化
设定事务隔离级别
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
| 隔离级别 | 脏读 (Dirty Read) | 不可重复读 (Non-Repeatable Read) | 幻读 (Phantom Read) | 说明 |
|---|---|---|---|---|
| 读未提交 (READ UNCOMMITTED) | 可能 | 可能 | 可能 | 可能读到未提交的“脏”数据,并发最高 |
| 读已提交 (READ COMMITTED) | 不可能 | 可能 | 可能 | 默认级别,保证只读到已提交的数据 |
| 串行化 (SERIALIZABLE) | 不可能 | 不可能 | 不可能 | 最严格,完全串行执行,性能最低 |
脏读(DirtyRead)
所谓脏读就是对脏数据的读取,而脏数据所指的就是未提交的已修改数据。也就是说,一个事务正在对一条记录做修改,在这个事务完成并提交之前,这条数据是处于待定状态的(可能提交也可能回滚),这时,第二个事务来读取这条没有提交的数据,并据此做进一步的处理,就会产生未提交的数据依赖关系,这种现象被称为脏读。如果一个事务在提交操作结果之前,另一个事务可以看到该结果,就会发生脏读。
举例:小张本月奖金 500 块,财务错误地加在小王工资上,还未提交,小王通过事务查询自己的工资多了 500,后续财务核算进行修改,将事务进行回滚,这样员工 B 通过事务看到的是脏读。
不可重复读(Non-RepeatableRead)
一个事务先后读取同一条记录,但两次读取的数据不同,我们称之为不可重复读。如果一个事务在读取了一条记录后,另一个事务修改了这条记录并且提交了事务,再次读取记录时如果获取到的是修改后的数据,这就发生了不可重复读情况。
举例:在事务A中,读取到张三的工资为 5000,操作没有完成,事务还没提交。与此同时,事务 B 把张三的工资改为 8000,并提交了事务。 随后,在事务 A 中,再次读取张三的工资,此时工资变为 8000,两次读取到的结果不一致。
幻像读(PhantomRead)
一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为幻像读。
举例:目前公司员工有 10 人,老板通过事务 A 读取公司所有人数为 10 人。此时招聘部通过事务B插入一条新员工的记录。这时,事务 A 再次查看公司的员工人数记录,记录为 11 人。此时产生了幻读。
核心功能:实时跟踪所有会话的详细信息,包括会话 ID、用户、客户端 IP、执行的 SQL 语句、等待事件等。
核心字段:
SESS_ID: 会话 ID,系统内部标识
SQL_TEST: 取 sql 的头 1000 个字符
STATE: 会话状态。
CREATE 创建,表示会话对象已创建,但还不能使用;
STARTUP 启动,表示会话正在启动中;
IDLE 空闲,表示会话当前没有执行操作;
ACTIVE 活动,表示会话当前正在执行操作;
PENDING 限流等待,当 INI 参数 MAX_CONCURRENT_TRX>0 时,会话可能会因为并行事务限流而处于此状态;
FREEING 正在释放,表示会话正在被释放;
USER_NAME: 当前用户
TRX_ID: 事务 id,为 0 表示事务未开始或事务已结束
CLNT_IP: 客户端 IP
CLNT_HOST: 客户端主机名
核心功能:记录当前所有事务的状态,包括事务 ID、开始时间、锁持有情况、回滚段使用等。
核心字段:
ID:当前活动事务的 ID 号
STATUS:当前事务的状态。
NOT START 未开始任何操作;
ACTIVE 活动;
LOCK WAIT 锁等待;
ROLLING 正在回滚;
PRE_COMMIT 两阶段事务的预提交状态;
TO_RELEASE DPC 环境下分布式事务的等待释放状态。分布式事务完成第二阶段提交后转入 TO_RELEASE 状态,分布式事务在所有节点都转入 TO_RELEASE 状态后才允许释放
ISOLATION:隔离级。0:读未提交;1:读提交;2:可重复读;3:串行化
SESS_ID:当前事务的所在会话 ID,系统内部标识
THRD_ID:当前事务对应的线程 ID
等待事务排查:
//查询正在等待的事务列表
SELECT * FROM V$TRXWAIT;
根据查询出来的事务id查询事务详情
SELECT ID,SESS_ID,SQL_TEST,CLNT_IP, SQL_TEXT
FROM V$TRX left join V$SESSIONS B where A.SESS_ID = B.SESSID
WHERE ID = (2613902)
核心思路:
SELECT ID as 被阻塞的事务id,
WAIT_FOR_ID as 阻塞的事务id,
WAIT_TIME as 等待时间,
THRD_ID as 阻塞事务的线程id
FROM V$TRXWAIT;
说明当前存在锁等待,事务2613902正在等待事务2613901释放锁,已等待78653902秒。
步骤2:关联事务与会话,定位操作源
通过V$TRX(事务信息)和V$SESSIONS(会话信息)关联,获取事务对于的会话id和操作用户:
SELECT
t.ID AS 事务ID,
t.SESS_ID AS 会话ID,
s.USER_NAME AS 操作用户,
s.CLNT_IP AS 客户端IP,
t.STATUS AS 事务状态
FROM V$TRX t
JOIN V$SESSIONS s ON t.SESS_ID = s.SESS_ID
WHERE t.ID IN (
SELECT ID FROM V$TRXWAIT
UNION
SELECT WAIT_FOR_ID FROM V$TRXWAIT
);
步骤3:查看事务执行的SQL语句
-- 查看阻塞事务和等待事务执行的SQL
SELECT
s.SESS_ID AS 会话ID,
t.ID AS 事务ID,
CASE
WHEN t.ID IN (SELECT WAIT_FOR_ID FROM V$TRXWAIT) THEN '阻塞方'
WHEN t.ID IN (SELECT ID FROM V$TRXWAIT) THEN '等待方'
END AS 角色,
SUBSTR(s.SQL_TEXT, 1, 200) AS 执行的SQL
FROM V$SESSIONS s
JOIN V$TRX t ON s.SESS_ID = t.SESS_ID
WHERE t.ID IN (
SELECT ID FROM V$TRXWAIT
UNION
SELECT WAIT_FOR_ID FROM V$TRXWAIT
);
其他相关查找阻塞会话语句:
查询活跃会话
SELECT SESS_ID, USER_NAME, CLNT_IP, SQL_TEXT
FROM V$SESSIONS
WHERE STATE= 'ACTIVE';
查询阻塞会话
select s.USER_NAME,s.sess_id,s.TRX_ID,s.SQL_TEXT,s.RUN_STATUS from v$sessions s,v$lock l where s.trx_id=l.trx_id and l.blocked=1;
查询阻塞源头
select * from v$sessions where trx_id in (select wait_for_id from v$trxwait where wait_for_id not in (select id from v$trxwait));
查询被阻塞的信息和引起阻塞的信息
SELECT SYSDATE STATTIME,DATEDIFF(SS,S1.LAST_SEND_TIME,SYSDATE) SS,
'被阻塞的信息' WT,S1.SESS_ID WT_SESS_ID,S1.SQL_TEXT WT_SQL_TEXT,S1.STATE WT_STATE,S1.TRX_ID WT_TRX_ID,
S1.USER_NAME WT_USER_NAME,S1.CLNT_IP WT_CLNT_IP,S1.APPNAME WT_APPNAME,S1.LAST_SEND_TIME WT_LAST_SEND_TIME,
'引起阻塞的信息' FM,S2.SESS_ID FM_SESS_ID,S2.SQL_TEXT FM_SQL_TEXT,S2.STATE FM_STATE,S2.TRX_ID FM_TRX_ID,
S2.USER_NAME FM_USER_NAME,S2.CLNT_IP FM_CLNT_IP,S2.APPNAME FM_APPNAME,S2.LAST_SEND_TIME FM_LAST_SEND_TIME
FROM V$SESSIONS S1,V$SESSIONS S2,V$TRXWAIT W
WHERE S1.TRX_ID=W.ID
AND S2.TRX_ID=W.WAIT_FOR_ID;
查询阻塞关系
with sel_lock as
(SELECT O.NAME, O.schid, o.pid, L.*
FROM V$LOCK L, SYSOBJECTS O
WHERE L.TABLE_ID = O.ID
AND BLOCKED = 1),
locks as
(select b.sess_id, b.sql_text, a.name, a.addr, a.schid, b.trx_id
from sel_lock a, V$sessions b
where a.trx_id = b.trx_id),
locks_wait as
(select b.sess_id, b.sql_text, a.addr, b.trx_id
from sel_lock a, V$sessions b
where a.tid = b.trx_id)
select a.sess_id 被阻塞会话,
a.sql_text 被阻塞语句,
b.sess_id 造成阻塞会话,
b.sql_text 造成阻塞语句,
a.name,
(select name
from sysobjects
where ID = a.schid
and type$ = 'NVWATSTYBZ') 模式名
from locks a, locks_wait b
where a.addr = b.addr;
查看数据库的连接信息
SELECT 'sp_close_session('||A.SESS_ID||');',
A.SESS_ID AS 会话id,
A.SQL_TEXT AS SQL语句,
A.STATE AS 会话状态,
A.N_USED_STMT AS 当前会话使用句柄数量,
A.CURR_SCH AS 当前模式,
A.USER_NAME AS 用户名,
A.TRX_ID AS 事务ID,
A.CREATE_TIME AS 会话创建时间,
A.CLNT_TYPE AS 客户端类型,
A.TIME_ZONE AS 时区,
A.OSNAME AS 操作系统名称,
A.CONN_TYPE AS 连接类型,
B.PROTOCOL_TYPE AS 协议类型,
B.IP_ADDR AS 访问ip地址
FROM SYS.V$SESSIONS A ,SYS.V$CONNECT B where A.Sess_id= B.SADDR AND A.USER_NAME = 'NV2' ORDER BY
SF_GET_EP_SEQNO(A.rowid),A.Sess_id ;
正常释放锁:通过查询出来的阻塞事务的操作方(通过客户端IP和操作用户定位),让其提交或者回滚事务:
COMMIT;
ROLLBACK;
强制终止阻塞对话:若阻塞事务无法正常释放,可通过SP_CLOSE_SESSION存储过程种植阻塞会话
SP_CLOSE_SESSION(140398304267936); --140398304267936是阻塞方的会话ID
不管是阻塞方执行COMMIT;、ROLLBACK;还是其他会话执行SP_CLOSE_SESSION(140398304267936);强制终止会话,等待方的执行语句随之完成。
文章
阅读量
获赞
