深入理解MySQL事务:原理、应用与实践(下)
事务的四大特征
事务的四大特性(ACID)(面试)
数据库的事务必须具备ACID特性,ACID是指 Atomicity(原子性)、Consistensy(一致性)、Isolation(隔离性)和Durability(持久性)的英文缩写。
1.原子性(Atomicity)
原子性是事务的四大特性(ACID)之一,它确保事务包装的一组SQL(一组业务逻辑)是一个不可分割的工作单位。事务中的操作要么全部发生,要么全部不发生。原子性的核心思想是:事务中的所有操作被视为一个整体,要么全部成功执行,要么全部失败回滚。
原子性的重要性
在实际应用中,原子性是确保数据一致性和完整性的关键。例如,在银行转账、电子商务交易等场景中,如果一个操作失败,整个事务必须回滚,以确保数据的一致性。如果没有原子性保证,可能会导致数据不一致或部分操作成功的问题,严重影响系统的可靠性和用户的信任。
原子性的实现机制
MySQL通过以下机制来实现原子性:
-
事务日志(Transaction Log):
- 在事务执行过程中,MySQL会将事务的所有操作记录到事务日志中。事务日志通常存储在磁盘上,即使数据库发生崩溃,也可以通过事务日志恢复数据。
- 事务日志中记录了所有写操作(如
INSERT
、UPDATE
、DELETE
)的详细信息,包括操作前的数据和操作后的数据。
-
回滚日志(Rollback Log):
- 在事务执行过程中,MySQL会将每个操作的回滚信息记录到回滚日志中。如果事务需要回滚,MySQL可以通过回滚日志撤销所有未提交的操作,恢复到事务开始前的状态。
-
两阶段提交(Two-Phase Commit):
- 在分布式事务中,MySQL使用两阶段提交协议来确保原子性。两阶段提交协议分为准备阶段和提交阶段,确保所有参与节点要么全部提交,要么全部回滚。
示例:原子性的验证
假设我们有一个简单的转账操作,A给B转账100元。我们通过以下步骤来验证原子性:
-- 开启事务
START TRANSACTION;
-- 执行转账操作
UPDATE tb_account SET money = money - 100 WHERE `name` = 'A';
UPDATE tb_account SET money = money + 100 WHERE `name` = 'B';
-- 提交事务
COMMIT;
在这个示例中,转账操作被包装在一个事务中。如果所有操作都成功执行,事务将提交,A的账户将减少100元,B的账户将增加100元。如果任何一个操作失败,事务将回滚,所有操作将被撤销,A和B的账户余额将保持不变。
原子性的验证步骤
-
开启事务:
- 使用
START TRANSACTION
或BEGIN
语句开启一个新的事务。
- 使用
-
执行操作:
- 在事务中执行一组SQL操作,例如转账操作。
-
提交或回滚事务:
- 如果所有操作都成功执行,使用
COMMIT
语句提交事务。 - 如果任何一个操作失败,使用
ROLLBACK
语句回滚事务。
- 如果所有操作都成功执行,使用
-- 开启事务
START TRANSACTION;
-- 执行转账操作
UPDATE tb_account SET money = money - 100 WHERE `name` = 'A';
UPDATE tb_account SET money = money + 100 WHERE `name` = 'B';
-- 提交事务
COMMIT;
原子性的重要性总结
原子性是确保数据一致性和完整性的关键特性。通过原子性,可以确保事务中的所有操作被视为一个整体,要么全部成功执行,要么全部失败回滚。理解并应用原子性机制,有助于提升系统的可靠性和稳定性,确保数据的一致性和完整性。
2.一致性(Consistency)
一致性是指数据处于一种语义上有意义且正确的状态;
事务一致性是指事务执行的结果必须是使数据从一个一致性状态变到另一个一致性状态。
事务的成功与失败,最终数据库的数据都是符合实际生活的业务逻辑。一致性绝大多数依赖业务逻辑和原子性。
一致性是事务的四大特性(ACID)之一,它确保数据处于一种语义上有意义且正确的状态。事务一致性是指事务执行的结果必须是使数据从一个一致性状态变到另一个一致性状态。事务的成功与失败,最终数据库的数据都是符合实际生活的业务逻辑。一致性绝大多数依赖业务逻辑和原子性。
一致性的重要性
在实际应用中,一致性是确保数据正确性和完整性的关键。例如,在银行转账、电子商务交易等场景中,事务的执行结果必须符合业务逻辑,确保数据的一致性。如果没有一致性保证,可能会导致数据不一致或逻辑错误的问题,严重影响系统的可靠性和用户的信任。
一致性的实现机制
MySQL通过以下机制来实现一致性:
-
事务的原子性:
- 原子性确保事务中的所有操作要么全部成功执行,要么全部失败回滚。通过原子性,可以确保事务执行的结果不会导致数据不一致。
-
业务逻辑约束:
- 一致性依赖于业务逻辑的正确性。在设计数据库和事务时,必须确保事务的执行结果符合业务逻辑。例如,在转账操作中,必须确保转账前后账户余额的总和保持一致。
-
完整性约束:
- 数据库中的完整性约束(如主键约束、外键约束、唯一约束等)确保数据的一致性。事务在执行过程中必须遵守这些约束,确保数据的正确性。
示例:一致性的验证
假设我们有一个简单的转账操作,A给B转账100元。我们通过以下步骤来验证一致性:
-- 开启事务
START TRANSACTION;
-- 执行转账操作
UPDATE tb_account SET money = money - 100 WHERE `name` = 'A';
UPDATE tb_account SET money = money + 100 WHERE `name` = 'B';
-- 提交事务
COMMIT;
在这个示例中,转账操作被包装在一个事务中。事务的执行结果必须确保A和B的账户余额的总和保持一致。转账前,A和B的账户余额总和为2000元;转账后,A的账户余额为900元,B的账户余额为1100元,总和仍然为2000元。
一致性的验证步骤
-
开启事务:
- 使用
START TRANSACTION
或BEGIN
语句开启一个新的事务。
- 使用
-
执行操作:
- 在事务中执行一组SQL操作,例如转账操作。
-
验证一致性:
- 在事务提交后,验证数据的正确性和一致性。例如,验证转账前后账户余额的总和是否保持一致。
-- 开启事务
START TRANSACTION;
-- 执行转账操作
UPDATE tb_account SET money = money - 100 WHERE `name` = 'A';
UPDATE tb_account SET money = money + 100 WHERE `name` = 'B';
-- 提交事务
COMMIT;
-- 验证一致性
SELECT SUM(money) FROM tb_account;
一致性的重要性总结
一致性是确保数据正确性和完整性的关键特性。通过一致性,可以确保事务执行的结果符合业务逻辑,确保数据的一致性。理解并应用一致性机制,有助于提升系统的可靠性和稳定性,确保数据的正确性和完整性。
3、隔离性(Isolation)
多个用户并发的访问数据库时,一个用户的事务不能被其他用户的事务干扰,多个并发的事务之间要相互隔离。
隔离性是事务的四大特性(ACID)之一,它确保多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务干扰,多个并发的事务之间要相互隔离。隔离性的核心思想是:每个事务在执行过程中对数据的修改,对其他事务是不可见的,直到事务提交或回滚。
隔离性的重要性
在实际应用中,隔离性是确保数据一致性和并发控制的关键。例如,在银行转账、电子商务交易等场景中,多个用户可能同时进行操作,如果没有隔离性保证,可能会导致数据不一致或并发冲突的问题。通过隔离性,可以确保每个事务在执行过程中不受其他事务的影响,从而提升系统的可靠性和稳定性。
隔离级别
MySQL提供了四种隔离级别,用于控制事务之间的隔离程度:
-
读未提交(Read Uncommitted):
- 最低的隔离级别,允许一个事务读取另一个未提交事务的数据。这种隔离级别可能导致脏读(Dirty Read),即读取到未提交的数据。
-
读已提交(Read Committed):
- 允许一个事务读取另一个已提交事务的数据。这种隔离级别可以避免脏读,但可能导致不可重复读(Non-repeatable Read),即在同一个事务中多次读取同一数据,结果可能不同。
-
可重复读(Repeatable Read):
- 默认的隔离级别,确保在同一个事务中多次读取同一数据,结果始终相同。这种隔离级别可以避免脏读和不可重复读,但可能导致幻读(Phantom Read),即在同一个事务中多次读取同一范围的数据,结果可能不同。
-
串行化(Serializable):
- 最高的隔离级别,确保事务串行执行,完全隔离。这种隔离级别可以避免脏读、不可重复读和幻读,但可能导致性能问题,因为事务需要排队执行。
示例:隔离性的验证
假设我们有两个并发的事务,分别进行转账操作。我们通过以下步骤来验证隔离性:
-- 事务1:A给C转账100元
START TRANSACTION;
UPDATE tb_account SET money = money - 100 WHERE `name` = 'A';
UPDATE tb_account SET money = money + 100 WHERE `name` = 'C';
COMMIT;
-- 事务2:B给C转账200元
START TRANSACTION;
UPDATE tb_account SET money = money - 200 WHERE `name` = 'B';
UPDATE tb_account SET money = money + 200 WHERE `name` = 'C';
COMMIT;
在这个示例中,两个事务并发执行,但它们之间是相互隔离的。每个事务在执行过程中对数据的修改,对另一个事务是不可见的,直到事务提交或回滚。
隔离性的验证步骤
-
开启两个并发事务:
- 在两个不同的会话中,分别开启两个事务。
-
执行转账操作:
- 在两个事务中分别执行转账操作。
-
验证隔离性:
- 在每个事务中查询账户余额,验证每个事务在执行过程中对数据的修改,对另一个事务是不可见的。
-- 事务1中查询账户余额
SELECT * FROM tb_account;
-- 事务2中查询账户余额
SELECT * FROM tb_account;
隔离性的重要性总结
隔离性是确保数据一致性和并发控制的关键特性。通过隔离性,可以确保每个事务在执行过程中不受其他事务的影响,从而提升系统的可靠性和稳定性。理解并应用隔离性机制,有助于提升系统的并发性能和数据一致性,确保多个并发事务之间的相互隔离。
4、持久性(Durability)
指一个事务一旦被提交,它对数据库的改变将是永久性的,哪怕数据库发生异常,重启之后数据依然存在。
持久性是事务的四大特性(ACID)之一,它确保一旦事务被提交,其对数据库的改变将是永久性的,即使数据库发生异常或重启,数据依然存在。持久性的核心思想是:一旦事务提交,其结果将永久保存在数据库中,不会因为系统故障或其他原因而丢失。你只要执行commit,数据永久性改变了,即使执行rollback也回不来了
持久性的重要性
在实际应用中,持久性是确保数据可靠性的关键。例如,在银行转账、电子商务交易等场景中,一旦资金转移成功,就必须确保这些操作是永久性的,不会因为系统故障或重启而丢失。如果没有持久性保证,可能会导致资金丢失或重复转账等问题,严重影响系统的可靠性和用户的信任。
持久性的实现机制
MySQL通过以下机制来实现持久性:
-
事务日志(Transaction Log):
- 在事务提交时,MySQL会将事务的所有操作记录到事务日志中。事务日志通常存储在磁盘上,即使数据库发生崩溃,也可以通过事务日志恢复数据。
- 事务日志中记录了所有写操作(如
INSERT
、UPDATE
、DELETE
)的详细信息,包括操作前的数据和操作后的数据。
-
双写缓冲(Doublewrite Buffer):
- MySQL使用双写缓冲机制来确保数据页的完整性。在将数据页写入磁盘之前,MySQL会先将数据页写入双写缓冲区,然后再写入磁盘。如果写入过程中发生故障,可以通过双写缓冲区恢复数据页。
-
检查点(Checkpoint):
- 检查点是事务日志中的一个标记,表示在该点之前的事务已经完全写入磁盘。当数据库重启时,MySQL会从最近的检查点开始恢复数据,确保所有已提交的事务都能被正确恢复。
示例:持久性的验证
假设我们有一个简单的转账操作,A给B转账100元。我们通过以下步骤来验证持久性:
-- 开启事务
START TRANSACTION;
-- 执行转账操作
UPDATE tb_account SET money = money - 100 WHERE `name` = 'A';
UPDATE tb_account SET money = money + 100 WHERE `name` = 'B';
-- 提交事务
COMMIT;
在这个示例中,一旦执行 COMMIT
,A的账户将减少100元,B的账户将增加100元。即使数据库在提交后立即发生崩溃或重启,这些操作的结果也将永久保存在数据库中。
持久性的验证步骤
-
执行事务并提交:
- 执行上述转账操作,并确保事务成功提交。
-
模拟数据库崩溃:
- 在事务提交后,模拟数据库崩溃(例如,关闭数据库服务或强制重启服务器)。
-
重启数据库并验证数据:
- 重启数据库服务,并查询账户余额。
- 查询结果应显示A的账户余额为900元,B的账户余额为1100元,验证持久性。
-- 查询账户余额
SELECT * FROM tb_account;
持久性的重要性总结
持久性是确保数据可靠性的关键特性。通过事务日志、双写缓冲和检查点等机制,MySQL能够确保一旦事务提交,其结果将永久保存在数据库中,不会因为系统故障或重启而丢失。理解并应用持久性机制,有助于提升系统的可靠性和稳定性,确保数据的一致性和完整性。
小结
事务四个特性?
原子性
一致性
隔离性
持久性
事务特性 | 含义 |
---|---|
一致性(Consistency) | 事务前后数据的完整性必须保持一致 |
原子性(Atomicity) | 事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 |
隔离性(Isolation) | 是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离,不能相互影响。 |
持久性(Durability) | 指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响 |
事务并发访问问题
事务的并发访问引发的三个问题(面试) 多个线程访问事务
事务在操作时的理想状态:多个事务之间互不影响,如果隔离级别设置不当就可能引发并发访问问题。
并发访问的问题
脏读 (开发中绝对不允许出现脏读现象)
指一个事务读取了另外一个事务未提交的数据,脏读强调的是一个事务读取了未提交的事务的数据;
如果不考虑隔离性就会有脏读现象
TransactionA TransactionB 操作同一条数据
T1 时间段 AB各自手动开启事务
T2 时间段 B 更新数据
update user set age=18 where id=1;
T3 时间段 A 读数据
select *from user where id=1;
假设不考虑事务的隔离性 事务B还没提交 T3时间段 事务A读取到了事务B未提交的数据 age=18
T4 时间段 B回滚了 回到最开始的状态 age=16
假设我是淘宝阿里后台工程师 我有这个权限 我双十一在淘宝买了一把阿米洛的键盘,我下单了,给他截图,我已付款,商家随后开始发货,我收到货后使用了一段时间,我回滚了,买键盘的钱又回到我的支付宝里面了 生活中绝对不允许出现这种现象 实际开发中 我们一定要避免脏读
不可重复读 (开发是允许出现的) 读取的是已提交的 导致前后不一致
在一个事务内多次读取表中的数据,多次读取的内容不同,多发生在其他事务update操作时;
T1 时间段 AB各自手动开启事务
T2 时间段 A 查询数据
select *from user where id=1; 16
T3 时间段 B 更新数据并且提交了
update user set age=18 where id=1;
commit;
数据永久性改变了
T4 时间段 A再次读取数据 age=18
对于A来说 读取了事务B已经提交的数据 前后两次读取的结果不一样 一个16 一个18
倘若对时间的时效性考虑较多 则不允许出现不可重复读现象
幻读(虚读) 读取的是已提交的 强调的是数量 允许发生
一个事务内读取到了别的事务插入或者删除的数据,导致前后读取记录行数不同,多发生在delete或insert时;
T1 时间段 AB各自手动开启事务
T2 时间段 A 查询数据
select count(*) from user where age>15;-- 只有16
T3 时间段 B 插入一条新数据 随后commit提交
insert into user values(2,'lucy',22);
commit;
T4 时间段 A 再次查询 发现前后两次查询结果不一致
select count(*) from user where age>15;-- 有16 22
幻读 和 不可重复读 的区别在哪
幻读 影响的行记录数语句 insert/delete
一个事务内读取到了别的事务插入或者删除的数据,导致前后读取记录行数不同
不可重复读 强调的是修改数据语句 update
一个事务内2次读取,其中一次读取了另一个事务提交了的数据。
小结
赃读:一个事务读取另一个事务还没有提交的数据,一定避免。
不可重复读:一个事务读取多次数据内容不一样,主要是update语句。事务已经提交了。 可以发生的。
幻读:一个事务读取多次数量不一样,主要是delete或者insert语句。事务已经提交了。可以发生的。
事务的隔离级别
MySQL数据库规范规定了4种隔离级别,用于解决上述出现的事务并发问题;
通过以上问题演示,我们发现如果不考虑事务的隔离性,会遇到脏读、不可重复读和虚读等问题。所以在数据库中我们要对上述三种问题进行解决。MySQL数据库规范规定了4种隔离级别,分别用于描述两个事务并发的所有情况。
上面的级别最低,下面的级别最高。“是”表示会出现这种问题,“否”表示不会出现这种问题。
级别 | 名字 | 隔离级别 | 脏读 | 不可重复读 | 幻读 | 数据库默认隔离级别 |
---|---|---|---|---|---|---|
1 | 读未提交 | read uncommitted | 是 | 是 | 是 | |
2 | 读已提交 | read committed | 否 | 是 | 是 | Oracle和SQL Server |
3 | 可重复读 | repeatable read | 否 | 否 | 是 | MySQL |
4 | 串行化 | serializable | 否 | 否 | 否 |
2、安全和性能对比
安全性:serializable > repeatable read > read committed > read uncommitted
性能 : serializable < repeatable read < read committed < read uncommitted
3、注意:其实三个问题,开发中最严重的问题就是脏读,这个问题一定要避免,而关于不可重复读和虚读其实只是感官上的错误,并不是逻辑上的错误。就是数据的时效性,所以这种问题并不属于很严重的错误。如果对于数据的时效性要求不是很高的情况下,我们是可以接受不可重复读和虚读的情况发生的。
开发中脏读现象绝对不能发生 公司里面的数据库的隔离级别不会设置read uncommitted
Oracle和SQL Server 隔离级别默认是 read committed
MySQL 隔离级别默认是 repeatable read 可以避免脏读/不可重复读 不能避免幻读
serializable 对象序列化 前面遇到过 这个隔离级别是最安全的 脏读 幻读 不可重复读 都可以避免 好像没有数据库设置serializable这个隔离级别 有利有弊 就安全性而言串行化serializable 这个级别是最高的 但是它的性能也是最低的,
小结
MySQL的四种隔离级别
读未提交:read uncommitted
读已提交:read committed 可以避免脏读
可重复读:repeatable read mysql默认的,可以避免脏读 和不可重复读
串行化:serializable