MySQL进阶—事务篇💛

小龙coding大约 13 分钟

MySQL (3)

1、事务四大特性?MySQL的事务原理?

简述事务:一系列操作组成,要么全部成功,要么全部失败

详细描述

事务是由一个有限的数据库操作序列构成。数据库事务通常包含了一组对数据库的读和写操作。这组操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。

数据库事务的主要目的是为了保证数据库操作的一致性。即使在并发操作或系统故障的情况下,事务也能保证数据库的一致性。

事务ACID特性

数据库事务必须满足所谓的ACID属性,即:

  1. 原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部执行,要么全部不执行。
  2. 一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的意思是数据库中的数据满足预定的规则和约束。(事务修改前后的数据总体保证一致 转账)
  3. 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行。(事务的结果只有提交了其他事务才可见)
  4. 持久性(Durability):已被提交的事务对数据库的修改应该是永久的,即使在系统故障的情况下也能保持。

事务的并发问题:

  1. 脏读:读到未提交的数据
  2. 不可重复读:一个事务下,两次读取数据不一致(侧重内容数据的修改)
  3. 幻读:事务A 按照一定条件进行数据读取, 期间事务B 插入了相同搜索条件的新数据,事务A再次按照原先条件进行读取时,发现了事务B 新插入的数据 称为幻读(侧重新增或删除,插入数据读到多了一行)

隔离级别原理及解决问题分析:

  1. 读未提交:原理:直接读取数据,不能解决任何并发问题
  2. 读已提交:读操作不加锁,写操作加排他锁,解决了脏读。原理:利用MVCC实现,每一句语句执行前都会生成Read View(一致性视图)
  3. 可重复读:MVCC实现,只有事务开始时会创建Read View,之后事务里的其他查询都用这个Read View。解决了脏读、不可重复读,快照读(普通查询,读取历史数据)使用MVCC解决了幻读,当前读(读取最新提交数据)通过间隙锁解决幻读(lock in share mode、for update、update、detete、insert),间隙锁在可重复读下才生效。(默认隔离级别
  4. 可串行化:原理:使用锁,读加共享锁,写加排他锁,串行执行

总结**:读已提交和可重复读实现原理就是 MVCC Read View不同的生成时机。可重复读只在事务开始时生成一个Read View,之后都用的这个;读已提交每次执行前都会生成Read View**

2.详细说说ACID 底层实现原理?

简述版本:

原子性:undolog(记录事务开始前的老版本数据,可以保证原子操作,回滚,实现MVCC版本链)

隔离性:MVCC

持久性:redo log(记录事务开启后对数据的修改,可用于crash-safe)

详细描述:

ACID是数据库事务正确性的四大特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability):

  1. 原子性(Atomicity):原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,这和操作系统的原子操作类似。这个原理通常通过数据库的 undo 日志来实现。当数据库系统出现故障时,会利用这些 undo 日志将数据库恢复到事务开始前的状态,以此来保证原子性。
  2. 一致性(Consistency):一致性是指事务必须使数据库从一个状态变换到另一个状态。在事务开始和结束时,数据库都必须处于一致性状态。这个原理主要依赖于数据库系统的约束机制(如主键约束、外键约束、唯一约束等)以及应用程序来保证。
  3. 隔离性(Isolation):隔离性是当多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。这个原理主要通过数据库的锁机制和多版本并发控制(MVCC)来实现。
  4. 持久性(Durability):持久性是指一旦事务提交,则其所做的修改将永久保存到数据库中。即使系统发生崩溃,修改的数据也不会丢失。这个原理主要通过数据库的redo日志来实现。当系统发生故障时,会利用redo日志重做事务,以此来保证持久性。

3.MySQL隔离级别有哪些?并发时可能存在哪些问题?

MySQL支持以下四种事务隔离级别:

  1. 读未提交(Read Uncommitted):最低的隔离级别,允许读取尚未提交的数据变更,可能导致脏读、幻读或不可重复读。
  2. 读已提交(Read Committed):大多数数据库系统的默认隔离级别(但不包括MySQL和PostgreSQL)。只能读取已经提交的数据。可以防止脏读,但幻读和不可重复读仍可能发生。
  3. 可重复读(Repeatable Read):MySQL的默认隔离级别。在同一事务内的查询都会基于一个快照,保证在同一事务内多次读取同样记录的结果是一致的。但是理论上,该级别仍然无法防止幻读,尽管在InnoDB存储引擎中已经做了处理,实际上可以防止幻读。
  4. 串行化(Serializable):最高的隔离级别,完全服从ACID的隔离级别。所有的事务将依次逐个执行,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响性能,一般不会在实际应用中使用。

4.不可重复读和幻读区别?

在MySQL的InnoDB存储引擎中,"不可重复读"和"幻读"的概念与标准的ACID定义有所不同,主要体现在InnoDB的MVCC(多版本并发控制)机制上。

  1. 不可重复读(Non-repeatable read):在MySQL的InnoDB引擎中,不可重复读是指在一个事务内,多次读取同一数据,在事务未提交前,由于其他事务的修改导致后续读取的数据与前面读取的数据不一致。这种现象主要是由于其他事务对数据的修改(update或delete)造成的。InnoDB通过MVCC来解决这个问题,只要将事务隔离级别设置为REPEATABLE READ(可重复读,这也是InnoDB的默认隔离级别),就可以避免不可重复读的问题。
  2. 幻读(Phantom read):幻读是指在一个事务内,执行两次相同的查询,由于其他事务的插入操作,第二次查询结果包含了第一次查询中未出现的新行。这种现象主要是由于其他事务的插入操作造成的。具体什么是幻读?RR隔离级别幻读是否解决?怎么解决幻读?可见下方详述~

不过总的来说,不可重复读关注的是修改操作,而幻读关注的是插入操作。在InnoDB中,通过MVCC和不同的事务隔离级别,可以有效地控制这两种现象。

5.什么是幻读?RR隔离级别幻读解决了吗?怎么解决幻读?

幻读:是指在一个事务内读取到了别的事务插入的数据,导致前后读取的数据不一致。例如,你在一个事务中先后两次执行了相同的查询语句,第一次查询返回了5条记录,但第二次查询却返回了6条记录,这就是幻读。

在标准的 SQL 隔离级别定义中,可重复读隔离级别并不能解决幻读问题。因为标准定义下的重复读隔离级别,虽然可以防止同一条记录被其他事务修改,但无法防止其他事务插入新的满足查询条件的记录,因此可能出现幻读。只有在串行化(Serializable)隔离级别下,才能完全防止幻读。

但在 MySQL 的 InnoDB 存储引擎中,可重复读(Repeatable Read)隔离级别实际上已经解决了幻读的问题。

  1. 对于读操作,由于使用了一致性读(consistent read)和多版本并发控制(MVCC),可以保证在同一事务中多次读取同样记录的结果是一致的,避免了不可重复读和幻读的问题。
  2. 对于写操作,InnoDB 使用了 next-key locking 机制,这种机制会锁定记录行以及索引记录的“间隙”,防止其他事务在这些“间隙”中插入新的记录,从而避免了幻读。

但是,虽然在可重复读隔离级别下,InnoDB 可以有效地防止幻读,但如果事务在读取数据后又进行了写操作(例如,先SELECT,然后UPDATE),可能会出现因为并发操作而导致的数据不一致问题。这种情况下,可能需要使用更高的隔离级别(如串行化)或者显式地使用锁(如SELECT FOR UPDATE)来确保数据的一致性。

6.MySQL 是如何避免事务并发问题的?

7.靠缓存可以提高事务隔离级别的性能吗?

MySQL中的缓存机制可以提高数据库的整体性能,但并不能直接提高事务的隔离级别性能。

事务的隔离级别主要是用来解决"脏读"、"不可重复读"、"幻读"等并发问题的,它们主要通过锁机制和多版本并发控制(MVCC)等技术来实现,与缓存机制没有直接关系。

然而,MySQL的缓存机制,如查询缓存、InnoDB缓冲池等,可以显著提高数据库的读写性能。查询缓存可以缓存SELECT查询的结果,当相同的查询再次出现时,可以直接从缓存中获取结果,而不需要再次执行查询。InnoDB缓冲池可以缓存数据页和索引页,减少了对磁盘的IO操作,从而提高了数据库的性能。

所以,虽然缓存机制不能直接提高事务的隔离级别性能,但它可以提高数据库的整体性能,间接地提高了事务的处理效率

7.MySQL 事务隔离是如何实现的?

MySQL主要通过以下几种方式来避免事务并发问题:

  1. 锁机制:MySQL支持多种锁定级别,包括行级锁(InnoDB引擎)和表级锁(MyISAM引擎)。锁机制可以防止多个事务同时修改同一条数据,从而避免并发问题。
  2. MVCC(多版本并发控制):在InnoDB引擎中,通过MVCC机制可以使不同的事务看到不同版本的数据,从而实现非锁定并发控制。这种方式可以在一定程度上解决"脏读"、"不可重复读"和"幻读"等并发问题。
  3. 事务隔离级别:MySQL支持四种事务隔离级别:读未提交(READ UNCOMMITTED)、读已提交(READ COMMITTED)、可重复读(REPEATABLE READ)和串行化(SERIALIZABLE)。不同的隔离级别对并发问题的控制力度不同,用户可以根据实际需求选择合适的隔离级别。
  4. 死锁检测:当多个事务试图以不同的顺序锁定资源时,可能会出现死锁。MySQL的InnoDB存储引擎提供了死锁检测机制,当检测到死锁时,会主动回滚其中一个事务,以解决死锁问题。

8.详细讲解 MVCC 底层实现原理?(重点)

原理提炼总结:使用版本链+Read View

详细分析:

  • InnoDB 数据表每行数据记录会有几个隐藏字段:row_id,事务ID,回滚指针
  • InnoDB 采用主键索引(聚簇索引),会利用主键维护索引,若表没有主键,就用第一个非空唯一索引,若没有唯一索引,则用row_id这个隐藏字段作为主键索引。
  • 事务开启会向系统申请一个事务ID,严格递增,会向行记录插入最近操作它的那个事务的ID
  • undo log 会记录事务前老版本数据,然后行记录中回滚指针会指向老版本位置,如此形成一条版本链。因此可以利用undo log实现回滚,保证原子性,同时用于实现 MVCC 版本链(同一行数据可能有多个版本)。

img

可重复读与读已提交下区别:

  1. 读已提交隔离级别下,会在每次查询都生成一个 Read View;
  2. 可重读读只在事务开始时生成一个Read View,以后每次查询都用这个Read View,以此实现不同隔离界别。

Read View里面包含些什么?(一致性视图)

一个数组 + up_limit_id(低水位) + low_limit_id(高水位)(这里的up,low没写错,就是这么定义的)

1、数组里包含事务启动时当前活跃事务ID(未提交事务),低水位就是活跃事务最小ID,高水位就是下一次将分配的事务ID,也就是目前最大事务ID+1。

数据可见性规则是怎样实现的?

数据版本的可见性规则,就是 基于数据的row trx_id 和这个 **一致性视图(Read View)**的对比结果得到的。视图数组把所有的 row trx_id 分成了几种不同的情况(如图)

img

​ 图4 数据版本可见性规则

读取原理:

某事务T 要访问数据 A,先获取该数据 A 中的事务 Id (获取最近操作它的事务的事务ID),对比该事务T启动时刻生成的 Readview:

1、如果在 readview 的左边(比readview都小),表示这个事务可以访问这数据(在左边意味着该事务已经提交)

2、如果在 readview 的右边(比readview都大),表示这个版本是由将来启动的事务生成的,是肯定不可见的;

3、如果当前事务在未提交事务集合中:

a、若 row trx_id在数组中,表示这个版本是由还没提交的事务生成的,不可见;

b. 若 row trx_id不在数组中,表示这个版本是已经提交了的事务生成的,可见。

若不可以访问,获取 上述roll_pointer,通过版本链取上一版本,根据数据历史版本事务ID再重新与视图数组对比。

这样执行下来,虽然期间这一行数据被修改过,但是事务A不论在什么时候查询,看到这行 数据的结果都是一致的,所以我们称之为一致性读。