深入解析 MySQL 事务:从基础概念到高级应用

深入解析 MySQL 事务:从基础概念到高级应用

一、定义

事务是由一个或多个 SQL 语句组成的独立工作单元,在这个单元中,每个 SQL 语句都是相互依赖的。事务作为一个不可分割的整体存在,要么全部成功,要么全部失败,不可能存在部分成功、部分失败的情况。

简而言之,事务就是一个整体,内部的 SQL 语句要么全部执行成功,要么全部执行失败。一旦某条 SQL 语句执行失败或出错,整个事务将回滚(返回最初状态),所有受到影响的数据将恢复到事务开始前的状态;若所有 SQL 语句执行成功,则事务顺利提交。

在 MySQL 中,数据存储由不同的存储引擎管理,不同存储引擎有各自特点。常见存储引擎有 InnoDB、MyISAM 和 Memory 等。其中,InnoDB 支持事务(transaction),而 MyISAM 和 Memory 等不支持事务。

可以通过 SHOW ENGINES; 语句查看 MySQL 支持的存储引擎。

二、特性(ACID)

事务具有四个关键特性,即原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability),简称 ACID。

  • 原子性(Atomicity):事务是一个不可分割的最小工作单位,事务中的操作要么全部发生,要么全部不发生。
  • 一致性(Consistency):事务必须使数据库从一个一致状态变换到另一个一致状态。例如,李二给王五转账 50 元,要么李二账户减 50 元、王五账户加 50 元,要么都不发生。
  • 隔离性(Isolation):一个事务的执行不能被其他事务干扰,事务内部的操作及使用的数据对并发的其他事务是隔离的。
  • 持久性(Durability):事务一旦提交成功,它对数据库的改变将是永久性的,后续的操作或故障不会影响其结果。

三、分类

事务分为隐式事务和显式事务。DML 语句(INSERT、UPDATE、DELETE)是隐式事务。

隐式事务

没有明显的开启和结束标记,自动提交事务。比如,UPDATE 语句修改数据,相当于一个隐式事务。

举例:张三同学购买一个 CSDN 定制保温杯花 99 元,这相当于一个 UPDATE 语句,减去张三余额中的 99 元:

sql 复制代码
UPDATE accounts SET balance = balance - 99 WHERE name = '张三';

显式事务

具有明显的开启和结束标记。使用显式事务的前提是禁用自动提交功能(将 autocommit 设置为 0)。

查看当前 autocommit 状态:

sql 复制代码
SHOW VARIABLES LIKE 'autocommit';

禁用自动提交:

sql 复制代码
SET autocommit = 0;

四、开启事务

显式事务需要手动开启和提交或回滚。以下是一个使用显式事务的示例:

sql 复制代码
-- 步骤一:开启事务
START TRANSACTION;

-- 步骤二:编写事务中的 SQL 语句(INSERT、UPDATE、DELETE)
-- 例如,实现"李二给王五转账"的事务过程
UPDATE t_account SET balance = balance - 50 WHERE vname = '李二';
UPDATE t_account SET balance = balance + 50 WHERE vname = '王五';

-- 步骤三:结束事务
COMMIT; -- 提交事务
-- ROLLBACK; -- 回滚事务:即事务不执行,恢复到事务执行前的状态

五、并发问题

多个事务并发运行时,如果没有适当的隔离机制,就会发生以下问题:

  • 脏读(Dirty Read):事务 T1 读取了事务 T2 更新但未提交的数据,如果 T2 回滚,T1 读取的数据是无效的。
  • 不可重复读(Non-repeatable Read):事务 T1 读取某字段后,事务 T2 更新了该字段,T1 再次读取时,值不同。
  • 幻读(Phantom Read):事务 T1 读取某表的数据后,事务 T2 向该表插入新数据,T1 再次读取时,发现多了几行数据。

为了避免这些并发问题,MySQL 提供了四种事务隔离级别:

六、隔离级别

MySQL 提供四种事务隔离级别,不同隔离级别对应不同的并发控制程度和性能影响:

  • 读未提交(Read Uncommitted):允许读取未提交的变更,可能出现脏读、不可重复读和幻读。适用于对数据一致性要求低的场景。
  • 读已提交(Read Committed):只能读取已提交的变更,可以避免脏读,但仍可能出现不可重复读和幻读。适用于对数据一致性要求较高,但允许部分并发的场景。
  • 可重复读(Repeatable Read):确保多次读取同一字段的值一致,可以避免脏读和不可重复读,但仍可能出现幻读。MySQL 的默认隔离级别。
  • 串行化(Serializable):完全锁定事务执行期间的数据,避免所有并发问题,但性能较低。适用于对数据一致性要求极高,且允许性能降低的场景。

Oracle 支持两种事务隔离级别:Read Committed 和 Serializable。

Oracle 默认事务隔离级别是:Read Committed。

MySQL 默认事务隔离级别是:Repeatable Read。

事务隔离级别越高,数据一致性越好,但并发性能越差。开发者需要根据具体业务需求选择合适的隔离级别,以在性能和数据一致性之间取得平衡。

七、示例

假设我们有一个简单的银行账户转账系统,包含两个账户表(t_account)。我们希望在两个账户之间进行转账操作时,保证事务的一致性。

1. 创建数据库和表

sql 复制代码
CREATE DATABASE bank;
USE bank;

CREATE TABLE t_account (
    id INT AUTO_INCREMENT PRIMARY KEY,
    vname VARCHAR(50) NOT NULL,
    balance DECIMAL(10, 2) NOT NULL
);

INSERT INTO t_account (vname, balance) VALUES ('李二', 100.00), ('王五', 100.00);

2. 实现转账操作

sql 复制代码
-- 开启事务
START TRANSACTION;

-- 从李二账户扣除 50 元
UPDATE t_account SET balance = balance - 50 WHERE vname = '李二';

-- 向王五账户增加 50 元
UPDATE t_account SET balance = balance + 50 WHERE vname = '王五';

-- 提交事务
COMMIT;

-- 检查结果
SELECT * FROM t_account;

在实际应用中,开发者可以通过编程语言(如 Java、Python)结合数据库驱动(如 JDBC、SQLAlchemy)来管理事务。例如,在 Java 中,可以使用 JDBC 的 Connection 对象的事务管理方法:

java 复制代码
try (Connection conn = dataSource.getConnection()) {
    conn.setAutoCommit(false);

    try (PreparedStatement stmt1 = conn.prepareStatement("UPDATE t_account SET balance = balance - ? WHERE vname = ?")) {
        stmt1.setBigDecimal(1, new BigDecimal("50.00"));
        stmt1.setString(2, "李二");
        stmt1.executeUpdate();
    }

    try (PreparedStatement stmt2 = conn.prepareStatement("UPDATE t_account SET balance = balance + ? WHERE vname = ?")) {
        stmt2.setBigDecimal(1, new BigDecimal("50.00"));
        stmt2.setString(2, "王五");
        stmt2.executeUpdate();
    }

    conn.commit();
} catch (SQLException e) {
    conn.rollback();
    throw e;
}

通过这种方式,可以在应用程序层面确保事务的原子性、一致性、隔离性和持久性(ACID)。

相关推荐
打鱼又晒网18 分钟前
【MySQL】数据库精细化讲解:内置函数知识穿透与深度学习解析
数据库·mysql
大白要努力!23 分钟前
android 使用SQLiteOpenHelper 如何优化数据库的性能
android·数据库·oracle
tatasix1 小时前
MySQL UPDATE语句执行链路解析
数据库·mysql
南城花随雪。1 小时前
硬盘(HDD)与固态硬盘(SSD)详细解读
数据库
儿时可乖了1 小时前
使用 Java 操作 SQLite 数据库
java·数据库·sqlite
懒是一种态度1 小时前
Golang 调用 mongodb 的函数
数据库·mongodb·golang
天海华兮1 小时前
mysql 去重 补全 取出重复 变量 函数 和存储过程
数据库·mysql
gma9992 小时前
Etcd 框架
数据库·etcd
爱吃青椒不爱吃西红柿‍️2 小时前
华为ASP与CSP是什么?
服务器·前端·数据库
Yz98763 小时前
hive的存储格式
大数据·数据库·数据仓库·hive·hadoop·数据库开发