事务具备四大特性,即常说的 ACID:
开启事务并查看当前活动事务(一般来说,开启一个会话窗口即开启了一个新事务):
-- 开启事务
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无法修改该行数据。这正是默认隔离级别(读已提交)在起作用。
达梦支持通过 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无法回滚已提交的修改。
部分 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;
-- 根据保存点回滚
ROLLBACK TO SAVEPOINT two;
SELECT * FROM account_balance;
在并发场景下,多个事务同时操作同一份数据,可能产生以下现象:
达梦支持三种事务隔离级别,其对各类读现象的影响如下:
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 核心行为 |
|---|---|---|---|---|
| 读未提交(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
锁是保障数据安全的重要机制,类似互斥锁:让并发的任务在同一时间只有一个能运行,牺牲效率换取数据安全。
| 锁模式 | 命令关键字 | 主要用途 |
|---|---|---|
| 排他锁(X) | EXCLUSIVE |
独占表,禁止其他任何操作 |
| 共享锁(S) | SHARE |
允许其他事务查询,但禁止修改 |
| 意向排他锁(IX) | INTENT EXCLUSIVE |
表级意向锁,用于行级修改,与其他意向锁兼容 |
| 意向共享锁(IS) | INTENT SHARE |
表级意向锁,用于行级查询 |
-- 会话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;
-- 会话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 = '张三';
NOWAIT 让程序在无法立即获得锁时快速报错,而不是一直傻等:
-- 会话1:先锁住表
LOCK TABLE account_balance IN EXCLUSIVE MODE;
-- 会话2:尝试加锁并使用 NOWAIT
LOCK TABLE account_balance IN EXCLUSIVE MODE NOWAIT;
-- 报错:-6409: 第12行附近出现错误: 锁等待
查询当前谁在锁表:
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; -- 过滤系统锁
必要时可通过会话 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 = '张三';
-- 闪回表
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';
ROLLBACK 无法回滚;DDL 语句会触发隐式提交。SAVEPOINT + ROLLBACK TO SAVEPOINT 可实现事务内的部分回滚。NOWAIT 可避免长时间锁等待,必要时通过 SP_CLOSE_SESSION 解除锁。文章
阅读量
获赞
