MySQL 之事务详解

时间:2024-09-30 21:54:49

想象一下,你正在使用银行App转账,将100元从你的账户转到朋友的账户。这个看似简单的操作,背后其实包含着多个数据库操作:

  1. 检查账户余额: 确保你的账户余额足够支付转账金额。

  2. 从你的账户扣除100元: 更新你的账户余额。

  3. 将100元添加到朋友的账户: 更新朋友的账户余额。

如果在这三个步骤之间发生了错误,比如网络中断或者系统故障,会导致什么?你的账户可能已经扣款,但朋友的账户却没有收到钱!为了避免这种情况,数据库引入了事务的概念,就像一个“安全盒子”,确保一系列操作要么全部成功,要么全部回滚,保证数据的一致性。

一、什么是事务

在数据库中,事务是执行数据库操作的逻辑单元,它就像一个“安全盒子”,将一系列操作打包在一起,并遵循ACID四大特性:

1. 原子性 (Atomicity):不可分割的操作

事务就像一个不可分割的操作,要么全部执行成功,要么全部失败回滚,不会出现部分成功的情况。就像转账,要么钱成功转出并到账,要么就回到初始状态,不会出现钱扣了却没到账的情况。

例子:

假设你要从账户A转账100元到账户B,这个事务包含两个操作:

  • 从账户A扣除100元。

  • 向账户B添加100元。

如果因为某些原因,比如账户B不存在,导致向账户B添加100元失败,那么整个事务应该回滚,账户A的余额不应该改变。

2. 一致性 (Consistency):始终如一的状态

事务必须保证数据库从一个一致性状态转换到另一个一致性状态。也就是说,一个事务执行之前和执行之后,数据库都必须处于一致性状态。就像银行,无论何时查询,总资产都应该是一致的,不会因为转账过程中出现错误而导致总资产变化。

例子:

在转账的例子中,数据库的一致性状态可以定义为:所有账户的余额总和始终保持不变。事务开始前,所有账户的余额总和是100元,事务结束后,无论转账成功与否,所有账户的余额总和都应该还是100元。

3. 隔离性 (Isolation):互不干扰的操作

多个事务并发执行时,一个事务的执行不应影响其他事务的执行,就好像这些事务是串行执行的一样。这就像多个用户同时在银行办理业务,每个用户的操作都应该独立完成,不受其他用户操作的影响。

例子:

假设用户A要从账户A转账100元到账户B,同时用户B要查询账户A的余额。隔离性保证了用户B查询到的账户A余额是准确的,不会因为用户A的转账操作而受到影响,即使这两个操作是同时进行的。

4. 持久性 (Durability):永久保存的修改

一旦事务提交,其对数据库的修改就是永久性的,即使系统发生故障,这些修改也不会丢失。这就像转账完成后,即使银行系统出现故障,转账记录也不会消失。

例子:

在转账的例子中,当转账事务成功提交后,账户A和账户B的余额修改会被永久保存在数据库中,即使之后数据库崩溃,再次启动后,账户A和账户B的余额仍然是修改后的值。

二、MySQL中如何开启事务

在MySQL中,我们可以使用一些简单的语句来操控事务

1. 启动事务
START TRANSACTION; 
-- 或者
BEGIN;

这两条语句就像打开“安全盒子”,告诉MySQL:“我要开始一系列操作,请帮我保证它们要么全部成功,要么全部失败!”

2. 提交事务
COMMIT;

当你完成所有操作后,执行“COMMIT”,所有更改都会永久保存在数据库中。

3. 回滚事务
ROLLBACK;

如果在操作过程中出现错误,执行“ROLLBACK”,所有更改都会被撤销,数据库会回到事务开始前的状态。

例子:模拟银行转账
START TRANSACTION;

UPDATE accounts SET balance = balance - 100 WHERE user_id = 1; -- 从用户1账户扣款
UPDATE accounts SET balance = balance + 100 WHERE user_id = 2; -- 将钱添加到用户2账户

COMMIT; -- 提交事务,转账完成

如果在执行过程中出现错误,例如用户1余额不足,可以使用 ROLLBACK 语句撤销转账操作:

START TRANSACTION;

UPDATE accounts SET balance = balance - 100 WHERE user_id = 1;
-- 假设用户1余额不足,这里会执行失败

ROLLBACK; -- 回滚事务,撤销转账操作

三、隔离级别:为事务保驾护航

在现实世界中,多个用户可能会同时进行操作。为了避免相互干扰,MySQL提供了不同的隔离级别,就像图书馆的阅览规则,确保每个人都能获取到正确的信息:

  • Read Uncommitted (读未提交): 这是最低的隔离级别,允许一个事务读取另一个事务未提交的数据。就像在图书馆里偷看别人的草稿,可能会看到不完整或错误的信息。

  • Read Committed (读已提交): 这是大多数数据库系统的默认隔离级别,它只允许事务读取其他事务已经提交的数据。 就像在图书馆里只能看到已经出版的书籍,保证看到的信息都是完整可靠的。

  • Repeatable Read (可重复读): 该级别保证在同一个事务中多次读取同一数据时,得到的结果是一致的,可以避免脏读和不可重复读问题。就像在图书馆里借了一本书,在归还之前,书的内容不会被其他人修改。这是 MySQL 默认的隔离级别。

  • Serializable (串行化): 这是最高的隔离级别,它强制事务串行执行,可以避免所有并发问题,但会导致系统性能下降。 就像在图书馆里一次只允许一个人进入,保证任何时候都不会出现数据冲突。

并发问题:当多个事务“撞车”

当多个用户同时操作数据库时,如果没有设置合适的隔离级别,可能会出现一些“交通事故”:

  • 脏读 (Dirty Read): 一个事务读取到了另一个事务未提交的数据。 就像看到了别人还没写完的草稿,可能会得到错误的信息。

  • 不可重复读 (Non-repeatable Read): 一个事务中多次读取同一数据,因为其他事务的提交,导致每次读取的数据不一致。就像一本书在你阅读过程中被别人修改了,你前后看到的 내용 就不同了。

  • 幻读 (Phantom Read): 一个事务按照相同条件查询数据,因为其他事务的插入或删除操作,导致前后两次查询的结果集不一致。 就像书架上突然出现或消失了一本书,你前后看到的书的总数就不一样了。

为了避免这些“交通事故”,可以选择更高的隔离级别,或者使用锁机制来控制并发访问。

四、总结

事务是保证数据一致性的重要机制,就像一个“安全盒子”,保护着你的数据操作。

  • 理解ACID四大特性是掌握事务的关键。

  • 使用 START TRANSACTION 、 COMMIT 和 ROLLBACK 语句来控制事务。

  • 选择合适的隔离级别来避免并发问题。

  • 了解常见的并发问题,并学会如何避免。

希望本文能够帮助各位看官学习MySQL事务相关操作,感谢各位看官的观看,下期见,谢谢~