事务是逻辑上一组操作的集合,事务会把所有操作作为一个整体一起向系统 提交 或 撤销 操作请求,即这些操作要么同时成功,要么同时失败。
事务演示
基本操作
-- 1. 查询张三账户余额 select * from account where name = '张三'; -- 2. 将张三账户余额-1000 update account set money = money - 1000 where name = '张三'; -- 此语句出错后张三钱减少但是李四钱没有增加 模拟sql语句错误 -- 3. 将李四账户余额+1000 update account set money = money + 1000 where name = '李四'; -- 查看事务提交方式 SELECT @@AUTOCOMMIT; -- 设置事务提交方式,1为自动提交,0为手动提交,该设置只对当前会话有效 SET @@AUTOCOMMIT = 0; -- 提交事务 COMMIT; -- 回滚事务 ROLLBACK; -- 设置手动提交后上面代码改为: select * from account where name = '张三'; update account set money = money - 1000 where name = '张三'; update account set money = money + 1000 where name = '李四'; commit;
操作方式二
开启事务:
START TRANSACTION 或 BEGIN TRANSACTION;
提交事务:
COMMIT;
回滚事务:
ROLLBACK;
操作实例:
start transaction; select * from account where name = '张三'; update account set money = money - 1000 where name = '张三'; update account set money = money + 1000 where name = '李四'; commit;
四大特性ACID
- 原子性(Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败
- 一致性(Consistency):事务完成时,必须使所有数据都保持一致状态
- 隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行
- 持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的
并发事务存在的问题
问题 | 描述 |
脏读 | 一个事务读到另一个事务还没提交的数据 |
不可重复读 | 一个事务先后读取同一条记录,但两次读取的数据不同(侧重内容数据的修改) |
幻读 | 一个事务按照条件查询数据时,没有对应的数据行,但是再插入数据时,又发现这行数据已经存在(侧重新增或删除,插入数据读到多了一行) |
不可重复读:破坏了一致性,update 和 delete
幻读:破坏了一致性 insert
不可重复读和幻读的区别
● 不可重复读的重点是内容修改或者记录减少比如多次读取一条记录发现其中某些记录的值被修改;
● 幻读的重点在于记录新增比如多次执行同一条查询语句(DQL)时,发现查到的记录增加了。
幻读其实可以看作是不可重复读的一种特殊情况,
单独把区分幻读的原因主要是解决幻读和不可重复读的方案不一样。
举个例子:执行 delete 和 update 操作的时候,可以直接对记录加锁,保证事务安全。
而执行 insert 操作的时候,由于记录锁(Record Lock)只能锁住已经存在的记录,为了避免插入新记录,需要依赖间隙锁(Gap Lock)。
也就是说执行 insert 操作的时候需要依赖 Next-Key Lock(Record Lock+Gap Lock)
进行加锁来保证不出现幻读。
这三个问题的详细演示:https://www.bilibili.com/video/BV1Kr4y1i7ru?p=55cd
并发事务隔离级别
读未提交:原理:直接读取数据,不能解决任何并发问题
读已提交:读操作不加锁,写操作加排他锁,解决了脏读。原理:利用MVCC实现,每一句语句执行前都会生成Read View(一致性视图)
可重复读:MVCC实现,只有事务开始时会创建Read View,之后事务里的其他查询都用这个Read View。解决了脏读、不可重复读,快照读(普通查询,读取历史数据)使用MVCC解决了幻读,当前读(读取最新提交数据)通过间隙锁解决幻读(lock in share mode、for update、update、detete、insert),间隙锁在可重复读下才生效。(默认隔离级别)
可串行化:原理:使用锁,读加共享锁,写加排他锁,串行执行
总结:
读已提交和可重复读实现原理就是MVCC Read View不同的生成时机。
可重复读只在事务开始时生成一个Read View,之后都用的这个;读已提交每次执行前都会生成Read View。
MySQL 在 InnoDB下是默认可重复读的隔离级别,加上 MVCC机制 解决了脏读、不可重复读、幻读的问题。
隔离级别 | 脏读 | 不可重复读 | 幻读 |
Read uncommitted | √ | √ | √ |
Read committed | × | √ | √ |
Repeatable Read(默认) | × | × | √ |
Serializable | × | × | × |
- √ 表示在当前隔离级别下该问题会出现
- Serializable 性能最低;Read uncommitted 性能最高,数据安全性最差
- 在 MySQL 的 InnoDB 引擎下,Repeatable Read 其实不会发生幻读(MVCC机制)。
- 自 MySQL5.5 之后,默认的存储引擎是 Innodb。
查看事务隔离级别:
SELECT @@TRANSACTION_ISOLATION;
设置事务隔离级别:
SET [ SESSION | GLOBAL ] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE };
SESSION 是会话级别,表示只针对当前会话有效,GLOBAL 表示对所有会话有效
实现事务的原理
MySQL 中的 ACID 实现原理:
原子性:undolog(记录事务开始前的老版本数据,可以保证原子操作,回滚,实现MVCC版本链)
隔离性:读写锁 、MVCC
持久性:redo log(记录事务开启后对数据的修改,可用于crash-safe)
事务的重要性
MySQL 中事务的重要性体现在以下几个关键方面:
- 数据完整性:
事务能够确保一组数据库操作要么全部成功执行,要么全部不执行(即原子性)。例如,在转账过程中,需要同时减少一个账户的余额并增加另一个账户的余额,这两步操作必须作为一个不可分割的整体来完成。如果其中任何一步失败,则整个事务应当回滚以保持数据一致性。
- 一致性保证:
在事务执行前后,数据库状态始终保持一致,即使在多个并发事务同时进行时也是如此。每个事务都必须从一个一致性的状态转换到另一个一致性的状态,这意味着事务的结果必须遵循所有的业务规则和约束条件。
- 隔离性:
多个并发事务之间相互独立,互不影响。通过事务隔离级别控制(如读未提交、读已提交、可重复读、串行化等),可以防止脏读、不可重复读和幻读等问题,确保每个事务看到的数据视图都是稳定的。
- 持久性:
当事务提交后,对数据库的修改是永久性的,即使系统出现故障(如宕机)也能保证这些修改不会丢失。这是通过日志记录和恢复机制来实现的。
- 错误恢复与回滚:
如果在事务处理过程中发生错误或异常,可以通过回滚事务撤销所有已执行的操作,使数据库回到事务开始前的状态,从而避免因部分操作成功而导致的数据混乱。
综上所述,事务管理是关系型数据库系统中不可或缺的一部分,它为复杂的数据操作提供了可靠的环境,尤其对于涉及财务交易、库存管理等高要求一致性和完整性的场景至关重要。