数据库之MySQL第三篇事务

事务隔离级别有哪些?不同隔离级别会有什么问题?

事务隔离级别与读的关系?

隔离级别

脏读

不可重复读

幻读

读未提交( read-uncommitted )

读已提交( read-committed )

-

可重复读( repeatable-read )

-

-

串行化 ( serializable )

-

-

-

各种读问题是什么?

读类型

脏读

脏读

一个事务读取到另一个事务未提交的数据

不可重复读

读取的数据被其他事务修改且提交导致多次读取结果不一致

幻读

数据统计时,其他事务新增或删除记录导致多次读取结果不一致

底层是如何解决上面的读问题呢?

MVCC只在REPEATABLE READ和READ COMMITTED两个隔离级别下工作。其他两个隔离级别都和MVCC不兼容,因为READ UNCOMMITED总是读取最新的数据行,而不是符合当前事务版本的数据行。而SERIALIZABLE则会对所有读取的行都加锁。

第一步:维护undo log链
InnoDB为数据库中的每一行添加了三个隐藏字段:DB_TRX_ID(事务版本号)、DB_ROLL_PTR(回滚指针)、DB_ROW_ID(隐藏ID)。

  • DB_TRX_ID:记录了创建/更新这条数据的事务版本号(版本号会递增)。
  • DB_ROLL_PTR:记录了一个指向undo log中历史版本的数据指针。(用来支持回滚操作)
  • DB_ROW_ID:一个自增的隐藏行ID。

InnoDB基于事务版本号、回滚指针这两个字段,可以在undo log中形成一个单向链表,最新版本的数据放在链表头部,历史数据通过DB_ROLL_PTR指针进行关联。

第二步:查询时生成ReadView

查询时创建一个快照(下文称之为ReadView)ReadView包含了以下信息

  • m_ids: 活动事务id列表(活动事务指的是已经开始、尚未提交/回滚的事务)
  • min_trx_id: 最小活动事务id
  • max_trx_id:最大活动事务id
  • creator_trx_id:当前事务id

紧接着InnoDB会通过查询语句定位到最新版本的数据行,并根据以下规则获取到可以访问的数据版本。

  • 如果被访问版本的trx_id,与readview中的creator_trx_id值相同,表明当前事务在访问自己修改过的记录,直接返回该版本的数据;
  • 如果被访问版本的trx_id,小于readview中的min_trx_id值,表明生成该版本的事务在当前事务生成readview前已经提交,直接返回该版本的数据;
  • 如果被访问版本的trx_id,大于或等于readview中的max_trx_id值,表明生成该版本的事务在当前事务生成readview后才开启,此时该版本不可以被当前事务访问,需要通过隐藏的回滚指针从undo log中读取历史版本;
  • 如果被访问版本的trx_id,在readview的min_trx_id和max_trx_id之间,则需要判断trx_id值是否在m_ids列表中?
    • 如果在:说明readview创建时,创建该版本数据的事务还未提交,因此需要通过回滚指针读取历史版本并返回。
    • 如果不在:说明readview创建时,创建该版本数据的事务已经提交,所以直接返回该版本的数据;

可重复读隔离级别下,ReadView只会在第一次查询时创建,同一个事务中后续所有的查询共用一个ReadView,由此便解决了不可重复读的问题。

读已提交隔离级别下,每次查询都会创建一个新的ReadView。新建的ReadView会更新creator_trx_id以外的其余字段,因此不可重复读现象依然存在。但是由于ReadView可以判断出修改此数据的事务是否已经提交,因此可以避免脏读的出现。

RR隔离级别有没有解决幻读问题?

因为MVCC并不能解决幻读的问题。但是MySQL在可重复读级别,通过间隙锁解决了幻读问题,所以MySQL的RR隔离级别是没有幻读问题的。

事务的四大特性分别是什么?MySQL底层是如何实现的?

A undolog

C redolog

I 一致性是事务追求的最终目标,前问所诉的原子性、持久性和隔离性,其实都是为了保证数据库状态的一致性。

D MVCC + Lock

MVCC是什么?他是如何解决隔离性的实现原理是什么?

多版本并发控制,隔离多个事务解决并发读互相干扰。

原理: 版本链 + 读视图

版本链

1、对于InnoDB存储引擎,每一行记录都有两个隐藏列 trx_id、roll_pointer(如果表中没有主键和非NULL唯一键时,则还会有第三个隐藏的主键列row_id)。

2、多个事务并行操作某一行数据时,不同事务对该行数据的修改会产生多个版本,然后通过回滚指针(roll_pointer),连成一个链表,这个链表就称为版本链。

roll_pointer指向的是下一条数据的ID

trx_id开启时根据时间先后顺序自增的整数

row_id/id行唯一标识

读视图Read View

1、在innodb中,每个SQL语句执行前都会得到一个Read View。

2、RV用于判断当前事务可见哪个版本的数据。

核心字段

m_ids:当前系统中那些活跃(未提交)的读写事务ID, 它数据结构为一个List。

min_limit_id:表示在生成Read View时,当前系统中活跃的读写事务中最小的事务id,即m_ids中的最小值。

max_limit_id:表示生成Read View时,系统中应该分配给下一个事务的id值。

creator_trx_id: 创建当前Read View的事务ID

当前事务如何使用Read View判断数据是否对自己可见?

数据的事务id trx_id

生成RV时,RV记录的事务ID信息min_limit_id、max_limit_id、creator_trx_id

ⅰ. 如果数据事务ID小于当前事务ID(trx_id < min_limit_id),表明生成该版本的事务在生成Read View前,已经提交(因为事务ID是递增的),所以该版本可以被当前事务访问。

ⅱ. 如果trx_id>= max_limit_id,表明生成该版本的事务在生成ReadView后才生成,所以该版本不可以被当前事务访问。

ⅲ. 如果 min_limit_id =<trx_id< max_limit_id,需分3种情况讨论

● (1).如果m_ids包含trx_id,则代表Read View生成时刻,这个事务还未提交,但是如果数据的trx_id等于creator_trx_id的话,表明数据是自己生成的,因此是可见的。

● (2)如果m_ids包含trx_id,并且trx_id不等于creator_trx_id,则Read View生成时,事务未提交,并且不是自己生产的,所以当前事务也是看不见的;

● (3).如果m_ids不包含trx_id,则说明你这个事务在Read View生成之前就已经提交了,修改的结果,当前事务是能看见的。

版本链与undolog日志的关系?

● 首先生成一个事务ID=100【事务100】

● 把core_user表修改前的数据,拷贝到undo log 【事务101】

● 修改core_user表中,id=1的数据,名字由孙权改为曹操【事务101】

● 把修改后的数据事务Id=101改成当前事务版本号trx_id,并把roll_pointer指向undo log数据【事务100】地址。

数据库的日志有哪几种?分别有什么用?
数据库的日志主要包括以下几种:
1事务日志(Transaction Log):记录数据库中所有事务的操作,包括事务的开始、提交、回滚等操作。事务日志可以用于恢复数据库,保证数据的一致性和可靠性。
2错误日志(Error Log):记录数据库中发生的错误和异常,包括数据库启动和关闭时的错误、SQL语句执行时的错误、系统错误等。错误日志可以用于排查问题和分析故障原因。
3慢查询日志(Slow Query Log):记录执行时间超过一定阈值的SQL语句,可以用于优化SQL语句和查询性能。
4二进制日志(Binary Log):记录数据库中所有修改数据的操作,包括插入、更新、删除等操作。二进制日志可以用于主从复制和数据恢复。
5撤销日志(Undo Log):记录事务执行过程中对数据的修改操作,可以用于回滚事务。
6重做日志(Redo Log):记录事务执行过程中对数据的修改操作,可以用于恢复数据库。
这些日志在数据库中发挥着不同的作用,可以用于保证数据的一致性和可靠性、排查问题和分析故障原因、优化SQL语句和查询性能、实现主从复制和数据恢复等。在实际应用中,需要根据具体的需求和场景选择合适的日志类型和配置参数,以达到最优的性能和可靠性。
 

13、undolog与redolog区别?
Redo log和Undo log是MySQL中的两种日志,用于保证数据的一致性和可靠性。它们的区别如下:

  1. Redo log:Redo log是一种物理日志,用于记录数据的修改操作。当MySQL执行数据修改操作时,会先将修改操作记录到Redo log中,然后再将修改操作应用到数据文件中。在MySQL崩溃或者重启时,可以通过Redo log来恢复数据的一致性。
  2. Undo log:Undo log是一种逻辑日志,用于记录数据的回滚操作。当MySQL执行数据修改操作时,会先将原始数据记录到Undo log中,然后再将修改操作应用到数据文件中。在MySQL需要回滚事务或者查询历史版本时,可以通过Undo log来恢复原始数据。

因此,Redo log和Undo log的作用不同,Redo log用于恢复数据的修改操作,而Undo log用于恢复数据的原始状态。同时,Redo log是一种物理日志,记录的是数据的物理修改操作,而Undo log是一种逻辑日志,记录的是数据的逻辑回滚操作。

需要注意的是,Redo log和Undo log是MySQL中的两种重要日志,它们的大小和性能对MySQL的性能和可靠性有着重要的影响。因此,在进行MySQL的性能优化和容灾处理时,需要合理配置和管理Redo log和Undo log,以保证MySQL的性能和可靠性。

RR和RC下生成RV的时机是什么时候?

  1. RR(可重复读)隔离级别下生成读视图的时机:
    • 在事务开始时,生成一个初始的读视图。
    • 在每个语句执行前,会根据初始的读视图生成一个新的读视图。
    • 在事务执行期间,读视图不会发生变化,即使其他事务对数据进行了修改。
  1. RC(读已提交)隔离级别下生成读视图的时机:
    • 在每个语句执行前,会根据当前的数据库状态生成一个新的读视图。
    • 在事务执行期间,如果其他事务对数据进行了修改并提交,当前事务的读视图会相应地更新,以反映最新的数据库状态。

需要注意的是,生成读视图的时机并不意味着读操作的时机。读操作可以在生成读视图后的任意时间点进行,但读操作所能看到的数据是根据生成的读视图确定的。生成读视图的目的是为了保证在事务执行期间读取的数据是一致的,不会受到其他并发事务的修改影响。

总结起来,在RR隔离级别下,读视图在事务开始时生成,并在每个语句执行前更新;而在RC隔离级别下,读视图在每个语句执行前根据当前数据库状态生成,并可以根据其他事务的提交来更新。

undo log

MVCC实现原理

初始数据

id k

1 1

2 2

事务操作

读视图何时开启?

begin/start transaction 命令并不是一个事务的起点,在执行到它们之后的第一个操作 InnoDB 表的语句,事务才真正启动。如果你想要马上启动一个事务,可以使用 start transaction with consistent snapshot 这个命令。

第一种启动方式,一致性视图是在执行第一个快照读语句时创建的;

第二种启动方式,一致性视图是在执行 start transaction with consistent snapshot 时创建的。

还需要注意的是,我们的例子中如果没有特别说明,都是默认 autocommit=1。在这个例子中,事务 C 没有显式地使用 begin/commit,表示这个 update 语句本身就是一个事务,语句完成的时候会自动提交。事务 B 在更新了行之后查询 ; 事务 A 在一个只读事务中查询,并且时间顺序上是在事务 B 的查询之后。

这时,如果我告诉你事务 B 查到的 k 的值是 3,而事务 A 查到的 k 的值是 1。

你是不是感觉有点晕呢?

如果把事务 A 的查询语句 select * from t where id=1 修改一下,加上 lock in share mode 或 for update,也都可以读到版本号是 101 的数据,返回的 k 的值是 3。

如果再往前一步,假设事务 C 不是马上提交的,而是变成了下面的事务 C’,会怎么样呢?

事务 C’的不同是,更新后并没有马上提交,在它提交前,事务 B 的更新语句先发起了。前面说过了,虽然事务 C’还没提交,但是 (1,2) 这个版本也已经生成了,并且是当前的最新版本。那么,事务 B 的更新语句会怎么处理呢? 这时候,我们在上一篇文章中提到的“两阶段锁协议”就要上场了。事务 C’没提交,也就是 说 (1,2) 这个版本上的写锁还没释放。而事务 B 是当前读,必须要读最新版本,而且必须加 锁,因此就被锁住了,必须等到事务 C’释放这个锁,才能继续它的当前读。

在 MySQL 里,有两个“视图”的概念:

一个是 view。它是一个用查询语句定义的虚拟表,在调用的时候执行查询语句并生成结 果。创建视图的语法是 create view … ,而它的查询方法与表一样。

另一个是 InnoDB 在实现 MVCC 时用到的一致性读视图,即 consistent read view,用于 支持 RC(Read Committed,读提交)和 RR(Repeatable Read,可重复读)隔离级别 的实现。

RV的生成时机?

RC 每次查询语句都会生成RV

RR 遇到第一条查询语句时生成RV

当前读与快照读的区别?

当前读会阻塞。

InnoDB 里面每个事务有一个唯一的事务 ID,叫作 transaction id。它是在事务开始的时候向 InnoDB 的事务系统申请的,是按申请顺序严格递增的。 而每行数据也都是有多个版本的。每次事务更新数据的时候,都会生成一个新的数据版本,并 且把 transaction id 赋值给这个数据版本的事务 ID,记为 row trx_id。同时,旧的数据版本 要保留,并且在新的数据版本中,能够有信息可以直接拿到它。 也就是说,数据表中的一行记录,其实可能有多个版本 (row),每个版本有自己的 row trx_id。

当然,如果“上一个版本”也不可见,那就得继续往前找。还有,如果是这个事务自己更新的 数据,它自己还是要认的。 在实现上, InnoDB 为每个事务构造了一个数组,用来保存这个事务启动瞬间,当前正在“活跃”的所有事务 ID。“活跃”指的就是,启动了但还没提交。 数组里面事务 ID 的最小值记为低水位,当前系统里面已经创建过的事务 ID 的最大值加 1 记为高水位。 这个视图数组和高水位,就组成了当前事务的一致性视图(read-view)。 而数据版本的可见性规则,就是基于数据的 row trx_id 和这个一致性视图的对比结果得到 的。 这个视图数组把所有的 row trx_id 分成了几种不同的情况。

这样,对于当前事务的启动瞬间来说,一个数据版本的row_trx_id,有以下几种可能:
1.如果落在绿色部分,表示这个版本是已提交的事务或者是当前事务自己生成的,这个数据是可见的;
2. 如果落在红色部分,表示这个版本是由将来启动的事务生成的,是肯定不可见的;

3.如果落在黄色部分,那就包括两种情况
      a.若row_trx_id在数组中,表示这个版本是由还没提交的事务生成的,不可见;
      b.若row_trx_id不在数组中,表示这个版本是已经提交了的事务生成的,可见。

比如,对于图2中的数据来说,如果有一个事务,它的低水位是18,那么当它访问这一行数
据时,就会从V4通过U3计算出V3,所以在它看来,这一行的值是11。

基础

一条记录在MySQL是如何存储的?

MySQL 一行记录是怎么存储的? | 小林coding

日志

undo log、redo log、bin log各自有什么作用?

MySQL 日志:undo log、redo log、binlog 有什么用? | 小林coding

索引

索引优化有哪些方法?

索引常见面试题 | 小林coding

为什么 MySQL 采用 B+ 树作为索引?

为什么 MySQL 采用 B+ 树作为索引? | 小林coding

为什么MySQL建议单表不要超时1千万呢?

MySQL 单表不要超过 2000W 行,靠谱吗? | 小林coding

索引在哪些情况下会失效?

索引失效有哪些? | 小林coding

MySQL是如何加锁的?

MySQL 是怎么加锁的? | 小林coding

分析下面case为什么会产生死锁?

字节面试:加了什么锁,导致死锁的? | 小林coding

临键锁是如何解决删除导致幻读问题的?

MySQL 记录锁+间隙锁可以防止删除操作而导致的幻读吗? | 小林coding

事务

MySQL的事务隔离级别是如何实现的?

事务隔离级别是怎么实现的? | 小林coding

MySQL的可重复读级别完全解决了幻读问题吗?

MySQL 可重复读隔离级别,完全解决幻读了吗? | 小林coding

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值