1. 为什么需要数据库事务?

想象你在银行转账的场景:A账户转100元给B账户。如果扣款成功但存款失败,整个业务就会产生数据混乱。事务(Transaction)正是为了解决这类"要么全做,要么不做"的场景而生,它通过ACID特性(原子性、一致性、隔离性、持久性)确保数据操作的完整性。

2. 搭建开发环境

2.1 技术栈说明

本教程采用:

  • .NET 6.0
  • MySqlConnector 2.3.0
  • MySQL 8.0

2.2 安装NuGet包

dotnet add package MySqlConnector

3. 基础事务处理示例

using MySqlConnector;

public class TransactionDemo
{
    public static void BasicTransaction()
    {
        // 创建连接字符串(根据实际配置修改)
        var connectionString = "Server=localhost;Database=testdb;Uid=root;Pwd=123456;";
        
        using var connection = new MySqlConnection(connectionString);
        connection.Open();
        
        // 开启事务
        using var transaction = connection.BeginTransaction();
        
        try
        {
            // 第一条更新命令
            var cmd1 = new MySqlCommand(
                "UPDATE accounts SET balance = balance - 100 WHERE id = 1",
                connection,
                transaction);
            cmd1.ExecuteNonQuery();

            // 第二条更新命令
            var cmd2 = new MySqlCommand(
                "UPDATE accounts SET balance = balance + 100 WHERE id = 2",
                connection,
                transaction);
            cmd2.ExecuteNonQuery();

            // 提交事务
            transaction.Commit();
            Console.WriteLine("事务执行成功");
        }
        catch (Exception ex)
        {
            // 回滚事务
            transaction.Rollback();
            Console.WriteLine($"事务回滚,原因:{ex.Message}");
        }
    }
}

代码解析:

  1. BeginTransaction() 显式开启事务
  2. 所有数据库操作使用同一个事务对象
  3. 异常捕获时执行回滚
  4. 无异常时手动提交事务

4. 进阶事务处理:保存点

public static void SavepointTransaction()
{
    using var connection = new MySqlConnection(connectionString);
    connection.Open();
    
    using var transaction = connection.BeginTransaction();
    
    try
    {
        // 第一步操作:创建订单
        var createOrder = new MySqlCommand(
            "INSERT INTO orders (user_id, amount) VALUES (1, 100)",
            connection,
            transaction);
        createOrder.ExecuteNonQuery();
        
        // 设置保存点
        transaction.Save("AfterCreateOrder");

        // 第二步操作:扣减库存
        var updateStock = new MySqlCommand(
            "UPDATE products SET stock = stock - 1 WHERE id = 123",
            connection,
            transaction);
        int affected = updateStock.ExecuteNonQuery();
        
        if (affected == 0)
        {
            // 回滚到保存点
            transaction.Rollback("AfterCreateOrder");
            Console.WriteLine("库存扣减失败,回滚订单创建操作");
        }
        else
        {
            transaction.Commit();
            Console.WriteLine("所有操作成功完成");
        }
    }
    catch
    {
        transaction.Rollback();
        Console.WriteLine("整体事务回滚");
    }
}

5. 关联技术:ADO.NET核心对象

  • MySqlConnection:数据库物理连接
  • MySqlCommand:执行SQL命令
  • MySqlTransaction:事务控制对象
  • MySqlDataReader:顺序读取查询结果

6. 应用场景分析

6.1 典型使用场景

  • 金融交易系统(转账、支付)
  • 电商库存管理
  • 票务系统的座位锁定
  • 批量数据处理
  • 分布式系统补偿机制

6.2 不适用场景

  • 单条简单查询
  • 日志记录等非关键数据
  • 实时性要求极高的操作
  • 超大事务(处理超过10万条记录)

7. 技术优缺点对比

7.1 优势特性

  • 数据一致性保障
  • 完善的错误恢复机制
  • 支持保存点嵌套
  • 与C#语言深度集成
  • 良好的性能表现(相比ORM)

7.2 潜在缺陷

  • 需要手动管理连接和事务
  • 长期持有事务影响并发性能
  • 分布式事务支持有限
  • 代码复杂度相对较高

8. 开发注意事项

  1. 连接管理:确保及时关闭连接(推荐使用using语句)
  2. 超时设置:合理配置CommandTimeout(建议5-30秒)
  3. 隔离级别:根据业务需求选择合适的级别
  4. 异常处理:捕获特定异常类型(MySqlException)
  5. 性能优化:事务范围最小化原则
  6. 重试机制:对可重试异常实现自动重试
  7. 日志记录:详细记录事务开始/提交/回滚信息

9. 最佳实践总结

  1. 优先使用using语句管理资源
  2. 事务内避免长时间等待
  3. 重要操作添加保存点
  4. 使用异步方法提升吞吐量
  5. 定期检查未提交的事务
  6. 结合连接池优化性能
  7. 编写事务单元测试用例