一、事务基础概念
1.1 什么是事务
-- 事务是一组作为一个单元执行的sql操作
-- 要么全部成功,要么全部失败(acid特性)
1.2 acid特性
原子性(atomicity):事务中的所有操作要么全部完成,要么全部不完成
一致性(consistency):事务必须使数据库从一个一致状态转换到另一个一致状态
隔离性(isolation):并发事务之间互相隔离
持久性(durability):事务提交后,对数据库的修改是永久性的
二、事务基本语法
2.1 显式事务控制
-- 开始事务
begin transaction; -- sql server/postgresql
start transaction; -- mysql
-- 提交事务
commit;
-- 回滚事务
rollback;
-- 设置保存点
savepoint savepoint_name;
-- 回滚到保存点
rollback to savepoint_name;
-- 释放保存点
release savepoint savepoint_name; -- mysql
2.2 隐式事务模式
-- sql server中设置隐式事务
set implicit_transactions on;
-- 执行delete/insert/update时自动开始事务
-- 需要显式commit或rollback
set implicit_transactions off; -- 关闭隐式事务
三、事务隔离级别
3.1 标准隔离级别
-- 查看当前隔离级别(mysql)
select @@transaction_isolation;
-- 设置事务隔离级别
set transaction isolation level read uncommitted;
set transaction isolation level read committed;
set transaction isolation level repeatable read;
set transaction isolation level serializable;
-- 设置会话级别的隔离级别(mysql)
set session transaction isolation level read committed;
-- sql server设置隔离级别
set transaction isolation level snapshot; -- 行版本控制
3.2 隔离级别详解
-- 示例:不同隔离级别的行为
-- 1. read uncommitted(读未提交)
set transaction isolation level read uncommitted;
-- 可能读取到其他事务未提交的数据(脏读)
-- 2. read committed(读已提交)- 多数数据库默认级别
set transaction isolation level read committed;
-- 只能读取已提交的数据,避免脏读
-- 3. repeatable read(可重复读)- mysql默认
set transaction isolation level repeatable read;
-- 同一事务中多次读取相同数据结果一致
-- 4. serializable(可串行化)
set transaction isolation level serializable;
-- 最高隔离级别,完全串行执行
四、事务使用示例
4.1 基本事务示例
-- 银行转账示例
begin transaction;
declare @transfer_amount decimal(10,2) = 100.00;
-- 检查账户余额
if (select balance from accounts where account_id = 1) >= @transfer_amount
begin
-- 从账户1扣款
update accounts
set balance = balance - @transfer_amount
where account_id = 1;
-- 向账户2存款
update accounts
set balance = balance + @transfer_amount
where account_id = 2;
-- 记录交易日志
insert into transaction_log (from_account, to_account, amount, timestamp)
values (1, 2, @transfer_amount, getdate());
print '转账成功';
commit;
end
else
begin
print '余额不足';
rollback;
end
4.2 保存点使用示例
begin transaction;
-- 插入主表数据
insert into orders (customer_id, order_date, total_amount)
values (1, getdate(), 1000.00);
-- 设置保存点
savepoint after_order_insert;
-- 插入订单详情
insert into order_items (order_id, product_id, quantity, price)
values (scope_identity(), 101, 2, 500.00);
-- 检查库存
if (select stock from products where product_id = 101) >= 2
begin
-- 更新库存
update products set stock = stock - 2 where product_id = 101;
commit;
end
else
begin
-- 回滚到保存点(只回滚库存操作)
rollback to after_order_insert;
-- 可以继续其他操作或完全回滚
rollback;
end
4.3 嵌套事务处理
-- sql server嵌套事务示例
begin transaction maintran;
print '主事务开始,@@trancount = ' + cast(@@trancount as varchar);
begin try
-- 第一个操作
insert into table1 (column1) values ('value1');
-- 嵌套事务
save transaction savepoint1; -- sql server使用save transaction
begin try
insert into table2 (column2) values ('value2');
-- 提交嵌套事务(实际只提交到保存点)
end try
begin catch
rollback transaction savepoint1;
print '嵌套事务回滚';
end catch
-- 继续主事务操作
update table3 set column3 = 'updated' where id = 1;
commit transaction;
print '主事务提交';
end try
begin catch
rollback transaction;
print '主事务回滚,错误: ' + error_message();
end catch
五、分布式事务
5.1 两阶段提交(2pc)
-- sql server分布式事务示例
begin distributed transaction;
-- 在本地数据库执行操作
update localdb.dbo.accounts
set balance = balance - 100
where account_id = 1;
-- 在远程数据库执行操作
update remoteserver.remotedb.dbo.accounts
set balance = balance + 100
where account_id = 2;
-- 提交分布式事务
commit transaction;
-- 如果失败,所有参与方都会回滚
-- rollback transaction;
5.2 xa事务(mysql)
-- mysql xa事务
-- 第一阶段:准备
xa start 'transaction1';
update accounts set balance = balance - 100 where id = 1;
update accounts set balance = balance + 100 where id = 2;
xa end 'transaction1';
xa prepare 'transaction1';
-- 第二阶段:提交或回滚
xa commit 'transaction1';
-- 或 xa rollback 'transaction1';
-- 查看xa事务状态
xa recover;
六、事务最佳实践
6.1 事务设计原则
-- 1. 保持事务简短
begin transaction;
-- 只包含必要的操作
-- 避免在事务中包含用户交互
commit;
-- 2. 按顺序访问资源,避免死锁
-- 所有事务按相同顺序访问表
-- 3. 使用合适的隔离级别
set transaction isolation level read committed;
-- 平衡一致性和性能
-- 4. 错误处理
begin transaction;
begin try
-- 业务操作
commit;
end try
begin catch
rollback;
-- 记录错误日志
insert into error_log (error_message, error_time)
values (error_message(), getdate());
end catch
6.2 死锁处理
-- 死锁检测和处理
set deadlock_priority low; -- 设置死锁优先级
set lock_timeout 5000; -- 设置锁超时时间(毫秒)
begin transaction;
begin try
-- 业务逻辑
commit;
end try
begin catch
if error_number() = 1205 -- 死锁错误号
begin
-- 死锁处理逻辑
print '检测到死锁,重试中...';
-- 可以实现重试逻辑
end
else
begin
rollback;
throw; -- 重新抛出错误
end
end catch
七、不同数据库的事务差异
7.1 mysql事务
-- 开启事务(mysql)
start transaction;
-- 或
begin;
-- 设置自动提交
set autocommit = 0; -- 关闭自动提交
set autocommit = 1; -- 开启自动提交(默认)
-- 查看innodb状态(包含事务信息)
show engine innodb status;
-- mysql 8.0+ 支持原子ddl
-- ddl操作在事务中可回滚
7.2 postgresql事务
-- postgresql事务块
begin;
-- 或
start transaction;
-- 设置事务特性
begin transaction
isolation level serializable
read write
deferrable;
-- postgresql支持ddl事务
begin;
create table test (id serial primary key);
alter table test add column name varchar(100);
-- 可以回滚ddl操作
rollback;
7.3 sql server事务
-- sql server事务
begin transaction;
-- 使用事务标记
begin transaction tran1 with mark '重要更新';
-- 查看活动事务
dbcc opentran;
-- 使用行版本控制
alter database mydb set allow_snapshot_isolation on;
alter database mydb set read_committed_snapshot on;
八、性能优化建议
8.1 减少事务开销
-- 1. 批量操作减少事务次数
begin transaction;
-- 批量插入
insert into orders (customer_id, order_date)
select customer_id, getdate()
from #temp_customers;
commit;
-- 2. 适当调整隔离级别
set transaction isolation level read committed;
-- 3. 避免长事务
set lock_timeout 30000; -- 30秒超时
-- 4. 使用表提示控制锁行为
select * from orders with (nolock) -- 脏读,谨慎使用
where order_date > '2024-01-01';
select * from orders with (uplock) -- 更新锁
where order_id = 100;
九、监控和诊断
9.1 监控活动事务
-- sql server
select
session_id,
transaction_id,
name as tran_name,
transaction_begin_time,
transaction_type,
transaction_state
from sys.dm_tran_active_transactions
join sys.dm_tran_session_transactions
on sys.dm_tran_active_transactions.transaction_id =
sys.dm_tran_session_transactions.transaction_id;
-- mysql
select * from information_schema.innodb_trx;
select * from performance_schema.events_transactions_current;
-- postgresql
select * from pg_stat_activity
where state = 'idle in transaction';
十、实战示例:完整的事务处理
-- 电商订单处理事务
create procedure processorder
@customer_id int,
@order_items orderitemtype readonly -- 表值参数
as
begin
set nocount on;
declare @order_id int;
declare @total_amount decimal(10,2) = 0;
begin transaction;
begin try
-- 1. 计算总金额
select @total_amount = sum(price * quantity)
from @order_items;
-- 2. 创建订单
insert into orders (customer_id, order_date, total_amount, status)
values (@customer_id, getdate(), @total_amount, 'pending');
set @order_id = scope_identity();
-- 3. 插入订单详情
insert into order_items (order_id, product_id, quantity, price)
select @order_id, product_id, quantity, price
from @order_items;
-- 4. 更新库存(带锁)
update p
set p.stock = p.stock - oi.quantity,
p.reserved = p.reserved + oi.quantity
from products p with (uplock)
inner join @order_items oi on p.product_id = oi.product_id
where p.stock >= oi.quantity;
-- 5. 扣款
update customers
set balance = balance - @total_amount
where customer_id = @customer_id
and balance >= @total_amount;
-- 6. 更新订单状态
update orders
set status = 'completed'
where order_id = @order_id;
-- 7. 记录日志
insert into order_log (order_id, action, timestamp)
values (@order_id, 'order_completed', getdate());
commit transaction;
print '订单处理成功';
end try
begin catch
rollback transaction;
-- 记录错误
insert into error_log (error_message, error_procedure, error_time)
values (error_message(), error_procedure(), getdate());
throw; -- 重新抛出错误
end catch
end;