SQL Server事务 #
一、事务概述 #
1.1 什么是事务 #
事务是一组数据库操作的逻辑单元,这些操作要么全部成功,要么全部失败。
text
事务ACID特性:
├── 原子性(Atomicity)
│ └── 事务是不可分割的工作单位
├── 一致性(Consistency)
│ └── 事务使数据库从一个一致状态变到另一个一致状态
├── 隔离性(Isolation)
│ └── 多个事务并发执行时互不干扰
└── 持久性(Durability)
└── 事务一旦提交,对数据的修改是永久的
1.2 事务控制语句 #
sql
-- 开始事务
BEGIN TRANSACTION;
BEGIN TRAN;
-- 提交事务
COMMIT TRANSACTION;
COMMIT TRAN;
COMMIT;
-- 回滚事务
ROLLBACK TRANSACTION;
ROLLBACK TRAN;
ROLLBACK;
-- 保存点
SAVE TRANSACTION savepoint_name;
-- 回滚到保存点
ROLLBACK TRANSACTION savepoint_name;
二、基本事务 #
2.1 简单事务 #
sql
-- 简单事务
BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT TRANSACTION;
2.2 带错误处理的事务 #
sql
-- 带错误处理的事务
BEGIN TRY
BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT TRANSACTION;
PRINT 'Transaction completed';
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
ROLLBACK TRANSACTION;
PRINT 'Transaction failed: ' + ERROR_MESSAGE();
END CATCH
2.3 嵌套事务 #
sql
-- 嵌套事务
BEGIN TRANSACTION; -- @@TRANCOUNT = 1
BEGIN TRANSACTION; -- @@TRANCOUNT = 2
INSERT INTO users (name) VALUES ('Test');
COMMIT TRANSACTION; -- @@TRANCOUNT = 1
COMMIT TRANSACTION; -- @@TRANCOUNT = 0
-- 注意:嵌套事务的COMMIT只减少@@TRANCOUNT
-- 只有最外层的COMMIT才真正提交
-- 任何ROLLBACK都会回滚所有事务
2.4 保存点 #
sql
-- 使用保存点
BEGIN TRANSACTION;
INSERT INTO users (name) VALUES ('User1');
SAVE TRANSACTION after_user1;
INSERT INTO users (name) VALUES ('User2');
-- 可以回滚到保存点
ROLLBACK TRANSACTION after_user1;
-- 继续其他操作
INSERT INTO users (name) VALUES ('User3');
COMMIT TRANSACTION;
-- 结果:User1和User3被插入,User2被回滚
三、事务隔离级别 #
3.1 隔离级别概述 #
text
SQL Server隔离级别:
├── READ UNCOMMITTED
│ └── 允许脏读
├── READ COMMITTED(默认)
│ └── 避免脏读,可能不可重复读
├── REPEATABLE READ
│ └── 避免脏读和不可重复读,可能幻读
├── SERIALIZABLE
│ └── 完全隔离,避免所有问题
├── SNAPSHOT
│ └── 行版本控制,避免所有问题
└── READ COMMITTED SNAPSHOT
└── 读操作使用行版本
3.2 并发问题 #
text
并发问题:
├── 脏读(Dirty Read)
│ └── 读取未提交的数据
├── 不可重复读(Non-repeatable Read)
│ └── 同一事务中两次读取结果不同
├── 幻读(Phantom Read)
│ └── 同一事务中两次查询返回的行数不同
└── 丢失更新(Lost Update)
└── 两个事务同时更新,一个覆盖另一个
3.3 设置隔离级别 #
sql
-- READ UNCOMMITTED
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
BEGIN TRANSACTION;
SELECT * FROM users; -- 可能读取脏数据
COMMIT;
-- READ COMMITTED(默认)
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN TRANSACTION;
SELECT * FROM users; -- 只读取已提交数据
COMMIT;
-- REPEATABLE READ
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN TRANSACTION;
SELECT * FROM users WHERE id = 1; -- 锁定读取的行
-- 其他事务无法修改这些行
SELECT * FROM users WHERE id = 1; -- 保证相同结果
COMMIT;
-- SERIALIZABLE
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
SELECT * FROM users WHERE status = 1; -- 锁定范围
-- 其他事务无法插入status=1的新行
COMMIT;
-- SNAPSHOT
SET TRANSACTION ISOLATION LEVEL SNAPSHOT;
BEGIN TRANSACTION;
SELECT * FROM users; -- 读取事务开始时的数据版本
COMMIT;
3.4 隔离级别对比 #
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 并发性能 |
|---|---|---|---|---|
| READ UNCOMMITTED | 可能 | 可能 | 可能 | 最高 |
| READ COMMITTED | 避免 | 可能 | 可能 | 高 |
| REPEATABLE READ | 避免 | 避免 | 可能 | 中 |
| SERIALIZABLE | 避免 | 避免 | 避免 | 低 |
| SNAPSHOT | 避免 | 避免 | 避免 | 高 |
四、锁机制 #
4.1 锁类型 #
text
SQL Server锁类型:
├── 共享锁(S,Shared)
│ └── 用于读操作
├── 排他锁(X,Exclusive)
│ └── 用于写操作
├── 更新锁(U,Update)
│ └── 用于更新操作
├── 意向锁(I,Intent)
│ └── 表示更细粒度的锁
├── 架构锁(Sch)
│ └── 用于DDL操作
└── 大容量更新锁(BU)
└── 用于批量复制
4.2 锁粒度 #
text
锁粒度(从大到小):
├── 数据库锁(Database)
├── 表锁(Table)
├── 页锁(Page)
├── 行锁(Row)
└── 键锁(Key)
4.3 查看锁 #
sql
-- 查看当前锁
SELECT
request_session_id AS 'Session',
resource_type AS 'Type',
resource_database_id AS 'DB',
resource_associated_entity_id AS 'Resource',
request_mode AS 'Mode',
request_status AS 'Status'
FROM sys.dm_tran_locks
WHERE resource_type = 'OBJECT';
-- 使用sp_lock(已废弃)
EXEC sp_lock;
-- 使用sp_who2查看阻塞
EXEC sp_who2;
4.4 锁提示 #
sql
-- NOLOCK:不获取锁,允许脏读
SELECT * FROM users WITH (NOLOCK);
-- UPDLOCK:获取更新锁
SELECT * FROM users WITH (UPDLOCK) WHERE id = 1;
-- XLOCK:获取排他锁
SELECT * FROM users WITH (XLOCK) WHERE id = 1;
-- ROWLOCK:使用行锁
UPDATE users WITH (ROWLOCK) SET name = 'Test' WHERE id = 1;
-- TABLOCK:使用表锁
SELECT * FROM users WITH (TABLOCK);
-- HOLDLOCK:持有锁到事务结束
SELECT * FROM users WITH (HOLDLOCK) WHERE id = 1;
五、死锁 #
5.1 死锁示例 #
sql
-- 会话1
BEGIN TRANSACTION;
UPDATE users SET name = 'Test1' WHERE id = 1;
-- 等待会话2释放id=2的锁
UPDATE users SET name = 'Test1' WHERE id = 2;
COMMIT;
-- 会话2
BEGIN TRANSACTION;
UPDATE users SET name = 'Test2' WHERE id = 2;
-- 等待会话1释放id=1的锁
UPDATE users SET name = 'Test2' WHERE id = 1;
COMMIT;
-- 死锁发生!
5.2 查看死锁 #
sql
-- 启用死锁跟踪
DBCC TRACEON (1222, -1); -- 输出死锁信息到错误日志
DBCC TRACEON (1204, -1); -- 输出死锁格式化信息
-- 使用扩展事件
-- SSMS: Management → Extended Events → system_health
-- 查看死锁图
SELECT
xed.value('@timestamp', 'datetime') AS CreationTime,
xed.query('.') AS DeadlockGraph
FROM (
SELECT CAST([target_data] AS XML) AS TargetData
FROM sys.dm_xe_session_targets AS st
INNER JOIN sys.dm_xe_sessions AS s ON s.address = st.event_session_address
WHERE s.name = N'system_health'
) AS Data
CROSS APPLY TargetData.nodes('RingBufferTarget/event[@name="xml_deadlock_report"]/data/value') AS XEventData(xed);
5.3 避免死锁 #
text
避免死锁策略:
├── 按相同顺序访问对象
├── 保持事务简短
├── 使用较低的隔离级别
├── 使用适当的锁提示
├── 避免用户交互
└── 设置死锁优先级
sql
-- 设置死锁优先级
SET DEADLOCK_PRIORITY LOW; -- 低优先级,容易被牺牲
SET DEADLOCK_PRIORITY NORMAL; -- 正常优先级
SET DEADLOCK_PRIORITY HIGH; -- 高优先级
-- 设置锁超时
SET LOCK_TIMEOUT 5000; -- 5秒超时
六、阻塞 #
6.1 查看阻塞 #
sql
-- 查看阻塞
SELECT
t1.resource_type AS 'Resource Type',
OBJECT_NAME(t1.resource_associated_entity_id) AS 'Object',
t1.request_session_id AS 'Blocked Session',
t2.blocking_session_id AS 'Blocking Session',
t2.wait_type AS 'Wait Type',
t2.wait_time AS 'Wait Time',
t2.status AS 'Status'
FROM sys.dm_tran_locks t1
JOIN sys.dm_exec_requests t2 ON t1.request_session_id = t2.session_id
WHERE t2.blocking_session_id > 0;
-- 使用sp_who2
EXEC sp_who2 'active';
-- 使用sp_whoisactive(第三方工具)
-- EXEC sp_whoisactive;
6.2 解决阻塞 #
sql
-- 终止阻塞会话
KILL 53; -- 53是会话ID
-- 设置锁超时
SET LOCK_TIMEOUT 30000; -- 30秒
-- 使用NOLOCK提示
SELECT * FROM users WITH (NOLOCK);
七、事务最佳实践 #
7.1 设计原则 #
text
事务设计原则:
├── 保持事务简短
├── 避免用户交互
├── 使用适当的隔离级别
├── 按相同顺序访问资源
├── 使用错误处理
├── 避免长事务
└── 及时提交或回滚
7.2 性能优化 #
sql
-- 使用SET XACT_ABORT ON
SET XACT_ABORT ON; -- 出错时自动回滚
BEGIN TRANSACTION;
-- 操作
COMMIT TRANSACTION;
-- 使用适当的隔离级别
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 使用锁提示
SELECT * FROM users WITH (NOLOCK);
八、总结 #
事务要点:
| 特性 | 说明 |
|---|---|
| ACID | 原子性、一致性、隔离性、持久性 |
| 隔离级别 | 控制并发访问 |
| 锁机制 | 保护数据一致性 |
| 死锁 | 循环等待导致 |
| 阻塞 | 锁等待导致 |
最佳实践:
- 保持事务简短
- 使用适当的隔离级别
- 添加错误处理
- 按相同顺序访问资源
- 设置合理的锁超时
下一步,让我们学习用户权限管理!
最后更新:2026-03-27