MySQL事务原理总结

redo日志

  • 一般组成:类型、表空间ID、页号、具体内容
  • Mini-Transaction
    • 对页面的原子访问
    • 一条语句可以包含若干个Mini-Transaction
    • 乐观插入:只用记录一条MLOG_COMP_REC_INSERT类型的记录
    • 悲观插入:数据页剩余空间不足,导致页分裂,需修改多处–>产生多条redo日志。这些操作必须是原子性的–>Mini-Transaction,对应redo日志中的一组记录
  • redo日志刷盘
    • log buffer空间不足时: 超过log buffer总容量一半
    • 事务提交时
    • 将脏页刷新到磁盘时,将对应的redo日志刷盘
    • 周期性的刷盘
    • 关闭服务器时
    • 建立检测点(checkpoint)时
  • redo日志文件:
    • ib_logfile0, ib_logfile1, …
    • 系统变量innodb_log_files_in_group指定个数
    • 循环使用日志文件0~n
    • 新的日志会覆盖掉旧的checkpoint之前的日志
  • log sequence number
    • log sequence number(lsn) 当前总共已经写入的redo日志的字节数(log block header,log block trailer也计入),用于指向redo记录
    • flushed_to_disk_lsn 指示已经刷入磁盘的日志的最后位置
    • flush链表中的缓冲页记录:
      • oldest_modification:指向 第一次修改该页的Min-Transaction开始处 的lsn
      • newest_modification: 指向 最近一次修改该页的Min-Transaction开始处 的lsn
  • checkpoint
    • checkpoint_lsn:在此之前的redo记录对应的操作所修改的页,已经从Buffer Pool刷入磁盘。checkpoint_lsn之前的redo记录可以从日志文件中覆盖
    • checkpoint_lsn = flush链表中页的oldest_modification的最小值
    • 请添加图片描述

InnoDB-undo日志

  • 事务Id-在事务中第一次执行增删改操作时分配
  • InnoDB的数据页记录中roll_pointer字段为指向其对应的一条undo的指针
  • undo日志格式
    • INSERT操作undo日志-字段:
      • 本条日志结束(下条日志开始)的地址
      • 日志类型(TRX_UNDO_INSERT_REC)
      • 日志编号
      • 主键各列的值
      • 本条日志开始的地址
    • DELETE操作:
      • delete mark:将记录的deleted_flag标志位设位1
      • purge: 当删除语句所在的事务提交后,后台将标记了的记录移入垃圾链表中(等待被重用)
    • DELETE操作undo日志字段:
      • 本条日志结束(下条日志开始)的地址
      • 日志类型(TRX_UNDO_DEL_MARK_REC)
      • 日志编号
      • 对应表的id
      • 记录头信息的前4个bit
      • 旧记录的trx_id的值
      • 旧记录的roll_pointer值 (指向上一条undo日志,形成版本链)
      • 主键信息
      • 索引列信息
      • 本条日志开始的地址
    • UPDATE操作:
      • 不更新主键,更新后的列的长度与更新前的一样大:就地更新
      • 不更新主键,更新后的列的长度与更新前的不一样大:删除旧记录(直接移入垃圾链表),再插入新记录
      • 更新主键:删除旧记录(普通的delete操作相同 delete mark),再插入新记录
    • UPDATE操作undo日志:
      • 不更新主键:TRX_UNDO_UPD_EXIST_REC类型:与delete操作的TRX_UNDO_DEL_MARK_REC相似,记录主键和更新前列的值
      • 更新主键:删除旧记录时产生TRX_UNDO_DEL_MARK_REC类型undo记录;插入新纪录时产生TRX_UNDO_INSERT_REC类型记录
  • undo日志分两类:
    • TRX_UNDO_INSERT: TRX_UNDO_INSERT_REC类型的undo日志. 该类型的undo日志在事务提交后可以直接删除
    • TRX_UNDO_UPDATE: 除TRX_UNDO_INSERT_REC类型的undo日志. 该类型的undo日志在事务提交后不删除,为MVCC服务
  • FIL_PAGE_UNDO_LOG页面
    • 用于储存undo日志
    • undo日志页面组成双链表结构(一个双链表对应一个段Undo Log Segment)
    • 一个undo日志链表中只储存一种类型的undo日志
    • 一个事务运行时可产生4个undo日志链表(Undo Log Segment)
      • 普通表的TRX_UNDO_INSERT类型undo日志的链表
      • 普通表的TRX_UNDO_UPDATE类型undo日志的链表
      • 临时表的TRX_UNDO_INSERT类型undo日志的链表
      • 临时表的TRX_UNDO_UPDATE类型undo日志的链表
    • 每一个Undo Log Segment的第一个页面储存着关于该段的信息
      • TRX_UNDO_STATE属性-该Undo页面链表的状态:
        • TRX_UNDO_ACTIVE:活跃状态—事务正在写入undo日志
        • TRX_UNDO_CACHED: 被缓存—等待被重用
        • TRX_UNDO_TO_FREE: 等待被purge
        • TRX_UNDO_PREPARED

事务隔离级别

  • 不一致性:
    • 脏写(dirty write): 一个事务修改了另一个未提交事务修改过的数据
    • 脏读(dirty read): 一个事务读取了另一个未提交事务修改过的数据
    • 不可重复读(non-repeatable read): 一个事务修改了另一个未提交事务读取的数据(前后两次读出的结果不一样)
    • 幻读(Phantom): 一个事务写入了符合某搜索条件的数据,导致另一个事务前后两次按该条件查询时得到的结果不一致(对MySQL,可能是UPDATE、INSERT、DELETE导致)
  • 隔离级别
    • InnoDB通过,保证不会发生脏写
    • 隔离级别脏读不可重复读幻读
      READ UNCOMMITTED 未提交读可能可能可能
      READ COMMITTED 已提交读不可能可能可能
      REPEATABLE READ 可重复读不可能不可能可能(在一定程度上避免)
      SERIALIZABLE 可串行化不可能不可能不可能
  • 设置隔离级别: set [global | session] transaction isolation level <4个隔离级别>
  • MVCC(Multi-Version Concurrency Control 多版本并发控制)原理
    • 聚簇索引记录 和 undo日志 通过roll_pointer属性组成链表 – 版本链
    • 聚簇索引记录 和 undo日志 中记录着对其进行改动的事务的id(trx_id) – 版本链中该产生该版本的事务的id
    • ReadView(一致性视图 )(主要记录创建时刻活动的事务有哪些)属性:
      • m_ids:生成ReadView时,当前系统中活跃的读写事务的id列表
      • min_trx_id:m_ids中的最小值
      • max_trx_id:m_ids中的最大值
      • creator_trx_id:生成该ReadView的事务的id
    • 对于某个ReadView,判读记录的某个版本是否可见(即判断 产生这个版本的事务 是否在 产生该ReadView前 提交,已提交则可以访问):
      • 若被访问版本的trx_id与ReadView的creator_trx_id相同(该版本由该事务创建),可以访问
      • 若被访问版本的trx_id小于ReadView的min_trx_id(产生该版本的事务在该ReadView生成前已提交),可以访问
      • 若被访问版本的trx_id大于ReadView的max_trx_id(产生该版本的事务在该ReadView生成后才开启),不能访问
      • 若被访问版本的trx_id在[min_trx_id, max_trx_id]中
        • 且trx_id在m_ids中(产生该版本的事务在ReadView生成时还未提交),不可访问
        • 且trx_id不在m_ids中(产生该版本的事务在ReadView生成时已提交),可以访问
    • 访问某条记录时,顺着版本链寻找可以访问的最新的版本进行访问
    • READ COMMITED 隔离级别:每一次执行查询都会生成一个ReadView,保证读取到已提交的事务
    • REPEATABLE READ 隔离级别:事务的第一次查询时生成ReadView,保证之后每一次读取相同的记录都是相同的值
    • 使用二级索引进行查询的情况
      • 二级索引页面的Page Header中的RAGE_MAX_TRX_ID属性:最大的修改了该页面的事务的id(最新的修改对于的事务)
      • 若ReadView的min_trx_id大于PAGE_MAX_TRX_ID(已提交) 可访问
      • 否则,回表按版本链查询
    • purge的执行
      • purge操作:将无用的Undo日志 和 标记为删除的记录 彻底删除
      • 事务提交后其Undo日志链表 将 加入到其对应的回滚段的History链表中
      • 请添加图片描述

  • 读-读:无影响
  • 写-写:需要在写时加锁,避免脏写
  • 读-写、写-读:
    • 方式1:读取操作使用多版本并发控制(MVCC),写时加锁
    • 方式2:读、写操作时都加锁 (多)
  • 读操作:
    • 一致性读(Consistent Read)–采用MVCC进行读取操作:普通SELECT语句在READ COMMITTED、REPEATABLE隔离级别下读
    • 锁定读(Locking Read)–读取前加锁:
      • SELECT … LOCK IN SHARE MODE : 读取前加S锁
      • SELECT … FOR UPDATE :读取前加X锁
  • 写操作:
    • DELETE: 对记录加X锁,再delete mark
    • UPDATE:
      • 未修改记录的键,列大小不变:加X锁,原地修改
      • 未修改记录的键,列大小改变:加X锁,彻底删除该记录,再插入新记录
      • 修改记录的键,对原记录进行DELETE操作,再INSERT新记录
    • INSERT: 插入新记录,其收到隐式锁保护
  • S、X、IS、IX锁
  • InnoDB锁
    • 表级别S锁、X锁:对表执行DDL语句时加锁(一般情况使用server层的元数据锁,而不适用InnoDB的表级S、X锁)
    • 表级别IS锁、IX锁
    • 表级别AUTO-INC锁:控制自增列的值的自增(可用系统变量innodb_autonic_lock_mode控制其是在整个语句完成后释放,还是获取值后释放)
    • 行级别锁:
      • Record Lock(LOCK_REC_NOT_GAP):仅锁一条记录
      • Gap Lock(LOCK_GAP):给某条记录加该锁->锁定该记录前的间隙,不允许在此记录前插入。用于在一定程度上解决幻读
      • Next-Key Lock(LOCK_ORDINARY):Record Lock + Gap Lock 锁定该记录和其前面的间隙
      • Insert Intention Lock(LOCK_INSERT_INTENTION):在插入记录时,若被Gap Lock堵塞。则生成一个Insert Intention Lock表明等待插入
      • “隐式锁”:在内存中没有实际的锁结构的锁。
        • 新插入的记录在事务提交前需要用锁保护,以防出现脏读和脏写。
        • 可以使用记录的trx_id进行判断插入该记录的事务是否已经提交,以阻止其他事务的操作—达到锁的作用–隐式锁
        • 其他事务尝试对加了“隐式锁”的记录加X、S锁时,会为该“隐式锁”生成一个锁结构,在让尝试施加的X\S锁等待
  • InnoDB锁的内存结构
    • 锁所在的事务信息
    • 索引信息:对于行锁,需记录加锁的具体索引(聚簇索引/二级索引)
    • 表锁/行锁信息 (指向表/记录)
    • type_mode:锁的类型。表锁/行锁; S/X/IS/IX/AUTO-INC锁; Record/Gap/Next-Key/InsertIntention锁;
  • 加锁大致过程
    • 需要加锁的语句:
      • SELECT … LOCK IN SHARE MODE
      • SELECT … FOR UPDATE
      • DELETE
      • UPDATE
    • 隔离级别小于等于READ COMMITTED时,只对符合条件的记录加Record Lock锁(仅锁记录)
    • 隔离级别大于等于REPEATABLE READ时,不仅对符合条件的记录加Next-Key Lock锁,对在索引中扫描过的符合索引条件下推条件的记录都加Next-Key Lock锁。(防止插入符合条件的记录,避免幻读)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
MySQL事务实现的原理主要涉及ACID特性以及锁机制。 ACID是指事务的四个特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。原子性表示事务的所有操作要么全部成功,要么全部失败回滚;一致性保证了事务执行前后数据库的完整性;隔离性确保并发执行的事务相互之间是隔离的,互不干扰;持久性保证了一旦事务提交,其结果将永久保存在数据库中。 MySQL使用锁机制来实现事务的隔离性。当多个事务同时对数据库进行读写操作时,为了避免数据的不一致性,MySQL会使用不同的锁来保证事务的隔离性。 MySQL提供两种类型的锁:共享锁(S锁)和排他锁(X锁)。共享锁允许事务进行读操作,但不允许进行写操作;排他锁允许事务进行读写操作。当一个事务需要对某个数据进行修改时,它会先申请排他锁,其他事务需要读取该数据时,需等待排他锁释放。 MySQL中还存在多个隔离级别,包括读未提交、读提交、可重复读和串行化。每个隔离级别会对并发事务的读写操作进行不同程度的锁定控制,从而保证事务的隔离性。 总结来说,MySQL事务的实现原理是通过ACID特性和锁机制来保证事务的原子性、一致性、隔离性和持久性。锁机制通过共享锁和排他锁来控制对数据的读写访问,不同的隔离级别则决定了锁的粒度和控制方式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

绫零依

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值