【每日八股】MySQL篇(二):事务
MySQL 之事务的四大特性(ACID)?
- 原子性(Atomicity):一个事务被视为一个不可分割的最小工作单元,整个事务中的操作要么全部成功,要么全部失败回滚,对于一个事务而言,不可能只执行其中一部分操作,这就是事务的原子性。
- 一致性(Consistency):数据库总是从一个一致性的状态转移到另一个一致性的状态;
- 隔离性(Isolation):一个事务所做的修改在最终提交前,其它事务是不可见的;
- 持久性(Durability):一旦事务提交,那么其所做的修改就会永远保存在数据库当中。此时即使系统崩溃,修改的数据也不会丢失。
概括:MySQL 事务的四大特性分别是原子性、一致性、隔离性和持久性。
并发事务会出现什么问题?
- 脏读:指的是读取到了其他事务未提交的数据。未提交意味着某条事务的执行可能失败,造成回滚,也就是读取到的数据可能未在数据库当中,也就是读取到了不存在的数据。读取到不一定存在的数据就是脏读。
- 不可重复读:不可重复读指的是在同一个事务内,不同时间读取到的数据可能是不一样的,这个现象产生的原因是读取的数据被其它事务修改并提交了。不可重复读常出现在更新操作中。【可重复读:指的是在同一个事务内,最开始读取到的数据和事务结束前任意时刻读取到的数据是一致的】
- 幻读:在一个事务中,两次相同的查询返回了不同的结果,尽管两次查询之间没有修改或删除现有的数据,但可能有新的插入。
MySQL 的事务隔离级别
- 读未提交(Read Uncommitted):指一个事务还未提交时,它所做的变更就可以被其它事务看到。(隔离级别最低,可能导致脏读、幻读和不可重复读)
- 读已提交(Read Committed):指一个事务提交之后,它所做的变更才能被其它事务看到。(避免了脏读,但可能出现不可重复读和幻读)
- 可重复读(Repeatable Read):确保在同一事务中每次读取到的同一数据的结果都是一致的。(MySQL 的默认隔离级别,避免了脏读和不可重复读,但在某些情况下会出现幻读)
- 串行化(Serializable):会对记录加上读写锁,在多个事务对某条记录进行操作时,如果发生了读写冲突,那么后访问的事务必须等待前一个事务执行完成才能执行。(最高的隔离级别,不会发生脏读、幻读以及不可重复读,但是性能开销也是最大的)
在不同事务隔离级别下会发生什么现象?
- 读未提交:脏读、幻读、不可重复读;
- 读已提交:幻读、不可重复读;
- 可重复读:幻读;
- 串行化:无;
解决脏读现象:将隔离级别升级到“读已提交”;
解决不可重复读现象:将隔离级别升级到“可重复读”;
解决幻读:不建议将隔离急别升级到串行化,因为这样会导致在并发事务时性能很差。
MVCC 实现原理?
MVCC(Multi-Version Concurrency Control,版本控制)是一种用于数据库管理系统中的并发控制方法,允许多个事务同时访问数据库而不阻塞。MVCC 通过维护数据的多个版本来实现高并发和一致性。
MVCC 的实现原理
一. 版本链:
- 每条记录都有一个版本链,每个版本包含数据内容和创建该版本的事务的ID(
trx_id
); - 当事务更新时,会创建一个新版本并添加到版本链中,旧版本保留;
二. Read View:
- 事务在执行时会生成一个
Read View
,用于确定哪些版本对当前事务可见; Read View
包含当前活跃事务 ID 列表、最小事务 ID (up_limit_id
)和下一个事务 ID(low_limit_id
);
三. 可见性判断:
- 事务读取数据时,根据
Read View
判断版本链中哪个版本可见; - 如果版本的事务 ID 小于
up_limit_id
且不在活跃事务列表中,则该版本可见; - 如果版本的事务 ID 等于当前事务ID,则该版本可见;
- 如果版本的事务 ID 大于等于
low_limit_id
,则该版本不可见;
四. 事务提交与回滚:
- 事务提交时,其修改的版本对其他事务可见;
- 事务回滚时,其修改的版本被标记为无效;
MVCC 的优点
- 高并发:读操作不会阻塞写操作,写操作也不会阻塞读操作;
- 一致性:事务读取的数据是一致的,不受其他事务影响;
MVCC 的缺点
- 存储开销:需要维护多个版本,增加存储空间;
- 清理机制:需要定期清理旧版本,防止存储膨胀;
幻读是如何解决的?
- 快照读(普通 select 语句):通过 MVCC 方式解决幻读,在可重复读隔离级别下,事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,即使中途有其它事务插入了一条数据,也是查询不出这条数据的。
- 当前读(select … for update 语句):通过 next-key lock(记录锁 + 间隙锁)方式解决了幻读。
注意,MySQL 可重复读隔离级别没有解决幻读,而是尽可能地避免了幻读现象的发生。
读提交(Read Committed)怎么实现?
读提交隔离级别是在每一次读取数据时,都会生成一个新的 Read View。事务执行期间多次读取到的同一数据前后可能不一致,因为在此期间另一个事务可能修改了这条记录,并提交了记录,导致不可重复读。