sql事务详细使用详解

一、事务基础概念

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;
相关推荐
「光与松果」8 小时前
Oracle中v$session视图用法
数据库·oracle
木辰風9 小时前
PLSQL自定义自动替换(AutoReplace)
java·数据库·sql
无限码力9 小时前
华为OD技术面真题 - 数据库MySQL - 3
数据库·mysql·华为od·八股文·华为od技术面八股文
heartbeat..9 小时前
Redis 中的锁:核心实现、类型与最佳实践
java·数据库·redis·缓存·并发
Prince-Peng9 小时前
技术架构系列 - 详解Redis
数据结构·数据库·redis·分布式·缓存·中间件·架构
虾说羊9 小时前
redis中的哨兵机制
数据库·redis·缓存
_F_y9 小时前
MySQL视图
数据库·mysql
2301_790300969 小时前
Python单元测试(unittest)实战指南
jvm·数据库·python
九章-9 小时前
一库平替,融合致胜:国产数据库的“统型”范式革命
数据库·融合数据库
2401_8384725110 小时前
使用Scikit-learn构建你的第一个机器学习模型
jvm·数据库·python