注册
数据库事务管理
专栏/技术分享/ 文章详情 /

数据库事务管理

LY 2026/09/23 228 0 0
摘要

达梦数据库事务管理详解:ACID、隔离级别、锁机制与闪回


一、事务的特性

事务具备四大特性,即常说的 ACID:

  • 原子性(Atomicity):事务中的操作要么全部成功,要么全部失败回滚。
  • 一致性(Consistency):事务执行前后,数据都必须处于合法、一致的状态。
  • 隔离性(Isolation):并发事务之间互不干扰。
  • 持久性(Durability):事务提交后,其对数据的修改是永久性的。

开启事务并查看当前活动事务(一般来说,开启一个会话窗口即开启了一个新事务):

-- 开启事务 START TRANSACTION; -- 查看当前所有活动事务 SELECT * FROM V$TRX WHERE STATUS = 'ACTIVE';

并发事务演示

先准备一张账户余额表:

-- 数据准备 CREATE TABLE account_balance ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT '账户ID', account_name VARCHAR(50) NOT NULL COMMENT '账户名称', balance DECIMAL(15,2) DEFAULT 0.00 COMMENT '账户余额' ); INSERT INTO account_balance (account_name, balance) VALUES ('张三', 12580.50), ('李四', 8888.88), ('王五', 50000.00), ('赵六', 3200.00), ('孙七', 100000.00), ('周八', 6666.66), ('吴九', 9999.99), ('郑十', 75000.00), ('钱十一', 5200.00), ('陈十二', 88000.00);

接着用两个会话演示并发读写:

-- 事务1:查询数据 START TRANSACTION; SELECT * FROM account_balance WHERE ACCOUNT_NAME = '张三'; -- 12580.5 -- 事务2:修改数据(未提交) UPDATE account_balance SET balance = balance + 1000 WHERE ACCOUNT_NAME = '张三'; SELECT * FROM account_balance WHERE ACCOUNT_NAME = '张三'; -- 13580.5 -- 事务1:再次查询,并尝试修改 SELECT * FROM account_balance WHERE ACCOUNT_NAME = '张三'; -- 12580.5 UPDATE account_balance SET balance = balance + 1000 WHERE ACCOUNT_NAME = '张三'; -- 请求被阻塞

可以看到:事务2修改数据但未提交之前,事务1查询到的仍是原始数据,且事务1无法修改该行数据。这正是默认隔离级别(读已提交)在起作用。


二、提交事务

2.1 自动提交

达梦支持通过 AUTOCOMMIT 控制是否自动提交:

-- 查看当前提交状态 SQL> SHOW AUTOCOMMIT; -- 开启自动提交 SQL> SET AUTOCOMMIT ON; -- 关闭自动提交 SQL> SET AUTOCOMMIT OFF;

自动提交下的行为演示:

SQL> SET AUTOCOMMIT ON; SQL> SELECT * FROM account_balance WHERE ACCOUNT_NAME = '张三'; -- 14580.50 SQL> UPDATE account_balance SET balance = balance + 1000 WHERE ACCOUNT_NAME = '张三'; SQL> ROLLBACK SQL> SELECT * FROM account_balance WHERE ACCOUNT_NAME = '张三'; -- 15580.50

结论:开启自动提交后,SQL 语句执行完会自动提交,ROLLBACK 无法回滚已提交的修改。

2.2 隐式提交

部分 DDL 语句在执行时会自动引发隐式提交,包括:CREATE、ALTER、TRUNCATE、DROP、GRANT、REVOKE 以及审计设置语句。

-- 会话1:创建表 CREATE TABLE test1(t1 INT, t2 VARCHAR); -- 会话2:在会话1提交之前即可查看成功(DDL 触发了隐式提交) SELECT * FROM test1;

三、事务的保存点

保存点(SAVEPOINT)能够让数据库在事务中进行"部分回滚",而不必回滚整个事务。

-- 对数据做三次修改,分别设置保存点 UPDATE account_balance SET account_name = '张3' WHERE account_name = '张三'; SAVEPOINT one; UPDATE account_balance SET account_name = '李4' WHERE account_name = '李四'; SAVEPOINT two; UPDATE account_balance SET account_name = '王5' WHERE account_name = '王五'; SAVEPOINT three; SELECT * FROM account_balance;

image1.png

-- 根据保存点回滚 ROLLBACK TO SAVEPOINT two; SELECT * FROM account_balance;

image2.png


四、数据库隔离

4.1 并发读现象

在并发场景下,多个事务同时操作同一份数据,可能产生以下现象:

  • 脏读:事务2更新了一行记录但尚未提交,事务1读取了更新后的行;随后事务2回滚,取消了刚才的修改,事务1读到的就是"脏"数据。
  • 不可重复读:事务1读取一行记录,紧接着事务2修改了该行并提交;事务1再次读取时,结果与之前不同。
  • 幻读:不可重复读的一种特殊现象,指同一查询在不同时刻返回了不同的行集合。

4.2 隔离级别

达梦支持三种事务隔离级别,其对各类读现象的影响如下:

隔离级别 脏读 不可重复读 幻读 核心行为
读未提交(Read Uncommitted) 可能 可能 可能 可以读到其他事务尚未提交的数据
读已提交(Read Committed) 不可能 可能 可能 默认级别。只读其他事务已提交的数据,保证一致性
可串行化(Serializable) 不可能 不可能 不可能 最高隔离级别。并发执行结果与串行执行相同,但可能出现"串行化事务被打断"错误

设置隔离级别示例:

-- 会话1:设置隔离级别为读未提交 SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -- 会话2:修改数据 SELECT * FROM account_balance WHERE ACCOUNT_NAME = '张三'; -- 4000 UPDATE account_balance SET balance = balance + 1000 WHERE ACCOUNT_NAME = '张三'; -- 会话1:查看数据(读未提交级别下可读到未提交的数据) SELECT * FROM account_balance WHERE ACCOUNT_NAME = '张三'; -- 5000

五、锁机制

锁是保障数据安全的重要机制,类似互斥锁:让并发的任务在同一时间只有一个能运行,牺牲效率换取数据安全。

5.1 锁模式

锁模式 命令关键字 主要用途
排他锁(X) EXCLUSIVE 独占表,禁止其他任何操作
共享锁(S) SHARE 允许其他事务查询,但禁止修改
意向排他锁(IX) INTENT EXCLUSIVE 表级意向锁,用于行级修改,与其他意向锁兼容
意向共享锁(IS) INTENT SHARE 表级意向锁,用于行级查询

5.2 排他锁

-- 会话1:加入排他锁 LOCK TABLE account_balance IN EXCLUSIVE MODE; UPDATE account_balance SET balance = 2000.00 WHERE account_name = '张三'; -- 会话2:查询数据会一直等待 SELECT * FROM account_balance; -- 会话1:提交并释放锁,会话2的语句才可执行 COMMIT;

5.3 共享锁

-- 会话1:对某些行加共享锁 SELECT * FROM account_balance WHERE account_name = '张三' FOR UPDATE; -- 会话2:可以查询,但不能修改对应行的数据 SELECT * FROM account_balance; UPDATE account_balance SET balance = 3000.00 WHERE account_name = '张三';

5.4 NOWAIT

NOWAIT 让程序在无法立即获得锁时快速报错,而不是一直傻等:

-- 会话1:先锁住表 LOCK TABLE account_balance IN EXCLUSIVE MODE; -- 会话2:尝试加锁并使用 NOWAIT LOCK TABLE account_balance IN EXCLUSIVE MODE NOWAIT; -- 报错:-6409: 第12行附近出现错误: 锁等待

5.5 查询锁与解除锁

查询当前谁在锁表:

SELECT l.TRX_ID, -- 事务ID l.LTYPE, -- 锁类型:OBJECT 或 TID l.LMODE, -- 锁模式:X, S, IX, IS 等 l.BLOCKED, -- 阻塞状态 s.SESS_ID, -- 会话ID(用于终止会话) s.USER_NAME, -- 用户名 s.SQL_TEXT, -- 当前执行的SQL s.STATE -- 会话状态 FROM V$LOCK l LEFT JOIN V$SESSIONS s ON (l.TRX_ID = s.TRX_ID) WHERE l.TABLE_ID = (SELECT ID FROM SYSOBJECTS WHERE NAME = 'ACCOUNT_BALANCE') -- 查找该表相关的锁 AND l.LMODE IS NOT NULL; -- 过滤系统锁

image3.png

必要时可通过会话 ID 强制解除锁:

CALL SP_CLOSE_SESSION(466868576);

六、闪回

当发生误操作(如删错数据、改错内容)并已提交后,闪回(Flashback)允许将数据恢复到过去的某个时间点,而无需从冗长的备份中恢复。

闪回的实现基础是回滚(UNDO)数据——即修改数据前数据库保存的"旧值"记录,闪回正是利用这些历史记录来还原数据。

-- 开启闪回 ALTER SYSTEM SET 'ENABLE_FLASHBACK' = 1; -- 延长闪回时间窗口,单位是秒,默认 90 秒 SP_SET_PARA_DOUBLE_VALUE(1, 'UNDO_RETENTION', 360); -- 查看"过去时"的数据 UPDATE account_balance SET balance = 5000.00 WHERE account_name = '张三'; -- 当前时间为 2026-08-28 16:07:00,查看两分钟前(16:05:00)的数据 SELECT * FROM account_balance WHEN TIMESTAMP '2026-08-28 16:05:00' WHERE account_name = '张三';

image4.png

-- 闪回表 FLASHBACK TABLE account_balance TO TIMESTAMP '2026-08-28 16:10:00'; -- 闪回事务查询 SELECT * FROM V$FLASHBACK_TRX_INFO WHERE COMMIT_TIMESTAMP > '2026-08-28 16:10:00';

总结

  • 事务特性(ACID):原子性、一致性、隔离性、持久性,是事务正确性的基石。
  • 提交方式:自动提交下 ROLLBACK 无法回滚;DDL 语句会触发隐式提交。
  • 保存点:SAVEPOINT + ROLLBACK TO SAVEPOINT 可实现事务内的部分回滚。
  • 隔离级别:默认读已提交;可串行化最严格但代价最高,需按业务权衡。
  • 锁机制:排他锁、共享锁、意向锁各有用途,NOWAIT 可避免长时间锁等待,必要时通过 SP_CLOSE_SESSION 解除锁。
  • 闪回:基于 UNDO 数据,可在误操作提交后快速恢复数据,是应急恢复的利器。
评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服