深入理解数据库事务(超详细)

一、事务的介绍

深入理解数据库事务(超详细)_第1张图片

事务是一组操作的集合,事务会把所有操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。 

二、事务的基本操作

2.1  事务操作方式一

深入理解数据库事务(超详细)_第2张图片

 例子:    转账场景(张三向李四转账)

  1. -- 1. 查询张三账户余额
  2. select * from account where name = '张三';
  3. -- 2. 将张三账户余额-1000
  4. update account set money = money - 1000 where name = '张三';
  5. -- 此语句出错后张三钱减少但是李四钱没有增加
  6. 模拟sql语句错误
  7. -- 3. 将李四账户余额+1000
  8. update account set money = money + 1000 where name = '李四';
  9. -- 查看事务提交方式
  10. SELECT @@AUTOCOMMIT;
  11. -- 设置事务提交方式,1为自动提交,0为手动提交,该设置只对当前会话有效
  12. SET @@AUTOCOMMIT = 0;
  13. -- 提交事务
  14. COMMIT;
  15. -- 回滚事务
  16. ROLLBACK;
  17. -- 设置手动提交后上面代码改为:
  18. select * from account where name = '张三';
  19. update account set money = money - 1000 where name = '张三';
  20. update account set money = money + 1000 where name = '李四';
  21. commit;

2.2  事务操作方式二

深入理解数据库事务(超详细)_第3张图片

 开启事务:
  START TRANSACTION 或 BEGIN TRANSACTION;
提交事务:
  COMMIT;
回滚事务:
  ROLLBACK;

操作实例:

  1. start transaction;
  2. select * from account where name = '张三';
  3. update account set money = money - 1000 where name = '张三';
  4. update account set money = money + 1000 where name = '李四';
  5. commit;

2.3  实际开发的案例 

一个实际开发中常见的例子是银行系统中的转账业务。在进行资金转移时,我们需要保证转出和转入两个账户的金额变动是原子性的,要么全部成功,要么全部失败。

以下是一个示例代码片段:

// 假设有两个账户:accountA 和 accountB
String accountA = "A";
String accountB = "B";
double transferAmount = 100.0; // 转账金额

// 获取数据库连接
Connection connection = getConnection();
try {
    connection.setAutoCommit(false); // 设置手动提交事务

    // 查询账户 A 的余额
    double balanceA = queryBalance(accountA);

    // 查询账户 B 的余额
    double balanceB = queryBalance(accountB);

    if (balanceA >= transferAmount) { // 检查账户 A 的余额是否足够
        // 扣除账户 A 的金额
        updateBalance(connection, accountA, balanceA - transferAmount);

        // 增加账户 B 的金额
        updateBalance(connection, accountB, balanceB + transferAmount);

        connection.commit(); // 提交事务
        System.out.println("转账成功!");
    } else {
        System.out.println("转账失败:账户 A 余额不足!");
    }
} catch (SQLException e) {
    connection.rollback(); // 发生异常,回滚事务
    System.out.println("转账失败:" + e.getMessage());
} finally {
    connection.close(); // 关闭数据库连接
}

在上述示例中,我们使用connection.setAutoCommit(false)将自动提交事务的选项关闭,并手动控制事务的提交和回滚。当余额足够时,我们更新账户 A 和账户 B 的余额,并使用connection.commit()提交事务。如果发生异常或余额不足的情况,我们使用connection.rollback()回滚事务。

通过使用事务,我们可以确保转账过程中的数据一致性和可靠性。只有当两个账户的金额都成功更新后,才会执行事务的提交操作,否则会回滚到事务开始前的状态。

这是一个简单的示例,实际应用中可能涉及更多复杂的业务逻辑和错误处理。但这个例子展示了如何在实际开发中使用START TRANSACTIONBEGIN来管理事务,确保转账操作的一致性和可靠性。

三、事务的四大特性

深入理解数据库事务(超详细)_第4张图片

  • 原子性(Atomicity):事务是不可分割的最小操作但愿,要么全部成功,要么全部失败
  • 一致性(Consistency):事务完成时,必须使所有数据都保持一致状态
  • 隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行
  • 持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的

四、并发事务问题

深入理解数据库事务(超详细)_第5张图片

问题 描述
脏读 一个事务读到另一个事务还没提交的数据
不可重复读 一个事务先后读取同一条记录,但两次读取的数据不同
幻读 一个事务按照条件查询数据时,没有对应的数据行,但是再插入数据时,又发现这行数据已经存在

常用的并发控制机制包括:

  1. 锁(Locking):使用锁来控制并发事务对数据的访问和修改,确保在某个事务读取或修改数据时,其他事务不能同时进行相同的操作。

  2. 事务隔离级别(Isolation Level):通过设置不同的事务隔离级别,定义了事务之间的可见性、并发控制的粒度等规则。

  3. 多版本并发控制(MVCC):每个事务在读取数据时,会看到一个特定的版本,而不是直接读取最新的数据。这样可以避免脏读、不可重复读和幻读问题。

  4. 时间戳排序(Timestamp Ordering):使用时间戳来对事务进行排序,根据时间戳来判断事务的执行顺序,确保事务按照正确的顺序读取和修改数据。

实际开发中,为了解决并发事务问题,需要根据具体情况选择适当的并发控制机制和事务隔离级别,并进行合理的设计和优化。同时,也需要进行充分的测试和验证,确保系统在高并发环境下依然能够保持数据的一致性和可靠性。

五、事务的隔离级别

深入理解数据库事务(超详细)_第6张图片

并发事务隔离级别:

隔离级别 脏读 不可重复读 幻读
Read uncommitted(读未提交)
Read committed(读已提交) ×
Repeatable Read(可重复读)(默认的隔离级别) × ×
Serializable(串行化) × × ×
  • √表示在当前隔离级别下该问题会出现
  • Serializable 性能最低;Read uncommitted 性能最高,数据安全性最差

注意:事务隔离级别越高,数据越安全,但是性能越低。


 查看事务隔离级别:
  SELECT @@TRANSACTION_ISOLATION;
设置事务隔离级别:
  SET [ SESSION | GLOBAL ] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE };
     SESSION 是会话级别,表示只针对当前会话有效,GLOBAL 表示对所有会话有效

你可能感兴趣的:(MySQL,数据库)