萤火小屋

优律的知识库

  • 首页
  • 归档
  • 分类
  • 标签
  • 留言
  • 关于

  • 搜索
消息队列 RabbitMQ Redis 双指针 力扣 动态代理 Git YAML SpringBoot SpringMVC 回溯算法 分治算法 归并排序 快排 手撕 事务 MySQL索引 MySQL 小技巧 Spring Framework Spring 动态规划 Linux Android 贪心算法 操作系统 进程调度模拟 IPv6 数据库 计算机组成原理 计算机基础 栈 Java 静态路由 路由器 交换机 数字通信 网络工程 计算机网络 Web http 大学学习技巧 程序设计 算法

MySQL-InnoDB引擎的事务

发表于 2021-03-18 | 分类于 MySQL | 0 | 阅读次数 303

1 一致性(consistency)

一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不应该被破坏。

1.1 undo log的机制

  1. undo log存放在数据库内部的一个特殊段(segment)中,这个段称为undo段(undosegment)。
  2. undo log是逻辑日志,因此只是将数据库逻辑地恢复到原来的样子。
  3. undo log的另一个作用是MVCC,即在InnoDB存储引擎中MVCC的实现是通过undo来完成。
  4. undo log的产生会伴随着redo log的产生,这是因为undo log也需要持久性的保护。

1.2 undo log与redo log的区别

  1. redo log通常是物理日志,记录的是页的物理修改操作。
  2. undo log是逻辑日志,根据每行记录进行记录。

2 隔离性(Isolation)

可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。

  • 为了性能往往不能100%兼顾隔离性。

2.1 通过lock(锁)实现

2.1.1 粒度

  1. 行锁
  2. 表锁

2.1.2 类型

  1. 共享锁(S):与IX/X锁不兼容
    行锁,允许事务读一行数据
  2. 排他锁(X):与任何锁都不兼容
    行锁,允许事务删除或更新一行数据
  3. 意向共享锁(IS):与X锁不兼容
    表锁,事务想要获得一张表中某几行的共享锁
  4. 意向排他锁(IX):与S/X锁不兼容
    表锁,事务想要获得一张表中某几行的排他锁

总结:

  1. X锁与任何锁都不兼容
  2. IX锁与S/X锁不兼容

2.1.3 机制

  1. 无锁
    MVCC(Multi-Version Concurrency Control):对于正在更新的数据,InnoDB会去读取该行的一个快照数据(undo log)

  2. 加锁
    X锁:SELECT ... FOR UPDATE
    S锁:SELECT ... LOCK IN SHARE MODE

2.1.4 算法

  1. Record Lock:单个行记录上的锁,总是会去锁住(聚簇/主键)索引记录
  2. Gap Lock:间隙锁,锁定一个范围,但不包含记录本身
  3. Next-Key Lock:锁定一个范围,并且锁定记录本身

2.1.5 问题

2.1.5.1 读取

  1. 脏读:某一个事务,读取了另外一个事务中未提交的数据(绝对要避免)

image.png

  1. 不可重复读:某一个事务,对同一个数据前后读取的结果不一致(有时也可以接受,但是要尽力杜绝)

image.png

  1. 幻读:某一个事务,对同一个表前后查询到的行数不一致(往往可以接受,但是尽量避免)

image.png

2.1.5.2 更新

  1. 第一类丢失更新:某一个事务的回滚,导致另外一个事务已更新的数据丢失了

image.png

  1. 第二类丢失更新:某一个事务的提交,导致另外一个事务已更新的数据丢失了

image.png

说明:任何隔离级别都不会发生这类问题,因为对于DML操作需要先加IX锁,它会直接阻塞事务2的行为。

2.1.6 死锁

2.1.6.1 场景

事务1:
update ... where id=1;
update ... where id=2;​

事务2:
update ... where id=2;
update ... where id=1;

2.1.6.2 解决

  1. 超时回滚(被动):innodb_lock_wait_timeout,当一个等待时间超过设置的某一阈值时,其中一个事务进行回滚,另一个等待的事务就能继续进行。
  2. 死锁检测(主动):wait-for graph,采用等待图的方式来进行死锁检测,这是一种更为主动的死锁检测方式。

image.png

2.1.7 升级

InnoDB存储引擎不存在锁升级的问题。
因为其不是根据每个记录来产生行锁的,相反,其根据每个事务访问的每个页对锁进行管理的,采用的是位图的方式。因此不管一个事务锁住页中一个记录还是多个记录,其开销通常都是一致的。

2.2 隔离级别

2.2.1 READ UNCOMMITTED

  • 未解决脏读、不可重复读、幻读问题

2.2.2 READ COMMITTED

  1. 采用Record Lock算法,解决了脏读问题
  2. 采用MVCC,总是读取被锁定行的最新一份快照数据

2.2.3 REPEATABLE READ

  1. 是默认的隔离级别;
  2. 采用Next-Key Lock算法,解决了脏读、不可重复读、幻读问题
  3. 采用MVCC,总是读取事务开始时的行数据版本

2.2.4 SERIALIZABLE

  • 解决了脏读、不可重复读、幻读问题(SELECT ... LOCK IN SHARE MODE)

3 持久性(Durability)

一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中。

使用redo log来解决

3.1 redo log的机制

  1. 当事务提交时,必须先将该事务的所有日志写入到redo log进行持久化,待事务的COMMIT操作完成才算完成。
  2. redo log是顺序写的,在数据库运行时不需要对该文件进行读取操作。
  3. 每次写入redo log文件后,InnoDB引擎都需要调用一次fsync操作。
  4. redo log通常是物理日志,记录的是页的物理修改操作。

3.2 redo log与bin log的区别

  1. redo log是在存储引擎层产生,bin log是在数据库的上层产生的,并且bin log​不仅仅针对于InnoDB存储引擎,MySQL中任何存储引擎对于数据库的更改都会产生二进制日志。
  2. bin log 是一种逻辑日志,其记录的是对应的SQL语句,而InnoDB存储引擎层面的redo log​是物理格式日志,其记录的是对于每个页的修改。
  3. bin log只在事务提交完成后进行一次写入,而redo log​在事务进行中不断地被写入,这表现为日志并不是随事务提交的顺序进行写入的。

4 原子性(Atom)

事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。

一致性、隔离性和持久性的实现是原子性的保证。



文章脑图
# 数据库 # MySQL # 事务
MySQL数据库的索引
手撕快速排序-Java代码
  • 文章目录
  • 站点概览
优律

优律

优律的知识库

83 日志
20 分类
44 标签
E-mail Twitter Instagram
Links
  • CZLisyx - 浮生志
  • Vedfolnir
0%
© 2019 — 2023 萤火小屋——优律的博客网站
网站已勉强运行 
Halo博客系统技术支持