数据库事务是数据库中一个操作单元,包含一组被整体执行和写入的多个数据操作。

这些操作可以包括创建(Create)、读取(Read)、更新(Update)或删除(Delete)操作。

事务过程中的数据处于不一致的状态,因为事务正在对数据库进行修改。一旦事务中的所有操作被提交(Commit),数据库就会恢复到一致的状态。

成功的事务意味着所有执行的操作都已被提交。

数据库事务在确保多个操作同时执行时数据库的一致性方面非常重要。它还提供了一种恢复由于操作失败或误操作导致数据更改的方法。


MySQL及其事务支持概述

MySQL数据库支持通过提供相关语句的形式来实现数据库事务。MySQL支持以下内置查询语句:

  • START TRANSACTION / BEGIN: 触发事务的开始。
  • COMMIT: 提交事务,使得对数据库产生的修改变为永久性。你可以通过以下查询设置数据库自动提交更改:
SET autocommit = 1;
  • SET: 允许设置事务提交方式,可以启用操作的自动提交,也可以禁用自动提交。即,只有当你调用COMMIT查询后操作才会提交。
/*禁用自动提交*/
SET autocommit = 0;
/*或者*/
SET autocommit = OFF;

/*启用自动提交每一个操作*/
SET autocommit = 1;
/*或者*/
SET autocommit = ON;
  • ROLLBACK: 可用于撤销对数据库所做的更改,从而将数据库返回到操作之前的状态(最近的提交状态)。

事务的ACID特性

ACID表示原子性(Atomicity)一致性(Consistency)隔离性(Isolation)持久性(Durability)。我们来逐个了解每个特性在事务中的含义。


原子性(Atomicity)

原子性意味着在数据库事务中发生的所有更改会被视为一个整体。这意味着在试图修改数据库时,所有更改要么同时发生,要么完全不发生。

就好比你和你的团队在构建一个应用程序。如果一个人写了一行代码,然后另一人删除了这一行代码,就好像什么都没有发生过。但如果每个人都添加了不同的代码,而且没有人删除代码,那么代码库就会逐渐变大。


一致性(Consistency)

一致性表示数据库中存储的数据始终处于有效和一致的状态。例如,如果数据库包含任何约束(如主键、外键等),则这些数据必须遵循相关约束规则。

举个例子,假设某个表有一个规则,规定特定列必须是整数值。一致性确保这些规则始终得到遵守,插入到该列中的数据只能是整型数据类型。


隔离性(Isolation)

隔离性是指多个事务可以在不互相干扰的情况下执行。事务的隔离级别决定了一个事务所做的更改对其他事务的可见性。

MySQL支持以下隔离级别:

  1. READ UNCOMMITTED: 在这个隔离级别(最低级别),一个事务可以读取其他事务尚未提交的数据。这意味着,其他事务可以修改当前事务正在读取的数据,但这些更改可能直到操作完成后才可见。
  2. READ COMMITTED: 在次低的隔离级别下,一个事务只能读取其他事务已经提交的数据。其他事务可以修改当前事务正在读取的数据,但这些更改要等到修改后的事务提交后才会可见。
  3. REPEATABLE READ: 这是更高级的隔离级别。在此级别,一个事务只能读取其他事务已经提交的数据,且会限制其他事务修改当前事务正在读取的数据。这意味着,即使其他事务提交了更改,如果某个事务再次执行SELECT语句,它会依旧看到同一数据。
  4. SERIALIZABLE: 这是最高的隔离级别。在此级别,一个事务只能读取其他事务已经提交的数据,并且防止其他事务修改该事务所读取的数据,以及添加新行(这些新行对当前事务不可见)。

默认情况下,MySQL使用READ COMMITTED隔离级别。然而,可以通过SET TRANSACTION ISOLATION LEVEL语句来改变隔离级别。


持久性(Durability)

持久性确保即使发生意外情况(例如故障或断电),事务的更改也会保存在数据库中。一旦事务被提交,其更改必须永久存储。

MySQL通过写前日志(Write-Ahead Logging, WAL)机制保证持久性。这种技术在对数据库进行修改之前,会先将事务日志写入磁盘。

该日志充当数据库的“路线图”,包含将在意外系统故障时如何重新进行更改的信息。因此数据库可以从日志中恢复,并重放事务中所做的更改,以确保数据一致性。

需要注意的是,虽然写前日志可能影响性能,但能保证数据安全却是值得付出的代价。


MySQL事务中的锁和并发控制

锁定是为了防止竞争条件的一种技术。竞争条件指的是多个事务尝试同时访问相同数据的情况。

MySQL使用以下几种锁来控制事务中的数据访问:

  1. 共享锁(Shared Locks): 允许多个事务同时读取相同数据,但禁止任何事务对数据进行修改。
  2. 独占锁(Exclusive Locks): 防止不同事务同时读取或修改相同数据。
  3. 意向锁(Intent Locks): 用于标志一个事务计划读取或写入某部分数据。
  4. 行级锁(Row-level Locks): 允许事务仅锁定它需要访问的特定行,而不是锁定整个表。

并发是允许多个事务同时运行,并且不会干扰彼此数据的方法。

MySQL使用多版本并发控制(MVCC, Multi-Version Concurrency Control)机制,允许多个事务同时读取和写入相同的数据而不会冲突。

这如何实现呢?每个事务在开始时会“捕获”将要修改的数据,并将其更改写入完全不同的版本,这样其他事务可以继续使用原始版本的数据,不会发生冲突。

要实现高并发,务必保持事务尽可能短,并避免长期运行的事务,以免长时间占用锁。


如何在MySQL中创建与使用事务

进行事务操作的第一步是通过START TRANSACTION语句启动一个事务。示例如下:

START TRANSACTION;
    INSERT INTO users (name, email) VALUES ('John Doe', 'johndoe@example.com');
    UPDATE accounts SET balance = SUM(balance) WHERE name = 'John Doe';

在这个例子中,用START TRANSACTION语句开启一个新事务。之后的两个语句 (INSERTUPDATE) 都是在此事务内执行的。

为了使更改永久保存,我们需要通过COMMIT语句提交更改:

START TRANSACTION;
    INSERT INTO users (name, email) VALUES ('John Doe', 'johndoe@example.com');
    UPDATE accounts SET balance = SUM(balance) WHERE name = 'John Doe';
COMMIT;

如果在事务过程中出现错误,想撤销更改,可以使用ROLLBACK语句。事务会被回滚,插入和更新语句不再执行,数据库数据不会发生改变。

START TRANSACTION;
    INSERT INTO users (name, email) VALUES ('John Doe', 'johndoe@example.com');
    UPDATE accounts SET balance = SUM(balance) WHERE user_id=15;
ROLLBACK;

如何在MySQL事务中使用InnoDB存储引擎

InnoDB是MySQL的一个存储引擎,提供了许多可提升数据库性能的功能,例如支持组装和执行多个SQL语句、加密数据、创建/删除索引而不影响数据库性能、良好的CPU及大数据处理能力等等。

要在MySQL中使用InnoDB进行事务操作,需要确保表使用的是InnoDB存储引擎。可以通过运行下面的查询来检查:

SHOW TABLE STATUS FROM your_database_name;

你可以通过修改my.cnf配置文件设置默认存储引擎为InnoDB,或者运行以下命令:

SET storage_engine=InnoDB;

如何处理事务中的错误和异常

在事务中处理错误和异常至关重要。处理错误的一种常见方法是在MySQL中使用SIGNALRESIGNAL语句,并结合异常处理。

以下是用TRY-CATCH结构处理事务异常的一个示例:

START TRANSACTION;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
    ROLLBACK;
UPDATE accounts SET balance = 5000 WHERE user_id = 1;
UPDATE accounts SET balance = 1000 WHERE user_id = 2;
IF (SELECT balance FROM accounts WHERE user_id = 1) < 0 THEN
    SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Insufficient balance';
END IF;
COMMIT;

如果在事务中发生了异常,将使用ROLLBACK撤销事务内的所有更改。SIGNAL语句可以用于在事务中自定义异常消息。

一旦事务内发生异常,不管是否处理,事务中的所有更改都会被回滚。


如何在MySQL事务中使用保存点(Savepoints)

在事务中使用SAVEPOINT语句可以设置保存点(savepoint),以允许事务回滚到特定的时间点,而不是回滚整个事务。

例如:

START TRANSACTION;
SAVEPOINT my_savepoint;
UPDATE accounts SET balance = 5000 WHERE user_id = 1;
UPDATE accounts SET balance = 1000 WHERE user_id = 2;
IF (SELECT balance FROM accounts WHERE user_id = 1) < 0 THEN
    ROLLBACK TO SAVEPOINT my_savepoint;
END IF;
COMMIT;

SAVEPOINT语句定义了一个命名为my_savepoint的保存点。如果在事务中捕获到异常,可以用ROLLBACK TO SAVEPOINT my_savepoint撤销保存点后的更改,而保留保存点之前的更改。



什么是数据库事务,为什么它重要?插图

关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台

除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接

本文链接:http://folen.top/2025/11/05/what-is-a-database-transaction/