InnoDB 引擎

索引

B+树存储数据量的计算

表初期设计就要考虑调优了.
一个原则:表的每个字段用多大的量?上限严格把控!
不能初学的时候,一个name就varchar255,不行,太浪费
(1)为什么要控制单行数据的大小?这就要涉及B+树咯
B+树 mysql 使用的索引数据的存储结构:(先以聚簇索引举例)叶子节点存储整行数据,非叶子节点存储主键值(如id是bigint,那就是8字节)和向下的指针(6字节)
因此对于bigint类型的mysql的表来说,非叶子节点每条数据占用14字节。
对于mysql的主键,id里1 2 3 这种。刚才说了8个字节。id为1存储80 00 00 01,为3就是 80 00 00 03,这就是8个字节。
(2)那么这种,针对三层的来说,一个根节点有多大呢?16kb的内存页能存多少数据?
161024/14 ≈ 1170,我们这里就用1100举例,好算数。即能存储1100个主键keyId和指针。
这样一来,第二层就有1100个节点。然后呢,每个节点又能存储1100个keyid和指针,然后就可以去第三层了.
第三层就是叶子节点,存数据的了。那第三层大概多少节点?那就是1100^2≈120W了。那这就能存储120W条整行的数据了。
回头来想一开始调优的问题,如果一条mysql的数据没好好规划,假如一行就16kb了,那么三层B+树只能存储120w条数据(??????)。120w叶子节点,一个节点16kb,一条mysql数据16Kb,那只能1个页存储一条。
(3)三层B+树,每次查询3次IO索引,每次咋索引?
用“二分查找索引”,复杂度logn。因为要加载内存页,所以mysql涉及索引查询速度就下来了。16kb内存页不是白加载的,这就是第一次io了。然后在这上面二分查找找到二层的时候,又得加载一次16kb内存页,这就是第二次。完事了又在第二层的这个内存页上二分查找定位三层内存页,还是16kb,第三次io。你看,三层b+树一次查询加载了48kb内存页。
假如我们刚才一个mysql数据是1kb,那么能存储的数据就是120w
16=1920w,数据量一下上来了!这就是我们说的,调优的第一步,根据需求,严格控制一个字段的大小,原因就在这个b+树的“高扇出性”。

索引数据结构对比

  1. 提到b+树了,bigint了,那就深入讲一讲:为啥要用b+树?二叉树/hash/B树不行吗?<br /> 1)**B B+树 最大的区别就是在【叶子节点和非叶子节点的存储上】**。B树上非叶子节点上也存储数据,这样的话很影响**树的高度**!你非叶子节点存指针的空间都拿去存数据了,那么能存储的指针数量不久少了吗<br /> 二叉树一个样,B+树是N叉树,同样的数据量,二叉树的高度就很高了,顶不住<br /> 2hash呢,查询是1,树都是logn。<br /> 这里深入一下。首先,**不支持模糊查询**,%和like这些都搞不了,你搞了这些hashcode咋算?算不了了,那么你这个桶位就找不到了,就这一点,hash就不能用。<br /> 第二点,hash**不支持范围查询**;B+树底层是双向环形链表,都是排好序的,查起来快得很;hash呢?不连续啊。<br /> 还有hash冲突的问题,不能联合查询的问题等,反正hash突出一个不合适。<br /> hash这里第二点:范围查询,这里需要引申一下“**最左前缀原则**”,这啥东西?比如说刚才这个模糊查询,匹配到前缀就狂查,因为是排好序的,就可以连着查,查到前缀不匹配位置才停止,非常好用。找到第一个,按照叶子节点排好的序,直接查.

聚集索引 & 二级索引

(1)扯了半天,该说说“聚集索引”:
每一张表,只有一张聚集索引B+树,通过主键id搞得存储结构就是主键id和指针叶子节点呢,存储的就是select*整行的数据
(2)“辅助索引”也是B+树
首先,叶子节点存“所有的索引”,以及对应的“主键索引”,不存整行数据
根据二级索引里找到的主键索引,进行【回表】!根据查到的主键去查找聚簇索引。第二点,对于非叶子节点,存储6字节指针,然后是索引。
第三,二级索引可以有多个。
(3)这个时候就要说了——“联合索引”
联合索引也得用B+树存储。联合索引是二级索引吗 ?
看你怎么定义。如果默认定义普通联合索引,如 name 和 age,那就是个普通的辅助索引,没有主键嘛。
(4)索引的 “随机性”
到这,聚簇/二级/联合索引就说完了。那么现在基于这些索引说一说。
首先呢,你咋定义索引呢?即哪个列索引合适?要看他的【随机性】,随机性越高越好!举几个例子,比如你呢,性别作为索引,就俩情况,做索引干嘛?其次呢,日期这种随机性就很高,带上时分秒的话。
怎么看随机性?使用命令show index,看 cardinality的值,越接近1那么离散型越高。当然也不是让你瞎搞,而是呢,你得根据业务吧。
(5)覆盖索引
索引呢,这块就差不多了。
这里补充一个“覆盖索引”,这个其实不该算索引,算“特效”。
假如还是以age和name为索引,如果用select name from table where name = 张三,这就覆盖索引的效果。因为name 有辅助索引,其叶子节点存储了索引值,他发现你只要name,那么此时就不用回表了!那你这select*的话,就必须回表了。
说白了就是覆盖二级索引,不用回表了。
索引这块差不多了,更详细的,生产上咋添加删除啥的,后面讲!

索引失效

上期说的是mysql中的第一步:单表调优,然后引导到了 索引,具体见上节
但是索引部分没有结束,本期要聊的第一个话题:怎么使用索引?索引的失效?
索引的使用方式有很多,我们只要避开这些索引失效的场景,绝大多数情况下使用都不会失效!
(所有)索引失效场景如下:

LOL 加减乘除(第1~4种)

  1. Llike,即模糊查询。当然并不是所有的like都失效,上节课最后那个Like就没失效,前半部分具体,后半部分模糊没事。 但是,like “%”**放在开头了,那G了.**<br /> O:用or。但是or也不是一用就GG。对于A or B,如果AB都建立的索引,那么此时索引不失效。**当 A B只要有一个不是索引” ,那就失效**。当然,如果AB都不是索引,那么连索引失效都谈不上,和索引屁关系没有!<br /> L:联合查询。如果AB定义为联合索引(AB),这样的话,如果语句为(这里没说字段,后面再讨论):select from table where A > 3 and B > 5,会用索引。select from table where B > 5,不会用索引。 总结一下,如果**创建的联合索引中,where中没有从 “联合索引的第一个索引”(例子中的A)开始索引的话,会索引失效**。<br /> 加减乘除:**索引字段进行了加减乘除,那么就失效**。

not/null(第5~6种,注意,这俩这里分开的)

  1. not:非。**涉及到这种 “非/父结果集”,索引都会失效**。如这里的语句,age是索引,这个写法就是失效的——select from table where age != 10。<br /> null:对于is nullis not null这种,并不一定会失效。如果还拿age这个字段举例,select from table where age is not null,这个就“可能会用到索引”,这里涉及一个mysql版本的问题,这个会在最后一部分详解。 <br /> **mysql中,允许索引这个字段为null,但是b+树只处理不为null的**,比如你100条数据,有2null,那么B+树只处理另外98age,**即“不对null值进行索引创建”**,另外俩就单独飘着呢(好像没说怎么解决???)。 <br /> 如何避免这种情况?mysql中创建字段时,例如age这种在业务上不可能为null的,**一定要设置成 非空字段!强制非空!**确保将来加索引的时候能够把这一列“完全”放入B+树节点上。 <br /> 如果呢,对于有些包含null的情况,**建议为这种字段设置 defalut 默认值,保证这个字段不出现null的情况**!

no method(第7种,这次这俩就是一个了,不像之前还分开的)

  1. mysql 中**索引用到一些 mysql 内置函数的时候,索引失效**:select date from table where dateadd ( fieldname , -1) = 2022128日(???这里语句没懂??意思是想取“昨天的数据”?)。这里 dateadd 函数给谁用呢?fieldname就是字段名,基础上-1了。你可以dateadd =( currentdate , -1),在这处理当前日期-1不就得了,没必要非要处理 字段。<br /> 其他method就不举例了。<br /> 反正用到了 mysql 内置函数的话,留个心眼,看看失效了没有.

no convert 类型转换函数(第8种)

  1. 比如说,员工id,就是搞得的七八位,十来位。位数比较长,也不知道将来会不会更长,所以一般把id设置为varchar的形式,当然bigintlong型都可以;varchar就是为了方便。现在呢,select …… from where id = 1 2 3 4 5 G了,设置的是varchar,结果你写了个int,那**mysql就调用了“内置函数convert”**,把12345自动convertvarchar,导致索引失效。<br /> 加个单引号嘛,id = 12345’。<br /> 补充依据,join查询的时候也会导致“索引失效”,如 join Aid = Bid,俩表索引都是id,会不会失效?看情况嘛,如果一个id varchar,一个是int,也会失效,因为比较的时候也发生了类型变换。(join还有其他失效的情况吗???)

离散读:我就是我,不一样的版本,不一样的我(第9种,20:30)

不一样的mysql版本表现不同。
select from table where A > 3,注意是,A是辅助索引。这里会使用到索引呢?
版本1:因为是,所以为了避免回表,不适用A
版本2:使用A,拿到id,回表查询。虽然从3次IO变成了6次IO,但是因为A排序了,所以效率还是不错。
那么,哪个版本对呢?都没错!mysql 5.6是一个分界点.
5.6以前(不包括5.6),是版本1,不走索引,因为是
,导致“三次IO,多次查询”。
(1)思路1:即根据三次IO找到A>3的起点,假设拿到45678这5个数,返回到主键索引后就“瞎几把乱插/随机查”,可能先查8,在查4,在查6 7 5..这样导致,5条数据回五次表,这一下IO变成3×5=15了。
(这部分很重要,引入“离散读”的概念,根据前面的叙述,我们发现,离散读发生在:二级索引叶子节点出来后,回聚簇索引查询整行数据的时候
(2)思路2:5.5的时候,Mysql想着还不如直接去索引里找 ,3次IO找到内存节点,按照内存页一条一条开始找比A打的,这样的话IO虽然只有三次,但是需要扫描全表。
(3)5.5索引使用情况:所以说,这俩思路都挺麻烦的!
因此5.5就得做一个“判断/阈值”:小于20w条或者该表总量一半的数据,采用聚集索引;太多的话,走辅助索引。
通过对5.5的分析,我们知道,这个标准其实不好判断,所以5.6的时候,引入了“multirange优化——mrr优化”,就是为了解决这个“5.5离散读的问题”,就之前的例子,别15次IO了,把二级索引找到的id排序,并存到内存,排好序的id就好在聚簇索引中回表了


隔离,锁,MVCC 这三个下节课讲:
隔离:mysql的4种隔离机制,Uncommited到commited,repeated read到serlize,结合MVCC怎么做
MVCC(一致性非锁定读:目的/多版本并发控制:名字)
锁:每种隔离机制下,用什么锁,锁和MVCC冲突吗

事务 & 锁

本期搞定三个板块:隔离 + mvcc + 锁,用以下4个主题串联:
什么是mvcc?
不同事务隔离级别下面mvcc的表现.
不同事务隔离级别下mysql出现的问题.
mysql 锁如何解决这些问题.


一、什么是mvcc?

  1. MVCC叫”多版本并发控制“,他的**目的是【一致性非锁定读】**.<br /> 先对MVCC进行一个总结:**mvcc mysql 基于自己的 ”回滚机制“ ,为并发场景下的 ”读操作“ 进行的优化**。<br /> MVCC——多版本并发控制,控制的是什么?为谁控制?==>【**为”读“控制**】<br />==> 因此,MVCC需要达成两个共识:<br />**(1)基于 mvcc undolog 机制(下节课讲日志),undolog 就是用来做回滚的 Log,这个是 mvcc 底层原理;**<br />**(2mvcc 的多版本控制为了 mysql “并发场景下的读操作” 实现的多版本并发控制,为了达到【并发环境下读操作不需要被锁定】的目的,来加快Mysql的读取**

二、不同事务隔离级别下面mvcc的表现.

  1. 首先,mysql 几种隔离级别?四种,从低到高:**读未提交,读已提交,可重复读,串行读.**<br /> 默认隔离界别:可重复读

1 首先说默认级别(可重复读)时的mvcc的使用情况和表现.

  1. 搞个例子,以3条线程举例:AB线程为修改线程,C为读取线程。AC同时向一条数据发起写和读的操作。注意,C发起操作就等于开启事务(?事务到底约束的是什么?读还是写?线程A还是线程C???)了,但是它不着急,它先sleep一下。<br /> 此时A没睡觉,它去修改了,A首先生成一版快照(undolog),防止回滚,这里假设A修改好了,然后提交了;这时,对于这条数据,它几个版本?俩!MVCC的多版本就在这里了。<br /> 此时B又来了,也是个写线程,它也要改,那么它也要给当前状态来个快照,然后b修改,提交。此时几个版本?3个:,a修改前的版本,a修改完成后b修改前的版本,b修改完成后的版本(不是4个吗?a修改后的这个和b修改前的重合了???)。<br /> (而且这个版本信息存在哪里的?一起存?不同线程分开存???)<br /> 此时c呢?c还睡觉呢,假设这个时候,它醒了,那他用哪个版本(注意,这里级别默认是rr级别)?==>读取”最原始“的那条数据——即A修改前的数据,**从 MVCC 版本快照来说,读取的是【发起事务时的原始快照】**<br /> 我们先不评价对错,我们现在只说 mvcc 在这种情况下,怎么表现的。

2 再说默认级别(读已提交)时的mvcc的使用情况和表现.

  1. 还是刚才的例子。AC同时开启,AB先后修改提交,C再读取,C读取的哪个?读取B提交的版本,**即读【最新提交的版本】**

3 再说说(读未提交)

  1. 看定义:能读取没提交的数据。 <br /> 这还需要 MVCC 吗?想一下这个过程:AC同时开启,A修改了,没提交,但是C能读到。这数据在哪呢?此时这个数据还没有完成持久化,还在缓存中呢,还是个临时数据,这都能读取到。此时啊,【mvcc基本没用了】,并发情况下,有写有读,这个时候已经乱了套了,无所谓版本控制了。<br />==>所以对于 读未提交,谈什么版本控制?没必要嘛

4 最后再说说最高隔离级别(串行化)

  1. 直接上结论:<br /> ==>所以对于 串行化,也别谈MVCC。对一张表的读写操作都会把表锁了,A改不完,c读不了;C没读完,其他也读不了(这是什么锁??表锁??),因此啊,也**没必要说MVCC了,此时你读的都是表里的数据,都不会去读版本的数据**。此时呢,相当于”没有并发“

5 mvcc 与 事务隔离性

  1. 刚才没有评价 rr rc 的好坏,先思考一个问题:对于RC,读取的是”最新的快照“,AC同时发起的是【两个事务】,B是另外一个事务,假如 C 能读取到最新的B提交的快照,那么此时”事务是隔离的嘛“?<br /> 没有隔离。你想想,mysql 事务的ACID中这个 I 是隔离性,这没隔离,咋解释?<br /> AB直接影响C的结果,明显MVCC违背了”事务的隔离性“!<br /> RR情况下就没这个问题,读取到的是原始版本,这才叫隔离性!

三、不同事务隔离级别下 mysql 出现的问题.

从低往高说.

1 读未提交 ==> 导致【脏读】

  1. **没提交的数据,没有持久化,是临时数据,是 脏数据,这里读到的话就是 ”脏读“。**<br /> 面试官问”脏读“,你咋回答?<br /> 你不能说什么是脏读,如果你说脏读就是读取到了其他线程未提交的数据,这么回答差点意思。你得这么说:**我们现在几乎碰不到脏读的情况,除非将mysql的隔离级别定义为读未提交;也就是说Mysql自己就KO脏读问题了**。

2 读已提交/rc ==> 导致【幻读】or【不可重复读】

(1)是什么
这里有区别吗?没啥大区别。但是如果你想钻牛角尖,非要知道这俩啥区别,也是可以的。
区分方式:
不可重复读:读取同一条数据,两次读取(value:值)不同.
幻读:执行同一条查询语句,两次查出来的(value的count:数据量)不同.
所以这个东西没必要较这个死真,一定要说清楚?理解就行嘛
(2)怎么导致的?mysql如何避免的?
首先达成一个共识:在 rc 这种模式下会造成 ”幻读“ 和 ”不可重复读“.
好家伙,回到刚才讲的 ABC。刚才AB是修改线程,C是读线程。现在改一改就行,模拟这两种情况。
搞一个 ”不可重复读“ 的场景:AC同时发起事务,此时C睡觉了,A修改并完成提交;C醒了,然后执行读操作,读取”最新快照“,然后C又睡了;此时B又来了,又改并提交;C又醒了,它又查,查出来的就是B提交的。这就不可重复读:一次读A提交的,一次读B提交的,不一样。
再来一个”幻读“场景,就在这上面改:B那里,它没改数据,直接把数据删了,C醒来一看,没数据了!这就是幻读。
(3)
==> 读已提交/rc 明显【违背了ACID中的I,即隔离性】,事务和事务之间是需要隔离的,就好比java之间的线程隔离,事务也需要隔离的。
(4)
当然,幻读 和 不可重复读 也会在 读已提交中出现.
脏读 不会在 rr 中出现.
之前没提,这里补充一下

3 串行化 ==> 导致”慢“

这是唯一问题,但是能解决所有安全问题

4 rr 级别

==>KO掉了上述三大问题:依靠rr默认级别下的 next key lock 机制,直接引入第四部分!

四、如何解决 “不可重复读” 和 “幻读”

(这部分参考JavaGuide部分的总结,前提是先搞懂什么是”快照读“,什么是”当前读“)
在RR(很重要啊这一点)级别下:
(1)KO不可重复读:
MVCC就可以解决不可重复读!
(2)KO幻读:
MVCC可以解决【快照读/一致性非锁定读】的幻读.
Next key lock 可以解决 【当前读】的幻读
因此,【MVCC + Next key lock】 联合才能解决幻读问题!其中Next key lock只能在RR级别下使用.

JavaGuide补充【一致性非锁定读 和 锁定读(幻读的两种形式)】

#一致性非锁定读 = 快照读 ==> RR级别下,[MVCC] 即可 KO

  1. 对于 [一致性非锁定读(Consistent Nonlocking Readsopen in new window](https://dev.mysql.com/doc/refman/5.7/en/innodb-consistent-read.html)的实现,通常做法是加一个版本号或者时间戳字段,在更新数据的同时版本号 + 1 或者更新时间戳。查询时,将当前可见的版本号与对应记录的版本号进行比对,如果记录的版本小于可见版本,则表示该记录可见<br /> 在 InnoDB 存储引擎中,[多版本控制 (multi versioning)open in new window](https://dev.mysql.com/doc/refman/5.7/en/innodb-multi-versioning.html) 就是对非锁定读的实现。如果读取的行正在执行 DELETE 或 UPDATE 操作,这时读取操作不会去等待行上锁的释放。相反地,InnoDB 存储引擎会去读取行的一个快照数据,对于这种读取历史数据的方式,我们叫它快照读 (snapshot read)<br /> 在 Repeatable Read 和 Read Committed 两个隔离级别下,如果是执行普通的 select 语句(不包括 select ... lock in share mode ,select ... for update)则会使用 一致性非锁定读(MVCC)。并且在 Repeatable Read 下 MVCC 实现了可重复读和防止部分幻读

#锁定读 = 当前读 ==> RR级别下,[MVCC + Next-key Lock] 可KO

如果执行的是下列语句,就是 锁定读(Locking Reads)open in new window

  • select … lock in share mode
  • select … for update
  • insert、update、delete 操作

在锁定读下,读取的是数据的最新版本,这种读也被称为 当前读(current read)。锁定读会对读取到的记录加锁:

  • select … lock in share mode:对记录加 S 锁,其它事务也可以加S锁,如果加 x 锁则会被阻塞
  • select … for update、insert、update、delete:对记录加 X 锁,且其它事务不能加任何锁

在一致性非锁定读下,即使读取的记录已被其它事务加上 X 锁,这时记录也是可以被读取的,即读取的快照数据。上面说了,在 Repeatable Read 下 MVCC 防止了部分幻读,这边的 “部分” 是指在 一致性非锁定读 情况下,只能读取到第一次查询之前所插入的数据(根据 Read View 判断数据可见性,Read View 在第一次查询时生成)。但是!如果是 当前读 ,每次读取的都是最新数据,这时如果两次查询中间有其它事务插入数据,就会产生幻读。所以, InnoDB 在实现Repeatable Read 时,如果执行的是当前读,则会对读取的记录使用 Next-key Lock ,来防止其它事务在间隙间插入数据

日志

redo log

1 redo log 有什么用?

事务隔离性由之前讲述的锁来实现
redo log称为重做日志,用来保证事务的持久性
redo通常是物理日志记录 的是 页的物理修改 操作。

2 redo log 如何体现 持久性?

重做日志用来实现事务的持久性,即事务ACID中的D。其由两部分组成:
(1)是内存中的重做日志缓冲(redo log buffer),其是易失的;
(2)是重做日志文件(redo log file),其是持久的。

3 redo log 写入过程

InnoDB是事务的存储引擎,其通过 Force Log at Commit 机制实现事务的持久性,即当事务提交(COMMIT)时,必须先将该事务的所有日志写入到重做日志文件进行持久化,待事务的COMMIT操作完成才算完成。
为了确保每次日志都写入重做日志文件,在每次将重做日志缓冲写入重做日志文件后,InnoDB存储引擎都需要调用一次 fsync 操作。
由于重做日志文件打开并没有使用 O_DIRECT 选项,因此重做日志缓存先写入文件系统缓存。为了确保重做日志写入磁盘,必须进行一次fsync操作。由于fsync的效率取决于磁盘的性能,因此磁盘的性能决定了事务提交的性能, 也就是数据库的性能。

image.png

4 redo log 刷盘机制

通过参数innodb_flush_log_at_trx_commit用来控制重做日志刷新到磁盘的策略。
(1)该参数的默认值为1表示事务提交时必须调用一次fsync操作。还可以设置该参数的值为0和2。
(2)0 表示事务提交时不进行写入重做日志操作,这个操作仅在master thread中完成,而在master thread中每1秒会进行 一次重做日志文件的fsync操作
(3)2 表示事务提交时将重做日志写入重做日志文件,但仅写入文件系统的缓存中,不进行fsync操作。在这个设置下, 当MySQL数据库发生宕机而操作系统不发生宕机时,并不会导致事务的丢失。而当操作系统宕机时,重启数据库后 会丢失未从文件系统缓存刷新到重做日志文件那部分事务。

举例: 逐条插入50万条数据。
innodb_flush_log_at_trx_commit = 1时: 用时 2 分 13 秒。50 万次写入重做日志;fsync 操作 50 万次。
innodb_flush_log_at_trx_commit = 0时: 用时 23 秒。约23次写如重做日志;fsync 操作约 23 次。
innodb_flush_log_at_trx_commit = 2时: 用时 35 秒。50万次写入重做日志(仅缓存);fsync 操作 0 次。

虽然用户可以通过设置参数innodb_flush_log_at_trx_commit为 0 或 2 来提高事务提交的性能,但是需要牢记的是,这种设置方法丧失了事务的ACID特性。而针对上述存储过程,为了提高事务的提交性能,应该在将50万行记录插入表后进 行一次的COMMIT操作,而不是在每插入一条记录后进行一次COMMIT操作。这样做的好处是还可以使事务方法在回滚时回滚到事务最开始的确定状态。
正确方法:innodb_flush_log_at_trx_commit = 1,将50万条数据在一个事务或者多个事务中分派提交,减少fsync次 数.

bin log 与 redo log 的不同

在 MySQL 数据库中还有一种二进制日志(binlog),其用来进行POINT-IN-TIME(PIT)的恢复及主从复制 (Replication)环境的建立。从表面上看其和重做日志非常相似,都是记录了对于数据库操作的日志。然而,从本质上来看,两者有着非常大的不同
(1)存储引擎
首先,重做日志是在InnoDB存储引擎层产生,而二进制日志是在MySQL数据库的上层产生的,并且二进制日志不仅仅针对于InnoDB存储引擎,MySQL数据库中的任何存储引擎对于数据库的更改都会产生二进制日志。
(2)内容形式
其次,两种日志记录的内容形式不同。MySQL数据库上层的二进制日志 bin log是一种逻辑日志,其记录的是对应的SQL语句
而InnoDB存储引擎层面的重做日志是物理格式日志,其记录的是对于每个页的修改

undo log

1 undo log 的 作用1:回滚

重做日志记录了事务的行为,可以很好地通过其对页进行“重做”操作。但是事务有时还需要进行回滚操作,这时就需要undo。因此在对数据库进行修改时,InnoDB 存储引擎不但会产生 redo,还会产生一定量的undo。这样如果用户执行的事务或语句由于某种原因失败了,又或者用户用一条ROLLBACK语句请求回滚,就可以利用这些 undo 信息将数据回滚到修改之前的样子。

2 undo log 的查看

redo存放在重做日志文件中,与redo不同,undo存放在数据库内部的一个特殊段(segment)中,这个段称为 undo段(undo segment)。undo段位于共享表空间内。可以通过py_innodb_page_info.py工具来查看当前共享表空间中undo的数量

3 undo log 的 作用 2:配合MVCC

除了回滚操作,undo的另一个作用是MVCC,即在 InnoDB 存储引擎中 MVCC 的实现是通过undo来完成。当用户读取 一行记录时,若该记录已经被其他事务占用,当前事务可以通过undo读取之前的行版本信息,以此实现非锁定读取

4 undo log 会产生 redo log

最后也是最为重要的一点是,undo log会产生redo log,也就是undo log的产生会伴随着redo log的产生,这是因 为undo log也需要持久性的保护。

主从