事务

本文围绕MySQL展开,介绍了事务的四大特性(ACID)、并发事务问题、隔离级别和传播行为,还阐述了事务相关命令。同时,详细讲解了Undo Log的类型、作用、存储位置等,以及MVCC机制,包括当前读和快照读、隐式字段、ReadView等内容,帮助理解数据库并发访问。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

四大特性(ACID)

在这里插入图片描述

  • 原子性(Atomicity): 原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生
  • 一致性(Consistency): 执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的
  • 隔离性(Isolation): 多个事务,事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离
  • 持久性(Durability): 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响

并发事务问题

  • 第一类修改丢失(回滚丢失): A事务回滚时,把已经提交的B事务的更新数据也一并回滚了
  • 第二类修改丢失(覆盖丢失): A事务提交时把B事务已经提交的数据覆盖了,造成B事务所做操作丢失
  • 脏读(Dirty read): 一个事务读到另一个事务未提交的数据
  • 不可重复读(Unrepeatableread): 事务A多次读取同一条数据时,事务B修改该条数据并提交事务,导致事务A每次读取到的数据结果不一致(update影响)
  • 幻读(Phantom read): 事务B对事务A的影响区间内进行增删操作,导致事务A读取到的数据一会多、一会少,就像产生幻觉了一样(insert/delete影响)

事务隔离级别

在这里插入图片描述

  • READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
  • READ-COMMITTED(读取已提交): oracle默认,允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
  • REPEATABLE-READ(可重复读): mysql默认,对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生(通过锁可以解决)
  • SERIALIZABLE(可串行化): 最高的隔离级别,完全服从ACID的隔离级别,所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰(select操作默认加上lock in share mode),可以防止脏读、不可重复读以及幻读

事务传播行为

  • PROPAGATION_REQUIRED(默认)
    • 外部方法没有开启事务,则内部方法会创建自己的新事务,事务间相互独立,互不干扰
    • 外部方法开启事务,则所有内部方法都会加入到外部方法的事务中
  • PROPAGATION_REQUIRES_NEW
    • 无论外部方法有无开启事务,内部方法都会创建自己的新事务,事务间相互独立,互不干扰
  • PROPAGATION_NESTED
    • 外部方法没有开启事务,则内部方法会创建自己的新事务,事务间相互独立,互不干扰
    • 外部方法开启事务,则内部方法的事务都是外部方法的子事务,当外部方法事务回滚,则所有子事务都回滚,当单一子事务回滚,不会影响其他子事务
  • PROPAGATION_SUPPORTS
    • 若外部方法有事务,则支持事务,若没有,则以非事务方式运行
  • PROPAGATION_NOT_SUPPORTED
    • 若外部方法有事务,则挂起,待事务执行完成后,再运行该方法
  • PROPAGATION_MANDATORY
    • 方法必须要有事务,否则抛错
  • PROPAGATION_NEVER
    • 方法不能有事务,否则抛错

事务相关命令

SELECT * FROM information_schema.INNODB_TRX;	// 查询正在运行的事务
SELECT @@tx_isolation	// 查看隔离级别

Undo Log

类型

  • insert undo log: 代表事务在insert新记录时产生的undo log,只在事务回滚时需要,并且在事务提交后可以被立即丢弃
  • update undo log: 事务在进行update或delete时产生的undo log,不仅在事务回滚时需要,在快照读时也需要,所以不能随便删除,只有在快速读或事务回滚不涉及该日志时,对应的日志才会被purge线程统一清除

作用

  • MVCC
  • 事务回滚

存储位置

  • undo log实际上就是存在rollback segment中旧记录链

purge线程

  • 为了实现InnoDB的MVCC机制,更新或者删除操作都只是设置一下老记录的deleted_bit,并不真正将过时的记录删除
  • purge线程自己也维护了一个read view(这个read view相当于系统中最老活跃事务的read view)
  • 如果某个记录的deleted_bit为true,并且DB_TRX_ID相对于purge线程的read view可见,那么这条记录一定是可以被安全清除的

回滚

  • 在回滚的时候,它实际上是做的相反的工作,比如一条INSERT ,对应一条 DELETE,对于每个UPDATE,对应一条相反的 UPDATE,将修改前的行放回去。undo日志用于事务的回滚操作进而保障了事务的原子性

MVCC

简介

  • MVCC(Mutil-Version Concurrency Control),就是多版本并发控制,MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问
  • 在Mysql的InnoDB引擎中就是指在已提交读(READ COMMITTD)和可重复读(REPEATABLE READ)这两种隔离级别下的事务对于SELECT操作会访问版本链中的记录的过程
  • 这就使得别的事务可以修改这条记录,反正每次修改都会在版本链中记录,SELECT可以去版本链中拿记录,这就实现了读-写,写-读的并发执行,提升了系统的性能

原理

当前读和快照读
  • 像select lock in share mode(共享锁),select for update,update,insert ,delete(排他锁)这些操作都是一种当前读,为什么叫当前读?就是它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁
  • 像不加锁的select操作就是快照读,即不加锁的非阻塞读;快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读;之所以出现快照读的情况,是基于提高并发性能的考虑,快照读的实现是基于多版本并发控制,即MVCC,可以认为MVCC是行锁的一个变种,但它在很多情况下,避免了加锁操作,降低了开销;既然是基于多版本,即快照读可能读到的并不一定是数据的最新版本,而有可能是之前的历史版本
隐式字段
  • DB_TRX_ID: 最近修改(修改/插入)事务ID:记录创建这条记录/最后一次修改该记录的事务ID
  • DB_ROLL_PT: 回滚指针,指向这条记录的上一个版本(undolog,存储于rollback segment里)
  • DB_ROW_ID: 隐含的自增ID(隐藏主键),如果数据表没有主键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引
  • DELETE BIT: 索引删除标志,如果DB删除了一条数据,是优先通知索引将该标志位设置为1,然后通过(purge)清除线程去异步删除真实的数据
ReadView
定义
  • 在事务执行的那一刻,会生成数据库系统当前的一个快照,记录并维护系统当前活跃事务的ID列表
组成
  • m_ids: 表示在生成ReadView时当前系统中活跃的读写事务的事务id列表
  • min_trx_id: 表示在生成ReadView时当前系统中活跃的读写事务中最小的事务id,也就是m_ids中的最小值
  • max_trx_id: 表示生成ReadView时系统中应该分配给下一个事务的id值
  • creator_trx_id: 表示生成该ReadView的事务的事务id
RC和RR级别下的InnoDB快照读有什么不同?
  • RC级别:
    • 事务中,每次快照读都会新生成一个快照和Read View, 这就是我们在RC级别下的事务中可以看到别的事务提交的更新的原因
  • RR级别:
    • 某个事务的对某条记录的第一次快照读会创建一个快照及Read View, 将当前系统活跃的其他事务记录起来,此后在调用快照读的时候,还是使用的是同一个Read View,所以只要当前事务在其他事务提交更新之前使用过快照读,那么之后的快照读使用的都是同一个Read View,所以对之后的修改不可见
    • 快照读生成Read View时,Read View会记录此时所有其他活动事务的快照,这些事务的修改对于当前事务都是不可见的,而早于Read View创建的事务所做的修改均是可见
可见性算法
  • DB_TRX_ID == creator_trx_id(可见),代表当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问
  • DB_TRX_ID < min_trx_id(可见),代表DB_TRX_ID所在的记录在Read View生成前出现的,对当前事务可见
  • DB_TRX_ID >= max_trx_id(不可见), 代表DB_TRX_ID所在的记录在Read View生成后才出现的,对当前事务不可见
  • min_trx_id <= DB_TRX_ID < max_trx_id,需要继续判断DB_TRX_ID是否在活跃事务之中
    • 如果在(不可见),代表Read View生成时刻,这个事务还在活跃,还没有Commit,所以该事务修改的数据,在当前事务是看不见的
    • 如果不在(可见),代表这个事务在Read View生成之前就已经Commit了,该事务修改的结果,在当前事务是能看见的
流程

在这里插入图片描述

  • 将要被修改的数据的最新记录中的DB_TRX_ID(即当前事务ID)取出来,与系统当前其他活跃事务的ID去对比(由Read View维护)
  • 如果DB_TRX_ID跟Read View的属性做了某些比较,不符合可见性算法,那就通过DB_ROLL_PTR回滚指针去取出Undo Log中的DB_TRX_ID再比较,即遍历链表的DB_TRX_ID(从链首到链尾,即从最近的一次修改查起),直到找到满足特定条件的DB_TRX_ID
  • 那么这个DB_TRX_ID所在的旧记录就是当前事务能看见的最新老版本
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

发财猪猪

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

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

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

打赏作者

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

抵扣说明:

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

余额充值