Mysql事务详解

什么是事务

事务就是保证一个或者多个sql语句都执行成功或者都执行失败,不存在一部分执行成功一部分执行失败的情况。

事务的四大特性

原子性

原子性是指事务是数据库执行的最小单元不可再分割,整个事务中的所有操作要么全部 commit 成功,要么全部失败 rollback,对于一个事务来说,不可能只执行其中的一部分 SQL 操作,这就是事务的原子性。

原子性的实现原理 原子性是通过undo log实现的,我们都知道,事务的开始到结束是从BEGIN TRANSACTIONCOMMENT ,中间出现意外情况就会ROLLBACK并且撤回所有已执行操作恢复到数据的原始状态。 每条数据的变更都会生成一条或两条(update)undo log日志记录,在SQL语句执行之前持久化到磁盘中。那么他是如何恢复的呢?InnoDb引擎为每条数据都默认设置了隐藏字段,包括当前执行事务的id和执行前一个数据所记录的undo log记录,根据这个指针对数据执行逆向操作。

持久性

一旦事务提交,则其所做的修改就会永久保存到数据库中,之后的其他操作或故障都不会对事务的结果产生影响。

持久性的实现原理

MYSQL数据库的数据是需要持久化到磁盘中的,但是如果所有操作都去操作磁盘,那么并发量高了之后效率就会降低,因此引入了缓冲池(Buffer poll )的概念,Buffer poll 中包含了磁盘中部分数据页的映射,也就是我们常说的缓存,当查询数据时,如果数据在Buffer poll 中存在则直接返回,不存在再去磁盘加载,写入数据也一样,如果是在Buffer poll 中,那么直接修改Buffer poll ,然后定期将数据刷回到磁盘。虽然Buffer Pool 的使用大大提高了读写数据的效率,但是也有别的问题,当MySQL宕机,而此时Buffer Pool 中修改的数据还没有刷新到磁盘,就会导致数据的丢失,事务的持久性无法保证。 于是,优秀的程序员们引入了redo log ,当我们对数据进行修改时,除了修改Buffer Pool 中的数据,还会在redo log 中记录这次操作。当事务提交时,会调用fsync 接口对redo log 进行刷盘。如果MySQL宕机,重启时可以读取redo log中的数据,对数据库进行恢复。

隔离性

在MySQL隔离性中,一般有两种情况:

  1. 要求同一时刻只能有一个事务对数据进行写操作,InnoDB通过锁机制来保证这一点。
  2. 在进行读操作的时候,可能出现脏读、不可重复读、幻读的问题。

首先讲第一种情况,MySQL要求同一时刻只能有一个事务对数据进行写操作,InnoDB通过锁机制来保证这一点。

锁机制的基本原理可以理解为:事务在修改数据之前,需要先获得相应的锁;获得锁之后,事务便可以修改数据;该事务操作期间,这部分数据是锁定的,其他事务如果需要修改数据,需要等待当前事务提交或回滚后释放锁。

至于锁机制中的锁,一般就是之前讲到的MySQL锁,大家可以去看看这篇MySQL锁的内容。

接着讲第二种情况,读操作可能出现脏读、不可重复读、幻读的问题。此处牵扯到MVCC。

以InnoDB为例,常见的锁有:

  • 表级锁:最大颗粒度的锁机制,锁定占用的资源最高,并发度最低,但是开销小,加锁快,不会出现死锁;
  • 行级锁:颗粒度最小,锁定占用的资源最少,并发度高,但是开销大,加锁比较慢,InnoDB的行锁是通过给索引上的索引项加锁 来实现的。只有通过索引检索数据,才能使用行锁,否则将使用表锁(锁住索引的所有记录)
  • 页级锁:性能和效率介于表级锁和行级锁;
  • 间隙锁:保证某个间隙内的数据在锁定情况下不会发生任何变化;
  • Next-key LOCK:根据索引,划分为一个个左开右闭的区间;
  • 意向锁:意向锁(IS、IX)是InnoDB数据操作之前自动加的,不需要用户干预。它的意义在于:当事务想去进行锁表时,可以先判断意向锁是否存在,存在时则可快速返回该表不能启用表锁,否则就需要等待。

如何解决第二个问题呢?答案就是MVCC MVCC是以一种并发控制机制,用于在多个并发事务同时读写数据库是保持数据库的一致性和隔离性,它是通过在每个数据行上维护多个版本的数据来实现的,当一个事务要对数据库中的数据进行修改时,MVCC会为该事务创建一个数据快照,而不是修改实际的数据行。

在详细说明MVCC之前,先了解下MYSQL的隔离级别

  • 读未提交:一个事务可以读到另一个事务没有提交的数据,会产生脏读,不可重复读,幻读的问题
  • 读已提交:事务只能读到其他事务提交过的数据,可以阻止脏读。
  • 可重复读:事务对同条数据多次执行的结果是相同的,不会产生数据不一致的情况,可以解决脏读,不可重复读。
  • 可串行化:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。

MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读) InnoDB 实现的 REPEATABLE-READ 隔离级别其实是可以解决幻读问题发生的,主要有下面两种情况:

  • 快照读:由 MVCC 机制来保证不出现幻读。
  • 当前读:使用 Next-Key Lock 进行加锁来保证不出现幻读,Next-Key Lock 是行锁(Record Lock)和间隙锁(Gap Lock)的结合,行锁只能锁住已经存在的行,为了避免插入新行,需要依赖间隙锁。

因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是 READ-COMMITTED ,但是你要知道的是 InnoDB 存储引擎默认使用 REPEATABLE-READ 并不会有任何性能损失。

MVCC实现原理

MVCC实现主要依赖三个元素:

  1. 隐藏字段: MYSQL为每个数据行都设置有默认的隐藏字段,DB_TX_ID (当前执行事务id),DB_ROLL_PTR (指向当前数据行上一个版本数据的指针),DB_ROW_ID(如何没有主键和非空字段默认的主键id)。
  2. undo log:除了用于事务回滚时,将数据恢复到原来的样子,当读取记录时,若该记录被其他事务占用或当前版本对该事务不可见,则可以通过 undo log 读取之前的版本数据,以此实现非锁定读。
  3. read view:主要是用来做可见性判断,当使用普通读操作时,数据库便会生成一个read view, 包含以下内容:
  • m_low_limit_id:目前出现过的最大的事务 ID+1,即下一个将被分配的事务 ID。大于等于这个 ID 的数据版本均不可见
  • m_up_limit_id:活跃事务列表 m_ids 中最小的事务 ID,如果 m_ids 为空,则 m_up_limit_idm_low_limit_id。小于这个 ID 的数据版本均可见 m_idsRead View 创建时其他未提交的活跃事务 ID 列表。创建 Read View时,将当前未提交事务 ID 记录下来,后续即使它们修改了记录行的值,对于当前事务也是不可见的。m_ids 不包括当前事务自己和已提交的事务(正在内存中)
  • m_creator_trx_id:创建该 Read View 的事务 ID

希望拿个好offer,大家加油!

相关推荐
2401_857622667 小时前
SpringBoot框架下校园资料库的构建与优化
spring boot·后端·php
2402_857589367 小时前
“衣依”服装销售平台:Spring Boot框架的设计与实现
java·spring boot·后端
哎呦没8 小时前
大学生就业招聘:Spring Boot系统的架构分析
java·spring boot·后端
_.Switch9 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
杨哥带你写代码10 小时前
足球青训俱乐部管理:Spring Boot技术驱动
java·spring boot·后端
AskHarries11 小时前
读《show your work》的一点感悟
后端
A尘埃11 小时前
SpringBoot的数据访问
java·spring boot·后端
yang-230711 小时前
端口冲突的解决方案以及SpringBoot自动检测可用端口demo
java·spring boot·后端
Marst Code11 小时前
(Django)初步使用
后端·python·django
代码之光_198011 小时前
SpringBoot校园资料分享平台:设计与实现
java·spring boot·后端