事务(Transaction)是数据库管理系统(DBMS)中的一个核心概念。MySQL 事务是指一组数据库操作,作为一个整体进行处理,确保要么全部成功,要么全部失败。
事务可以保证数据库的一致性、隔离性、持久性,避免在多个操作中出现部分成功、部分失败的情况。MySQL 使用 ACID 属性来保证事务的正确性和稳定性。
思考:我去银行给朋友汇款,我卡上有100元,朋友卡上50元。我给朋友转账50元,如果我的钱刚扣,而朋友的钱又没加时,网线断了, 怎么办?
数据库事务的概念与技术发展经历了多个阶段,随着计算机科学和数据库技术的进步,事务管理系统也不断演化。以下是数据库事务发展历程的主要阶段:
事务的概念最早可以追溯到20世纪70年代,随着关系型数据库理论的提出而逐步发展。E.F. Codd 在其关系模型的论文中,强调了数据库需要支持一致性和完整性。数据库事务概念的初步提出是为了解决数据库操作的原子性和一致性问题,尤其是在多用户并发环境下如何保证数据的正确性。
E.F. Codd
Jim Gray
在70年代末到80年代初期,随着关系型数据库管理系统的逐步推广,事务的管理开始成为一个重要的研究方向和实践需求。早期的数据库系统,如 IBM's DB2、Ingres 和 Oracle,开始加入事务处理功能。
随着并发控制的需求日益增加,事务隔离级别的设计和实现成为数据库管理系统中的重要组成部分。
随着互联网和分布式计算的快速发展,数据库事务的处理面临着更大的挑战。事务不仅需要在单一数据库内保证一致性,还需要在跨数据库或跨节点的分布式环境中进行协调。
随着云计算和微服务架构的广泛采用,数据库事务在架构上经历了进一步的演变,特别是在大规模分布式系统和无服务器架构下。
数据库事务的研究与技术发展依然在不断进步,以下是一些未来的趋势:
为了保证事务的可靠性,MySQL 遵循 ACID 原则,即原子性、一致性、隔离性和持久性:
其中:原子性和一致性由undolog实现;隔离性由mvcc实现;持久性由redeolog实现 举个例子: A向B转账500,转账成功,A扣除500元,B增加500元,原子操作体现在要么都成功,要么都失败 在转账的过程中,数据要一致,A扣除了500,B必须增加500 在转账的过程中,隔离性体现在A向B转账,不能受其他事务干扰 在转账的过程中,持久性体现在事务提交后,要把数据持久化(可以说是落盘操作)
MySQL 允许设置事务的隔离级别,以控制事务之间的相互影响。隔离级别决定了一个事务对其他事务操作的可见性和干扰程度。MySQL 支持以下四种隔离级别:
你可以通过以下 SQL 查询当前事务的隔离级别:
SELECT @@transaction_isolation;
你也可以设置事务的隔离级别,使用以下语句:
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
MySQL 事务中,锁是确保数据一致性和隔离性的关键。MySQL 支持多种类型的锁,常见的锁有:
MySQL 使用事务日志来保证数据的持久性和恢复能力。常见的日志包括:
在 MySQL 中,默认情况下,每个 SQL 语句都是一个独立的事务。当执行一个 SQL 语句时,它会自动提交(即自动执行 COMMIT
)。但是,如果你显式地使用 START TRANSACTION
或 BEGIN
开始事务,则需要显式地使用 COMMIT
或 ROLLBACK
来提交或回滚事务。
如果要关闭自动提交,可以使用以下语句:
SET autocommit = 0;
要启用自动提交,可以使用:
SET autocommit = 1;
START TRANSACTION
或BEGIN
语句显式地开始一个事务,使用COMMIT
提交事务,或者使用ROLLBACK
回滚事务。autocommit=1
),那么每个SQL语句都会被当作一个独立的事务,执行后立即提交。
在 MySQL 中,事务回滚会撤销所有未提交的事务修改。数据库会将事务的修改操作记录到 undo log,在回滚时,通过 undo log 撤销已执行的操作。
MySQL 事务允许设置保存点(Savepoint),这是一个在事务中设置的检查点。事务可以在保存点处回滚,而不是回滚整个事务。这有助于在事务执行过程中进行部分撤销。
MySQL 使用锁和事务隔离级别来处理并发事务时的数据访问问题。在多事务并发的情况下,可能会发生 死锁(deadlock),即两个或多个事务相互等待对方释放锁,导致所有事务都无法继续执行。
InnoDB 存储引擎是 MySQL 默认的事务存储引擎,支持 ACID 属性并通过行级锁和多版本并发控制(MVCC)来提高事务的隔离性。InnoDB 存储引擎的特点包括:
在 MySQL 中,通常通过以下 SQL 语句来管理事务:
MySQL 提供了几种 SQL 语句来控制事务的执行:
START TRANSACTION
或 BEGIN
来显式地开始一个事务。
START TRANSACTION; -- 或者 BEGIN;COMMIT
来提交事务,将事务中的所有操作持久化到数据库中。
COMMIT;ROLLBACK
来回滚事务,撤销事务中的所有操作,数据恢复到事务开始前的状态。
ROLLBACK;SAVEPOINT
允许在事务中设置保存点,可以在某个特定位置回滚到这个保存点。
SAVEPOINT savepoint_name;ROLLBACK TO SAVEPOINT
回滚到某个保存点,而不是回滚整个事务。
ROLLBACK TO SAVEPOINT savepoint_name;RELEASE SAVEPOINT
释放保存点。
RELEASE SAVEPOINT savepoint_name;
这里完整地展示 MySQL 中的事务操作,演示如何在银行转账场景下使用这些操作。首先初始化相关数据:包括在 accounts
表中插入账户余额数据,以及在 transactions
表中插入一些初始的交易记录。这样能够确保转账操作有实际数据支持。
以下是完整的 SQL 示例,包括账户数据的初始化、转账操作的执行、以及在不同情形下使用事务(提交、回滚等)的操作。
首先,我们创建 accounts
和 transactions
两个表,accounts
表用来存储账户信息,transactions
表用来存储交易记录。
-- 创建 accounts 表
CREATE TABLE accounts (
account_id INT PRIMARY KEY,
balance DECIMAL(10, 2) -- 账户余额
);
-- 创建 transactions 表
CREATE TABLE transactions (
transaction_id INT AUTO_INCREMENT PRIMARY KEY,
account_id INT,
amount DECIMAL(10, 2),
transaction_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (account_id) REFERENCES accounts(account_id)
);
接着,我们插入一些初始的账户数据,为转账操作提供支持。
-- 插入账户数据
INSERT INTO accounts (account_id, balance) VALUES (1, 500.00); -- 账户 A,余额 500
INSERT INTO accounts (account_id, balance) VALUES (2, 300.00); -- 账户 B,余额 300
假设我们要从账户 A 向账户 B 转账 100 元。以下是一个成功的转账操作流程:
-- 开始事务
START TRANSACTION;
-- 从账户 A 提款 100
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
-- 向账户 B 存款 100
UPDATE accounts SET balance = balance + 100 WHERE account_id = 2;
-- 记录这笔交易:账户 A 提款
INSERT INTO transactions (account_id, amount) VALUES (1, -100);
-- 记录这笔交易:账户 B 存款
INSERT INTO transactions (account_id, amount) VALUES (2, 100);
-- 提交事务,确保所有操作成功
COMMIT;
如果在某个步骤中发生了错误(比如账户 B 不存在),我们希望回滚所有操作,确保数据库状态不会被部分修改。以下是模拟账户 B 不存在的情况。
-- 开始事务
START TRANSACTION;
-- 从账户 A 提款 100
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
-- 假设账户 B 不存在,更新失败
UPDATE accounts SET balance = balance + 100 WHERE account_id = 999; -- 假设账户 999 不存在
-- 如果有错误,回滚所有操作
ROLLBACK;
在上面的例子中,由于账户 B(ID 为 999)不存在,第二条 UPDATE
语句将不会更新任何数据。事务会执行回滚操作,恢复事务开始前的数据状态,避免账户 A 的余额被错误地扣除。
如果我们想在一个事务中只回滚部分操作,而不是全部操作,可以使用保存点(Savepoint)。保存点允许你在事务的过程中设置检查点,然后根据需要回滚到某个特定的保存点。
-- 开始事务
START TRANSACTION;
-- 设置保存点:记录账户 A 更新之前的状态
SAVEPOINT before_account_a_update;
-- 从账户 A 提款 100
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
-- 设置另一个保存点:记录账户 B 更新之前的状态
SAVEPOINT before_account_b_update;
-- 向账户 B 存款 100
UPDATE accounts SET balance = balance + 100 WHERE account_id = 2;
-- 假设发生了错误,回滚到账户 A 更新前的状态
ROLLBACK TO SAVEPOINT before_account_a_update;
-- 提交事务
COMMIT;
在这个示例中:
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
时,系统会设置一个保存点 before_account_a_update
。before_account_b_update
。ROLLBACK TO SAVEPOINT
回滚到某个保存点。在这个例子中,我们回滚到 before_account_a_update
,撤销对账户 A 的修改,但不会撤销账户 B 的存款操作。
MySQL 支持不同的事务隔离级别,以控制事务之间的并发行为。以下是如何设置事务的隔离级别:
-- 设置事务的隔离级别为 READ COMMITTED
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 开始事务
START TRANSACTION;
-- 执行操作
UPDATE accounts SET balance = balance - 50 WHERE account_id = 1;
-- 提交事务
COMMIT;
常见的事务隔离级别:
通过以上步骤,我们演示了如何在 MySQL 中使用事务进行银行转账操作。关键的步骤包括:
通过这些功能,MySQL 提供了强大的事务支持,确保数据库在并发访问和多步骤操作中的一致性和可靠性。
在实际开发中,往往使用框架提供的事务支持,简化在应用中管理数据库事务的复杂性。后续将深入分析Java Spring框架,手撕源码,看Spring创始人,音乐学博士学位获得者,Rod Johnson,二十多年前通过精妙的设计,巧妙地实现了事务管理机制。这种设计不仅体现了深厚的技术功底,也让人感叹科学的尽头也许就是哲学,艺术。
创作不易,如果您喜欢这篇文章,记得点赞并关注,后续将会有更多内容与深入分析
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。