MySQL事务

MySQL事务

什么是事务

  • 事务是一组操作, 这组操作要么就是全部成功,要么就是全部失败,什么都不做;
  • 事务是并发控制的基本单位;
  • InnoDB 引擎支持事务,MyISAM 引擎是不支持事务。

事务特性

数据库事务包括如下特性:

  • 原子性(Atomicity)

    • 事务的所有操作,要么全部做完,要么全部不做,不可能停在中间状态;
    • 事务是一个不可分割的整体,就像原子一样不可分割;
  • 一致性(Consistency)

    • 指事务将数据库从一种状态转变为另一种状态时, 数据状态要保证前后一致, 不能有不一致的情况;
    • 事务开始前后,数据库中数据的完整性约束没有被破坏;
  • 隔离性 (Isolation)

    • 当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间, 不会受到其他事务的影响;
    • 并发事务间的修改必须相互隔离;
  • 持久性 (Durability)

    • 事务一旦提交,则其结果就是永久性的。即使发生宕机的故障,数据库也能将数据恢复;

事务并发安全问题

MySQL中并发操作带来的数据安全问题主要包括:

  • 脏读:A事务读到B事务 未提交数据;
  • 不可重复读:同一事务中重复读取相同记录的 不一致;
  • 幻读:同一事务中多次读取记录的 数量不一致;

隔离级别

针对上面出现的几种情况,在标准SQL规范中,定义了4个事务的隔离级别对其进行处理.

  • 读未提交RU, Read Uncommitted):事务A能读取到事务B的 已修改未提交的数据;
  • 读提交RC, Read Committed):事务A读取到的是其他并发事务的 已提交的数据, 并发事务的 未提交数据对其他事务不可见, 实现了 未提交数据的并发隔离;
  • 可重复读取RR, Repeatable Read):事务A在同一事务中可反复读取同一数据, 该数据的值不会受到其他并发事务的影响;
  • 序列化Serializable):提供严格的事务隔离。要求失去序列化执行,事务只能一个接一个地执行,不能并发执行。

这些隔离级别的安全等级和操作性能关系如下:

  • 不同等级隔离级别采用不同的实现方式;
  • 不同等级隔离级别的数据安全等级由上到下一步步提高的;
  • 不同等级隔离级别的性能由上到下一步步降低;
  • 高等级的隔离级别包含解决了低等级隔离级别的问题;
隔离级别脏读不可重复读幻读实现方式性能
读未提交(RU)OOO最好
读提交(RC)OO读不加锁, 写入、修改和删除加锁Oracle默认隔离级别
可重复读(RR)OMVCCInnoDB默认隔离级别
串行化(Serialser)读加共享锁,写加排它锁最差

MVCC

MVCC简介

  • MVCC(Multiversion Concurrency Control,多版本并发控制), 是一种用来解决 读-写冲突的无锁并发控制技术;
  • 可用于实现RR等级的事务隔离级别;
  • MVCC实现依赖于 隐藏字段,版本链读视图;
  • MVVC实现依赖记录行中的隐藏字段包括:
    • trx_id: 事务ID, 用来记录当前事务的ID,
    • roll_pointer: 回滚指针, 用于;

在MVCC并发控制中,读操作可以分成两类:

  • 快照读(snapshot read):
    • 读取的是记录的可见版本 (有可能是历史版本),不用加锁;
    • 包括简单的select语句;
  • 当前读(current read):
    • 读取的是记录的最新版本,需上锁;
    • 包括特殊的select语句包括(加锁, 插入, 更新, 删除等);

在可重读Repeatable reads事务隔离级别下:

  • SELECT时,读取创建版本号<=当前事务版本号,删除版本号为空或>当前事务版本号。
  • INSERT时,保存当前事务版本号为行的创建版本号
  • DELETE时,保存当前事务版本号为行的删除版本号
  • UPDATE时,插入一条新纪录,保存当前事务版本号为行创建版本号,同时保存当前事务版本号到原来删除的行

通过MVCC,虽然每行记录都需要额外的存储空间,更多的行检查工作以及一些额外的维护工作,但可以减少锁的使用,大多数读操作都不用加锁,读数据操作很简单,性能很好,并且也能保证只会读取到符合标准的行,也只锁住必要行。

MVCC在mysql中的实现依赖的是undo log与read view

  • undo log :undo log 中记录某行数据的多个版本的数据。
  • read view :用来判断当前版本数据的可见性

ReadView(读视图)

  • ReadView是MVCC中用来判断当前事务数据视图的窗口;
  • ReadView主要通过以下几个结构来判断当前事务的有效数据视图:
    • trx_ids: 当前并发的活跃事务id列表;
    • up_limit_id: , 当前已经提交的事务号 + 1, 所有<该值id的事务都已提交, 所以对当前事务来说应该都可见;
    • low_limit_id: 当前最大的事务号 + 1, 所有>该值id的事务, ;

ReadView可见性

UndoLog和版本链

  • 回滚日志(UndoLog)用于记录数据更新前的旧记录;
  • 每次对记录更新(包括insert, update, delete)时, 将会生成一条新undolog日志;
  • 每条undolog日志有一个roll_pointer字段, 指向上一条的undolog日志, 最终形成链式结构, 称为版本链;
  • 每条undolog有2种类型之一:
    • insert类型: 插入操作时生成, 事务提交后删除;
    • update类型: 更新或删除操作时生成, pruge线程回收;
  • undolog存储于<表名>.ibdata文件中;

版本链

MVCC的实现流程

mvcc的实现的大体流程如下:

  1. 事务开始时, 生成一个当前事务id(trx_id);
  2. 建立ReadView, ReadView包含了当前活跃的并发事务ID列表及过滤窗口;
  3. 查询数据, 使用ReadView中的版本号对查询数据进行过滤;
  4. 根据ReadView从UndoLog版本链中查询符合要求的数据;
  5. 返回符合要求的数据;

NextKey锁

  • MySQL 通过行锁+间隙锁的方式 解决了RR级别下解决了 幻读的问题;

总结

前面讲的重做日志,回滚日志以及锁技术就是实现事务的基础。

  • 原子性(Atomic): 通过 undo log 来实现的;
  • 持久性(D): 通过 redo log 来实现的;
  • 隔离性(I): 是通过 (读写锁+MVCC)来实现的;
  • 一致性C: 是通过原子性,持久性,隔离性来实现的!!!

参考

  1. Innodb中的事务隔离级别和锁的关系 - 美团技术团队
  2. 『浅入深出』MySQL 中事务的实现 - 面向信仰编程
  3. 深入学习MySQL事务:ACID特性的实现原理 - 编程迷思 - 博客园
updatedupdated2024-08-252024-08-25