1 、我创建了⼀个没有select权限的⽤户, 执⾏select * from T where k=1 , 报错“select command denied”,并没有报错“unknown column”,是不是可以说明是在打开表之后才判断读取的列不存在?
答:这个是⼀个安全⽅⾯的考虑。你想想⼀个⽤户如果没有查看这个表的权限,你是会告诉他字段不对还是没权限?如果告诉他字段不对,其实给的信息太多了,因为没权限的意思还包含了: 没权限知道字段是否存在。

2、wait_timeout 是客户端 ⾮交互式的连接时间,如果程序连接mysql SERVER,是交互连接, 关联的时间参数为interactive_timeout, 这两个时间参数需要尽量⼀致吗,⼀般设置多少合适?
query_cache_size 参数虽然不⽤了,我想确认下,关闭情况是 query_cache_size=0 要匹配参数
query_cache_type=off吗? 还是直接query_cache_size=0 即可?
答:第⼀个问题:是的,这两个尽量设置成相同。值的话取决于业务。如果你⾯对的是成熟的开发(⽐如公司内部团队),可以设置⼩些,分钟级别就⾏。
第⼆个问题:这两个都可以,不过⽤query_cache_type会好些(代码判断路径更短) 。

3、⽂⾥遇到临时内存会跟随⻓连接的存在⽽存在,直到连接被销毁。想问的是,这部分临时内 存是由于什么产⽣?为什么不提前释放呢?mysql有⽤到内存池吗?
答:排序、变量这些会占⽤内存,如果要复⽤,以前释放反⽽影响性能,MySQL没法⾃⼰决定,所以5.7之后提供了⽅法让客户端有命令来做;MySQL的内存池⼀般说的是查询缓存和引擎
⾥⾯的(⽐如InnoDB 的buffe pool), 跟线程内缓存不是⼀个概念。

4、⽂中join的例⼦,内表和外表 都有⾼区分度条件的情况下,先过滤出两表符合条件的记录, 再对这些记录做join,可不可以?感觉这样性能更⾼。
答:MySQL确实有你提到的这种算法,叫做index_merge. 不过我们这个case⾥⾯不会,由于实现的原因,MySQL⾄今没有⽀持跨表index_merge。

5、末尾问题有个疑问。是在分析器⾥⾯检查的列是否存在。且⽂章中说到执⾏器会检查有没有 权限。那为什么⼀个没有查询权限的⽤户进⾏查询语句,提示的是"没有权限"⽽不是"列不存 在"。按照这个逻辑权限应该是在执⾏器做的,那这个"权限不存在"是哪个模块检查的?
答:查询的时候权限不存在是在执⾏前单独多的⼀次判断,是为了安全考虑。就是如果没有权 限,你连“字段不存在”这个信息都不应该暴露给客户端。

6、如果你⽤的是 MySQL 5.7 或更新版本,可以在每次执⾏⽐较⼤的操作, 请问⽐较⼤的操作的判断维度是什么呢? 即怎么判断是⽐较⼤的操作?
答:其实就是类似于很多数据排序,或者⼀个操作复杂的存储过程。这个是可以在测试环境稳定复现的。 如果语句都差不多,或者⽐较随机,或者可以⽤另外⼀种⽅法,⽐如⼀个连接执⾏了
N个语句以后做reset这个动作。

7、既然在链接阶段已经通过权限表获取了这个该连接所具有的权限,那么在执⾏阶段再检查⼀ 次的意义何在?
答:执⾏器阶段会碰到需要再判断权限的情况,这时候读内存中事先存好的权限,⽽这个权限是在连接器阶段算出来存进去的。

8、请教个业务操作表的场景,如果对⼀个业务数据表的操作既有查询(分⻚查询批处理),⼜ 有更新,此场景下都是操作主库好呢?还是说查询⾛从库,更新操作⾛从库?您有没有好的建议呢,如果遇到了此类业务场景您会怎么分析解决此困扰呢?
答:必须做语句分类,延迟敏感的⾛主库,不敏感的⾛从库。

9、上⾯说如果开启缓存查询在不命中的情况下开始执⾏分析器,分析语句,那么我发送语句的
key是什么时候⽣成的还是直接拿语句作为key,这时候我过我语句的列中包含了错误的列名在这个阶段是不检查的吗?
答:就是语句做key, 这时候还没到分析器阶段,确实还没检查。 不过不影响逻辑正确性,你分析的时候要带上失效逻辑哈。

10、我们有什么办法实时获取⽬前已查询到多少⾏的数据了?⽐如我⽤php语⾔怎么调⽤
rows_examined实时获取查询的进度?
答:查询进度是没有的,如果是查询结束后,看查到多少⾏,就是MySQL_num_rows. 不过这个跟rows_examined不⼀样哦。

11、权限为什么不在句法分析之前呢,此时已经完成了词法分析,知道了查询的表和列了,完 全可以分析。
答:执⾏过程可能会有触发器这些,还是得到这⼀步才判断。 但是为了安全,有⼀个precheck 会在优化器之前判断⼀下权限。

12、关于在连接器⽅⾯,在连接器中怎样给新的连接归类为⻓连接还是短连接的,判断的标准 是什么?
答:不⽤判断,连接建⽴成功后就是连接,断开就是断开。只是我们建议减少创建连接到次数。

13、⽂中说明权限判断是在执⾏器中进⾏的,那么假如查询的语句命中了查询缓存,按流程就 直接返回了,那这种情况权限判断是怎么处理的呢?
答:从query_cache查到结果后,结果⾥⾯有表的信息,要做权限验证的,权限不够还是报错处 理。

14、c mysql接⼝连接设置好mysql_ping⾃动重连之后。查询时候会⾃动调⽤吗?还是要⾃⼰主动定时检测。
答:虽然会⾃动了,但是如果是程序⾥有⽤到事务,还是需要处理的,否则事务被拆成两个(前
⾯⼀半⾃动回滚)业务还不知道,也不是好事哦。

15、在mysql中的多字段模糊查询同⼀个值!怎么写的执⾏效率⾼?
答:MySQL ⽀持全⽂检索(fulltext index)的,⽐顺序遍历快,不过相⽐于ES这样的系统,
MySQL 的全⽂检索能⼒还是有待加强。

16、“MySQL 在执⾏过程中临时使⽤的内存是管理在连接对象⾥⾯的”
请问 “管理在连接对象⾥⾯” 这个怎么理解呢?是还有管理在其它什么地⽅的吗?是因为管理在对象⾥⾯才会被 OOM 的吗?
答:每个连接对应⼀个THD对象,这些内容都在这个对象⾥。

17、关于不建议⽤查询缓存的,虽然查询缓存命中率很低,但是⼀旦命中不是就能减少查询时 间吗。不使⽤查询缓存是能节省去查询缓存中匹配查询的流程,还有什么其它好处吗?从你说的内容中我还不是很明⽩不⽤查询缓存带来了什么好处?

答:不是,不使⽤查询缓存就不需要维护,就是查到结果不⽤写到查询缓存去,这个才是最⼤的收益哦。

18、查询缓存的时候需要执⾏select语句,这个时候不需要经过分析器对语法词法进⾏分析吗? 答:查询缓存不是”select语句”,是查询内部的⼀个数据结构。

19、java⽤jdbc或者mybatis连接数据库,对于定时任务(每天执⾏⼀次),连接器会在8⼩时
⾃动断开链接?那每天执⾏的时候是重新获取连接?
答:你要看下这个客户端的代码实现。看看他是不是⾃⼰会重新获得连接,或者是不是会维持⼼跳。

20、Write-Ahead Logging-先写⽇志再写磁盘,空闲的时候写⼊磁盘。那么如何判别空闲呢? 假如我增加⼀条数据 ,⽽这条数据只写⼊了⽇志 ,还没来得及写进磁盘,⽽我还要读取刚写⼊的这条数据怎么办呢?
答:读内存。⾄于空闲,系统有判断规则的,⽐如QPS这类。

21、问题1:
在缓存开启状态下, 要判别sql是select还是其他类型,保证只有select才去查询缓存,那么这个判断操作是何时进⾏的呢? 我猜测在查询缓存之前, 但是分析器也要对sql关键字进⾏分析,再分析⼀遍岂不是重复? 因为我没有看源码,所以这⼀块不明⽩, 如果我来设计的话, 可能是把查询缓存这个模块在分析器阶段来执⾏, 由分析器进⾏关键字解析,判断是何种sql语句,如果是select, 再去查询缓存, 如果不是继续往下进⾏好像更合理,看到⼤家都说先查询缓存,没有命中再去分析器, 感觉不太通。
问题2:
在mysql中通过“explain extended和show warnings”命令来查看的sql, 是不是优化器重写之后的sql? 以前我看到⼀本书中提到,也可能是⼀篇博客,mysql的单表的union查询其实会优化成or查询, 但是show warnings看到的还是union查询语句,所以有此疑问。
问题3:
update等sql清空缓存是在sql执⾏完返回结果给客户端之前还是之后, 还是说同时进⾏的, 会不会存在清空缓存失败, 但是告诉客户端update success, 缓存的地⽅⼀直不是很清楚, 所以正好 will be removed in a future releas, 真是⼀个⼤快⼈⼼。

答:1. 实际上肯定是要判断出是select语句, 但是之后的解析都没做哈。 要理解成“包含在内”也可以。
2.Show wanings不会把重写后的显示出来,只是格式化后。
3.清空缓存失败这个不太可能,这个内部结构的操作没什么依赖。

22、数据库所在的docker配了80G内存。但因数据库配置⽂件⾥的参数配置不好,通过计算, 达到了1024G,⽽,现在已经到达容器内存100%了。之后我把参数改到合理值,但内存还没 降,应该是保持会话的没⽣效,新会话⽣效了。现在数据库不能重启,也不能把会话都kill掉。要怎么处理才能使内存降下来的?
答:会话不能kill? 这样程序应该升级,正常的程序要能够处理异常连接连接断开哦。

23、如果⽤户名密码认证通过,连接器会到权限表⾥⾯查出你拥有的权限和执⾏阶段去查询有 没有select的权限这两个有什么区别?
答:连接器阶段是去系统表读数据,结果放在变量;执⾏器使⽤这个变量。

24、⽂中写到,执⾏器会调⽤引擎提供的借⼝查询数据。是不是可以认为,最终遍历数据是引 擎来完成当的。
答:“遍历”这个动作是执⾏器让做的,引擎给了两个接⼝:取第⼀个、取下⼀个。

25、⽂章提到:
1.建⽴连接成功之后,在连接器阶段会先到查询缓存查看是否存在该 SQL 的缓存。
2.针对⼀条 SQL 语句,在分析器的词法分析阶段,从关键字 select 中识别出是⼀条查询语句。
这⾥有⼀个疑问,在分析器阶段才识别出 SQL 是查询语句,⽽在连接器阶段,如何决定是否需要到查询缓存看看呢?
答:嗯,确实是“识别出这个是⼀个查询缓存”这个动作是在连接器之后,查询缓存之前的,但是在查询缓存之前也只做了这⼀步⼩⼩的解析哈。

26、MySQL的连接池就是把⽤完的链接放回池⼦⾥,给别的业务线程复⽤,变短链接为⻓链接。
MySQL的连接池是在InnoDB 的buffe pool⾥⾯的吗?和MySQL企业版的thread_pool的实现原理类似吗?

答:1. 不是,连接池是server层的。
2. Thread_pool是线程池,给不同的连接复⽤线程;连接池是复⽤连接。

27、MySQL 拿到⼀个查询请求后,会先到查询缓存看看,之前是不是执⾏过这条语句。之前执
⾏过的语句及其结果可能会以 key-value 对的形式,被直接缓存在内存中。key 是查询的语句,value 是查询的结果。如果你的查询能够直接在这个缓存中找到 key,那么这个 value 就会被直接返回给客户端。
⽼师请问关于上⾯的这段,如果执⾏语句相同,仅仅是条件不同,会⾛查询缓存么? 如 :select * from user where id=6
select * from user where id=7 这两句.
答:不会,查询缓存要求语句⼀摸⼀样才⾏,别说你这个参数变了,就算两个语句差异只是多⼀个空格都不⾏哦。

28、时间区间查询,between and跟<=>=有性能差异吗? 答:没有,不⽌时间区间,所有类型这两个都是等价操作。

29、执⾏器去操作引擎获取数据的时候,因为引擎查询数据也需要时间,那执⾏器是不是有个 等待的过程?您说的rows_examined是记录执⾏器每调⽤引擎获取数据刚时累加,那是不是可以这样理解在⼀个查询过程中,执⾏器会间隔⼀段时间去引擎获取数据?如果是这种的话,我⼜有⼀个疑问了,执⾏器怎么知道引擎已经查询完了,最终返回数据的呢?
答:不是。执⾏器是同步调⽤引擎接⼝,不是异步调⽤。执⾏过程中累计的。

30、问题1:和其他同学有类似的疑问:关于没有权限操作表的时候的报错:
如果分析器已经知道了“unknown column” 之后, 为什么还会报错: “select command
denied”。你之前的回答是:由于安全考虑,⽤户没有权限操作这个表,那么也不需要告诉⽤户这么多,直接告诉⽤户没有权限。
【重点疑问】如果是按照这样的话,且对表的权限检查都在执⾏器的话,那么每个sql在执⾏的时候⾄少都会执⾏到执⾏器吗?
因为同时看到⽂中在执⾏器章节:于是就进⼊了执⾏器阶段,开始执⾏语句。开始执⾏的时候, 要先判断⼀下你对这个表 T 有没有执⾏查询的权限,如果没有,就会返回没有权限的错误,如下所示。

SELECT command denied to user ‘b’@‘localhost’ for table ‘T’ 。
问题2:两位测试结果不⼀样,A的测试结果是Complete optimizer trace;B从他的描述来看, 测试结果应该是分析器。没有本项⽬上的MySQL权限,等明天⾃⼰本地版下好后,测试下结果。
答:1. 不是的,由于⼯程实现的原因,会有前置检查。
2. 这个也类似,其实如果看调⽤路径,执⾏器阶段都在mysql_parse函数⾥⾯调⽤的。 做这些阶段的拆分我觉得主要是⽅便我们从概念上理解,⼯程实现的时候确实不是1234这么按部就班 的。

31、执⾏ mysql_reset_connection 来重新初始化连接资源,我想请问下,如果在主从DB服务器执⾏这个操作会影响主从同步吗?
答:不会,这个操作没有写binlog,只是更新连接的线程内部数据。

32、“在⼯程实现上,如果命中查询缓存,会在查询缓存放回结果的时候,做权限验证。查询也会在优化器之前调⽤ precheck 验证权限。”不是很理解这块。
1.命中查询缓存之前在连接阶段不是已经做过权限验证了吗,为什么查询缓存放回结果的时候要再做权限验证。
2.优化器之前调⽤ precheck 验证权限和执⾏阶段的权限验证有何区别。
答:1. 连接阶段只是“获得权限信息”, 真正开始查询动作,才判断“有没有操作这个表的权限”

2. 不同的阶段, 执⾏器阶段是判断⼀些关联操作,⽐如更新⼀⾏的时候,由于触发器会再更新别的表,这种情况。

33、⽂中执⾏器模块给的例⼦sql是select * from ,引擎层返回了⼀整⾏数据,请问⽼师如果我只是select id from ,引擎层是只返回id⼀列,还是⼀整⾏。
答:只取id的话,引擎就只返回id给执⾏器。

34、怎样查看是⻓连接还是短连接,定期断开⻓连接怎样配置?另外最近做性能测试时发现⼤并 发⽤户同时连接MySQL时会有部分⽤户连接超时(设置的最⼤连接数1000,实际使⽤的最⼤连 接不会超过200),关于MySQL连接管理这块该怎样优化?
答:那你这个就是典型的短连接了,建议改成⻓链接⽅案。

35、为什么分析器完了还要⾛查询缓存?不是之前查询缓存没命中吗?查询缓存是在分析器阶 段之前的。
答:只是如果有更新语句,要去失效查询缓存。

36、MySQL 在执⾏过程中临时使⽤的内存是管理在连接对象⾥⾯的。这些资源会在连接断开的时候才释放。这⾥的内存是什么?不是BP,对吧。
答:是的,不是BP。 BP是在InnoDB⾥⾯维护的,只有InnoDB才;执⾏过程中的临时内存,是在连接对象⾥。

37、第⼀次调⽤的是“取满⾜条件的第⼀⾏”这个接⼝,之后循环取“满⾜条件的下⼀⾏”这个接
⼝,这些接⼝都是引擎中已经定义好的。这⾥⾯的接⼝怎么理解,我们在数据库中说的接⼝是什么。
答:这⾥的接⼝是指“引擎定义好的函数调⽤⽅法”,只是这个定义不是乱定义的,MySQL是⼀个插件式的结构,要求每个引擎以固定的⽅式提供调⽤⽅法。

38、skip-name-resolve这个参数是不是都需要配置优化呢,之前由于这个参数没有配置导致
sql查询特别慢,好多接⼝也因为这个响应时间特别⻓,请问⽼师域名解析是在连接器阶段吗? 还有这个参数是否为必须优化参数呢?
答:是在连接器阶段,建议全部设置为 skip-name-resolve。

39、mysql connection 内存占⽤过多,有没有办法在shell中看到具体的内存占⽤呢?
mysql_reset_connection 能不能再shell中执⾏?
答:官⽅版本还没有⽀持直接看内存消耗的,不能再shell执⾏的,是⼀个c的api。

40、请问⽼师,短连接和⻓连接是怎么设置的?是连接数据库的时候有什么参数吗?
答:不是,这是“⾏为”,⽐如连接完,执⾏⼀个查询,就断开,这是短连接;执⾏⼀个查询, 不断开,下次查询还⽤这个连接,持续使⽤,就是⻓链接。

41、短链接。 假设 代码执⾏2次select语句。每次都会重新建⽴连接吗? 答:这个是程序⾏为,看你程序怎么写的哈,也不⼀定就是2个。
⽐如以前⽤php写web⻚⾯,⼀个⻚⾯逻辑执⾏过程中⽤同同⼀个连接,这个⻚⾯⽤完就关掉连接。再刷新⻚⾯的时候再创建⼀个新连接,这个我也认为是短连接。

42、“输完命令之后,你就需要在交互对话⾥⾯输⼊密码。虽然密码也可以…”
如果是⽣产上⾯的数据库,为了查询,不在交互模式⾥直接输⼊密码,那么按照正确流程是如何进⾏连接的?场景是:在⽣产环境访问数据库。
发散⼀下,如果为了要养成良好习惯,我们在测试或者开发环境也不直接输⼊密码,那在测试或者开发环境怎样访问数据库是正确的?
答:1. 最好是在交互模式⾥⾯输⼊密码。
2. 对,测试或开发环境,最好也是交互模式⾥⾯输⼊密码。

43、逻辑架构图中,查询缓存有连接器和分析器两条线,是否说明有时候直接从连接器去查询缓 存查看是否命中,有时候会先经过分析器之后再去找查询缓存?如果是的话,为什么会有两种情况呢?缓存的key是⼀条sql语句吗?
答:分析器那条线是“更新”查询缓存。

44、MySQL 在执⾏过程中临时使⽤的内存是管理在连接对象⾥⾯的。 这句话啥意思? 答:就是说在线程断开的时候,内存肯定就会回收,不跟别的线程混⽤。

45、“数据库⾥⾯,⻓连接是指连接成功后,如果客户端持续有请求,则⼀直使⽤同⼀个连接。短连接则是指每次执⾏完很少的⼏次查询就断开连接,下次查询再重新建⽴⼀个。”想问下⻓连 接和短连接是不是⼈为的主观上来判断的,还是通过某些量化标准来定义的?
答:确实有主观成分,衡量标准就是“⼀次连接持续期间,执⾏了多少个sql语句”。

46、我可以认为redo log 记录的是这个⾏在这个⻚更新之后的状态,binlog 记录的是sql吗? Redo log不是记录数据⻚“更新之后的状态”,⽽是记录这个⻚ “做了什么改动”。
答:Binlog有两种模式,statement 格式的话是记sql语句, row格式会记录⾏的内容,记两条,更新前和更新后都有。

47、我想问下如果提交事务的时候正好重启那么redo log和binlog会怎么处理?此时redo log处于prepare阶段,如果不接受这条log,但是binlog已经接受,还是说binlog会去检查redo log的状态,状态为prepare的不会恢复?
答:Binlog如果已经接受,那么redolog是prepare, binlog已经完整了对吧,这时候崩溃恢复过程会认可这个事务,提交掉。 (你可以分析下这种情况下,是否符合我们要达到的“⽤binlog恢

复的库跟原库逻辑相同” 这个要求)。

48、有两个地⽅不太理解,⼀是,binlog也可以被引擎操作,那么binlog除了由执⾏器记录逻辑信息,是否引擎也会参与进binlog的记录?另外就是⼩结中提到的将log⽂件在磁盘中持久化, 这是⼀个怎样的操作?因为⼀想到外部磁盘总感觉和内存相⽐会很慢,每次更新都持久化磁盘会不会影响效率?
答:记录binlog这个动作是执⾏器做的,引擎会提供信息;写盘点代价⽐内存⾼的。但是这个是没法避免的。除⾮业务可以接受主机掉电丢数据。对于不能接受的业务,这个是绕不开的成 本。 所以幸好我们已经过了机械硬盘时代。

49、⽐如因为删表等操作需要回滚,但不希望数据库执⾏binlog中全部的语句 ⽐如说删除语句不执⾏的话 可以通过⾃⼰⼿动筛选数据库执⾏的binlog类型吗?还是必须都要执⾏呢?
答:可以在线程⾥⾯单独设置 是否使⽤binlog, 也可以单独设置binlog格式,前提是要有super 权限。

50、也就是说状态恢复的过程会去同时检查redo log和binlog?不然怎么能确定⼀个prepare的redo log已经写好了binlog,因为不检查的话不能确定到底是写好了binlog之后奔溃的还是写之前奔溃的,也就不能确定这个prepare log的合法性。如果检查的话那么不⽤两阶段提交好像也没什么问题,⽆论先写哪个⽇志都可以。
答:很好的思考,你考虑⼀下这个场景,redolog没写,binlog写好了,怎么算。 注意由于
binlog写完了,在备份恢复回来以后是会重放这个事务的。

51、在两阶段提交那部分,每当⼀个动作完成后,会有事件通知吗?⽐如在binlog写⼊成功后, 进⾏commit redo log,这个commit动作在成功或者失败后,会有回调或者事件通知吗?我感觉如果有通知机制的话,能更好的保持两个log的⼀致性,⽼师怎么认为呢?
答:这个就涉及“组提交”概念。是的,有通知,commit完成然后才继续后⾯的流程(⽐如返回
ack给客户端)。

52、redo log是为了快速响应SQL充当了粉板,这⾥有两个疑问:

  1. redo log本身也是⽂件,记录⽂件的过程其实也是写磁盘,那和⽂中提到的离线写磁盘操作有何区别? 2.响应⼀次SQL我理解是要同时操作两个⽇志⽂件?也就是写磁盘两次?

答:你的理解是对的。
1.写redo log是顺序写,不⽤去“找位置”,⽽更新数据需要找位置。
2.其实是3次(redolog两次 binlog 1次)。不过在并发更新的时候会合并写。

53、请问⽤redolog恢复时还写binlog吗?反之呢?
答:崩溃恢复过程不写binlog了,⽤binlog恢复实例(或搭建备库)的时候,是会写redolog 的。

54、频繁的更新写⼊,导致redolog过⼤,适合短周期的备份? 答:Redolog固定⼤⼩,不会“导致过⼤”的。

55、binglog ⼀般建议设置啥模式,假如row模式binglog 怎么回复? 答:建议设置row模式,row模式⽇志⼀样的可以⽤于重放的。

56、既然write pos和checkout都是往后推移并循环的,⽽且当write pos赶上checkout的时候要停下来,将checkout往后推进,那么是不是意味着write pos的位置始终在checkout后⾯,最多在⼀起,⽽这和⽼师画的图有些出⼊,不知道我的理解是不是有些错误。
答:因为是“循环”的,图中这个状态下,write_pos 往前写,写到3号⽂件末尾,就回到0号继续写,这样你再理解看看“追”的状态。
说明⼀下,⽂中“write pos和checkpoint之间的是’粉板’上还空着的部分,可以⽤来记录新的操作。” 这句话,说的“空着的部分”,就是write pos 到3号⽂件末尾,再加上0号⽂件开头到
checkpoint 的部分。

57、innodb结构为内存池和后台线程 , 其中内存池⾥存在redo⽇志缓冲 和 缓冲池,其中缓冲池以⻚为单位缓冲磁盘数据 。
问 题 : 1.之前⽼师说更新语句时执⾏器调⽤innodb存储引擎先从内存读数据,指的就是从innodb缓存池中读取数据把,没有的话就去读取磁盘找, 找到后再同步到缓冲池。
2.在 server层中, 连接器先 查询缓存 , 这个缓存指的也是 innodb 的缓冲池么。mysql 通过公有的缓冲组件调⽤ innodb 的缓冲池来获取值。 获取不到在⾛ 分析器 --> 优化器 --> 执⾏器的流程 。
3.从缓冲中取的时候 ,为什么没有 分析器 --> 优化器 --> 执⾏器 的流程呢?

答:1. 是的。
2.不是,server 层的查询缓存就是(query_cache) Server层跟innodb的buffer pool是没有交集的,引擎没有暴露这个接⼝,只有操作数据的接⼝。
3.跟2类似,不是⼀个东⻄哦。

58、redo 是引擎提供的,binlog 是server ⾃带的,⽂中提到前者⽤在crash的恢复,后者⽤于库的恢复,两者是否在某种程度上是重复的?如果在都是追加写的情况下,是否两种⽇志都能⽤于 crash 与 库 的恢复呢?
答:Crash-safe是崩溃恢复,就是原地满⾎复活;binlog恢复是制造⼀个影分身(副本)出 来。

59、是否在redo没写时重启数据库,会使事务丢失?这个交易也失败。怎样情况重启会出现数据库数据不⼀致呢?oracle都存在重启后数据不⼀致的情况。数据不⼀致后怎么恢复?
答:Redolog没写,这个不算丢数据的。交易失败是预期的,也应该这样处理。就靠业务重试。

60、⽼师,我现在有点搞不懂 mysql 缓存机制和 innodb缓存机制的关系 ,以及 redo⽇志也有缓存,如果在缓存中还没同步到 redo ⽇志⾥的时候,mysql 发⽣的 crash 时,是否更新记录就真的没了?
答:会没了,但是没关系,因为redolog都没写完,表示你事务还没提交,中间出了错,本来就 应该没的。

61、⽼师 如果 binlog 或者 redo log 在写⼊磁盘的过程中故障了怎么保障数据⼀致? 答:整个事务回滚,数据也没有,⽇志也没有,也是⼀致的。

62、问题⼀:写redo⽇志也是写io(我理解也是外部存储)。同样耗费性能。怎么能做到优化呢?
问题⼆:数据库只有redo commit 之后才会真正提交到数据库吗?
答:1. Redolog是顺序写,并且可以组提交,还有别的⼀些优化,收益最⼤是是这两个因素;
2. 是这样, 正常执⾏是要commit 才算完, 但是崩溃恢复过程的话, 可以接受“redolog
prepare 并且binlog完整” 的情况。

63、两阶段提交那⾥不太懂,我理解是不是只要binlog保存成功,数据就能恢复,redolog 是不是就为了加快innodb引擎的写速度的?
答:Binlog写完就算,但是并不是⽤binlog恢复,恢复这个⼯作还是innodb做的。

64、redo log的机制看起来和ring buffer⼀样的;如果在重启后,需要通过检查binlog来确认redo log中处于prepare的事务是否需要commit,那是否不需要⼆阶段提交,直接以binlog的为准,如果binlog中不存在的,就认为是需要回滚的。这个地⽅,是不是我漏了什么,拉不通。 答:⽂章中有提到“binlog没有被⽤来做崩溃恢复”,历史上的原因是,这个是⼀开始就这么设计 的,所以不能只依赖binlog。操作上的原因是,binlog是可以关的,你如果有权限,可以set
sql_log_bin=0关掉本线程的binlog⽇志。 所以只依赖binlog来恢复就靠不住。

65、明⽩double write是为了保证数据⻚⾯的完整性,我上午问的是db 异常关闭起来需要做恢复的时候,因为MySQL 的redo 是物理逻辑⽇志,记录的是⻚⾯逻辑的改动,所以要保证数据
⻚⾯的完整性,这个就是靠double write 去保证的,是这样吗?⽽Oracle因为redo是物理⽇志,所以不需要double write技术的保证?
答:对,这样说就精确 。不过现在有些硬盘⽀持16K的原⼦写保证,就可以关掉double write
buffer,MariaDB 就提供了这个参数允许关掉。

66、多写场景需要吧 指标应该是RTO。有个问题既然innodb事务性这么强,为什么在恢复临时库不⽤redo恢复呢?直接对⻚进⾏恢复 数据⼀致性强,恢复时⻓也短,不⽤多写⼀份binlog, 减少写放⼤。这样做主从直接redo同步岂不是更好吗(就是redo量有点⼤) ?
答:Redo主要就是因为循环使⽤会覆盖;不过你说的这个逻辑,业界已经有团队通过改造
InnoDB⾏为在做了,只是不是官⽅做的,所以正⽂中没提。

67、binlog为什么说是逻辑⽇志呢?它⾥⾯有内容也会存储成物理⽂件,怎么说是逻辑⽽不是物理?
答:这样理解哈。 逻辑⽇志可以给别的数据库,别的引擎使⽤,已经⼤家都讲得通这个“逻辑”;
物理⽇志就只有“我”⾃⼰能⽤,别⼈没有共享我的“物理格式”。

68、MYSQL第⼆讲中提到binlog和redo log, 我感觉binlog很多余,按理是不是只要redo log就够了?

您讲的时候说redo log是InnoDB的要求,因为以plugin的形式加⼊到MySQL中,此时binlog作为Server层的⽇志已然存在,所以便有了两者共存的现状。但我觉得这并不能解释我们在只⽤
InonoDB引擎的时候还保留Binlog这种设计的原因。
答:binlog还不能去掉。⼀个原因是,redolog只有InnoDB有,别的引擎没有。另⼀个原因是,
redolog是循环写的,不持久保存,binlog的“归档”这个功能,redolog是不具备的。

69、Bin log ⽤于记录了完整的逻辑记录,所有的逻辑记录在 bin log ⾥都能找到,所以在备份恢复时,是以 bin log 为基础,通过其记录的完整逻辑操作,备份出⼀个和原库完整的数据。在两阶段提交时,若 redo log 写⼊成功,bin log 写⼊失败,则后续通过 bin log 恢复时,恢复的数据将会缺失⼀部分。(如 redo log 执⾏了 update t set status = 1,此时原库的数据 status 已更新为 1,⽽ bin log 写⼊失败,没有记录这⼀操作,后续备份恢复时,其 status = 0,导致数据不⼀致)。若先写⼊ bin log,当 bin log 写⼊成功,⽽ redo log 写⼊失败时,原库中的
status 仍然是 0 ,但是当通过 bin log 恢复时,其记录的操作是 set status = 1,也会导致数据不⼀致。其核⼼就是, redo log 记录的,即使异常重启,都会刷新到磁盘,⽽ bin log 记录的, 则主要⽤于备份。
我可以这样理解吗?还有就是如何保证 redo log 和 bin log 操作的⼀致性啊?
答:⼏乎全对,除了这个“两阶段提交时,若redo log写⼊成功,但binlog写⼊失败…”这句话。实际上,因为是两阶段提交,这时候redolog只是完成了prepare, ⽽binlog⼜失败,那么事务本身会回滚,所以这个库⾥⾯status的值是0。如果通过binlog 恢复出⼀个库,status值也是0。这样不算丢失,这样是合理的结果,两阶段就是保证⼀致性⽤的。你不⽤担⼼⽇志写错,那样就是
bug了。

70、问题⼀:redolog记录更新⽇志,当更新完⼀个表之后再进⾏查询,此时会将这个表的
redolog刷到物理表中再进⾏查询吗?如果不是的话是如何保证数据最新?
问题⼆:数据恢复是以binlog的记录为准吗?如果要考虑redolog,那redolog如何帮助恢复? 答:1. 不会。就直接从内存读⾛,内存是对的就⾏。
2. ⽤binlog是⽤在备份恢复到过程中,这个过程就是⽤全量备份+binlog,你可以认为没redolog 的帮助。

71、binlog感觉像oracle的归档⽇志,那么也有设置binlog⽇期的参数,这个参数是?默认值⼜是多少?
答:expire_logs_days。默认是0 表示不⾃动删除;⾮零的话,单位是天。

72、如果认可这个事务可以从crash恢复,起码要求服务器告知客户端事务成功了吧,那您说redo log prepare + bin log ok,就可以恢复了,说明bin log写⼊成功就算事务成功了?那最后
⼀步确实没什么⽤呀。
答:有⽤,如果redolog 已经commit了,崩溃恢复就不需要去找binlog。

73、请教下redo log 和 binlog 也是需要写⽂件的,也会消耗IO,这个不会影响数据库的性能吗? 从内容看redo log⾸先写道缓存中,如果这个时候断电了,缓存也清空了,尚未写道磁盘,这个时候不是redo log 不完整的如何保证恢复的完整性呢?
答:要写IO,是会影响,但是为了数据可靠性,还是要做的。Redolog是直接写盘的,更新的内存是数据的内存。如果没有写完就重启,就回滚,不算的丢数据。

74、pos和checkpoint的位置⼀開始是怎麼確定的呢?checkpoint什麼時候移動呢?應該怎麼移動?移動多少?
答:⼀开始就是从头了,中间如果有重启,也从重启前的位置继续。 移动就是向右,到末尾循环;移动多少 取决于事务要写下多少的⽇志。

75、怎么知道binlog是完整的?
答:⼀个事务的完整binlog是有固定格式,也就是说有固定结尾的。

76、两个控制⽇志写磁盘的性能参数 如果都设置1 会不会对io影响很⼤ 对⼤并发也要设成1 吗?
答:⼀般这两个都建议设置成1(有时候称双1),除⾮你的系统IO撑不住了要临时应急。

77、两阶段提交是针对⾃动提交事务的语句来说的吗?如果是⼿动提交事务呢?
执⾏更新语句,只更新当前session缓存。当⼿动commit的时候,才是 记录redo log 处于
prepare状态,执⾏器执⾏,记录binlog,然后 redlog commit。是这样吗?
如果⽤户⼿动commit,但是第三步redlog变成 commit的时候挂掉,这时候⽤户收到的执⾏结果是什么?—这时候恢复的时候是认可这次commit的。
答:不是,这个跟⼿动还是⾃动⽆关。你说的commit是⼀个命令,“提交这个事务”,包含了整个提交过程(也就是说两次redolog和⼀次binlog操作,都是在你的这个commit 命令中做的)。

78、关于提到的’数据⻚’这个词我没有太理解,是⼀种存储⽅式么?
答:MySQL的记录是以“⻚”为单位存取的,默认⼤⼩16K。也就是说,你要访问磁盘中⼀个记录,不会只读这个记录,⽽会把它所在的16K数据⼀起读⼊内存。

79、update后先写⼊内存,然后写redo log,那么写⼊的这条数据在内存中的驻留时间是怎么处理的?是redo log写⼊磁盘后从内存中删除吗?
答:不会删除,除⾮内存不够⽤。不过在数据从内存删除之前,系统会保证它会被更新到磁盘数据上。

80、我想模式redo_log成功,bin_log失败;或者反过来。该如何模拟呢?
答:这个稍微有点麻烦,最直接⽅法是GDB,停在两个函数中间,然后coredump出来。

81、如果更新写⼊redolog后处于prepare状态,此时系统突然断电,那么binlog没有写⼊记录,重启后数据库的实际数据根据redolog更新了,但与binlog的语句记录不符,那么binlog怎么办?
答:不会,这时候事务会回滚,binlog也没有,所以是⼀致的。

82、在⼀个⼤事物或⻓事物⾥,⼀边执⾏sql⼀边写redo log吗?未提交的事物也会写到redo log file吗?
答:会先写⼀块内存,叫做log_buffer⾥⾯,在提交的时候再⼀次性写到磁盘。

83、在binlog写⼊磁盘后,commit提交前发⽣crash,由于commit没有成功,那返回给客户端的消息是事物失败,但是在系统恢复的时候却会重新提交事物,使之成功,这不是在欺骗客户端嘛?问题很⼤啊。
答:不是,并没有返回“事务失败”呀,⽽是“执⾏异常”。执⾏异常本来就可能成功也可能失败 的。你想⼀下这个场景:执⾏全部完成了,在回复客户端的时候⽹络断了,这怎么算。

84 、redo log是prepare状态, 写binlog时crash; 以及binlog写成功, 但是redolog更新为
commit状态时crash;重启后的回复机制⼜是怎么做的呢? 答:第⼀个回滚,第⼆个事务有效。

85、定时备份也是通过执⾏当前时间到上⼀次备份时间之间的binlog来完成的吗? 答:这个其实也是⼀种做法,不会⼤家⽤的更多的还是直接找个备库再全量备⼀次。

86、有没有这样的命令dump⽇志得到查询历史SQL语句?
答:更新在binlog⾥⾯有记录,⽤mysqlbinlog⼯具可以,仅限更新语句。

87、有⼀段不太懂:具体来说,当有⼀条记录需要更新的时候,InnoDB 引擎就会先把记录写到redo log(粉板)⾥⾯,并更新内存,这个时候更新就算完成了。
这⾥所说的,并更新内存,值得是更新内存⾥⾯的数据吗? 即id为2的数据,
另⼀个问题,按照前⾯所讲的分区,那内存⾥⾯最⼤存储的数据量是否为4个G?(即,⽼板⼀直没时间写⼊账本,⼩⿊板记录的最⼤数据量)。
答:1. 对,更新内存
2. 粉板记录的最多的更新记录是4G。 我们说的内存是放数据的内存,就是buffer_pool的⼤
⼩。

88、问题⼀:我的数据表设计 id user ud,先做个多线程拿号登陆的功能,未避免拿到重复的号码,我百度到使⽤UPDATE $db SET ud=1, id=LAST_INSERT_ID(id) WHERE ud=0 LIMIT 1
;SELECT user FROM $db WHERE ROW_COUNT()>0 and id=LAST_INSERT_ID(); 这 两 句来取号,但是效率很低,数据⼀⼤,mysql还容易崩溃。请问有更好的⽅法吗?
问题⼆:我的第⼆个项⽬数据表设计 id user info。数据利润 1,user1,1001;1002;1003。我想查询包含1001的数据,⽤like的话,数据少的话还可以接受,当百万数据的时候,就会很卡很慢。想请教⽼师有其他的⽅案吗?
答:1. 你这个做法不好,应该先insert,拿到的这个last_insert_id就⾏
2. 如果有这种需求,最好不要把这些拆成单⾏。 或者创建另外⼀个表单独维护包含“1001”对应的集合。

89 、 redo ⽇ 志 在 prepare 阶 段 之 后 就 已 经 刷 盘 啦 ? 我 按 我 的 理 解 ,
innodb_flush_log_at_trx_commit 应该是控制在commit之后才会写磁盘啊?如果是没有问题, 如果不是,binlog刷盘后是⽆法保证redolog刷盘的。
答:这个prepare已经是在你执⾏“COMMIT”语句⾥⾯了。

90、innodb_flush_log_at_trx_commit这个参数设置成 1,是不是等于binlog的归档了呢?

答:不是,这个但是是控制redolog的。 binlog存起来就有“归档”的功能。

91、问题⼀:⽂中‘’update 语句执⾏流程‘’示意图,是先修改数据,在记⽇志。不应该是先写
⽇志(⽣成重做⽇志),在修改数据(缓存⾥)吗?
问题⼆:redo log两阶段提交具体是怎么实现呢?我理解就是直接记录哪个⻚⾯做了什么修改到
redolog(硬盘⽇志⽂件)就结束了,已经落盘了,prepare,和commit怎么理解呢?
答:1. 这两个其实是交叉发⽣的(对于⼀个有多次更新⽇志的事务来说),所以可以忽略先后。关键是他们都在“把⽇志写⼊redolog物理⽂件之前。
2. 有了binlog, 需要两阶段。

92、为什么binlog没有crash-safe的能⼒呢?不是写磁盘了吗?可以在重启后把宕机前的binlog 中记录下来的异常事务sql语句执⾏⼀遍?
答:⼀开始没这么设计,实际上现在的binlog也不够,奔溃恢复需要⽇志和引擎内存状态配合的。

93、mysql的redo log有没有log buffer?对于⾼并发事务或者批量事务 如何优化redo⽂件的写
⼊瓶颈?
答:有buffer的,事务整个完成才学到redolog⽂件,redolog是顺序写。

94 、checkpoint 是指redo buffer 落盘的位置, 还是指⽇志更新写到表中的位置( crash
recovery的起始点),我在理解oracle的checkpoint是后者的。。就不知道他们俩是否都⼀样? 答: Write pos是redo buffer接下来要写的位置, crash recovery 的起点就是checkpoint. (Crash recovery起点。

95、如果第三步⽤户不是想commit⽽是想rollback,但不巧在第三步的时候崩溃了,回复后根据redo log和binlog⼀致来⾃动commit?
答:如果⽤户执⾏rollback语句,整个事务就放弃了,不会进⼊“prepare”状态。

96、更新停留在redo-log bin-log上时,查询是如何把新数据也包括进来?是在查询启动的时候把redo-log的数据刷新到⻚数据⾥⾯么?同⼀个数据库⾥,查询和更新同样多时,岂不是会让redo-log的“粉笔板”效果失效?

答:不会,最新的结果会保存在内存⾥⾯,就是“掌柜的记忆”,查询直接从内存返回就可以了。

97、情形是这样的,我们因为磁盘IO异常导致数据库异常重启了,并发现某个表数据损坏了
(⽆数据备份)且mysql⽆法开启,⼀开始的做法innodb_force_recovery设置到1-6直到数据库可 以 启 动 , 然 后 进 ⾏ mysqldump 备 份 , 然 ⽽ 并 不 能 备 份 , 报 错 是mysqldump: Error 2013: Lost connection to MySQL server during query when dumping ta ble XX表at row: 31961089。于是就跳过这个表进⾏备份,并修复了其他数据库,但是这个表的数据丢失最终请了外援修复好了,但是具体没告诉我们,不知道和我们这个有没联系?有没提示让我学学习下这⽅⾯?是可以⽤ibd⽂件修复吗?
答:你说的这个case我分析了⼀下。
1.这个现象应该是数据⻚( page) 的内容错了。 InnoDB 在读到数据⻚的时候会判断
checksum,碰到有不对的⻚,就会⾃⼰crash掉。
2.修改 force_recovery启动以后,mysqldump会显示Lost connection,表示出错的表是在主键索引上(如果是叶⼦节点,dump的时候是不会访问到的)。
3.这种场景的坏表修复,有⼀种⽅法,是把错误的page先忽略掉。每个page有checksum,可以扫⽂件找到checksum不对的page, 清空掉内容, 初始化成空的。( 另⼀种做法是修改
MySQL的代码让MySQL忽略这个错误,⽽不是crash)。
4.总之第3步的⽬的就是跳过那些已经错了的数据⻚,把表⾥⾯别的数据取出来。
5.按照前说的,可以判断出这个错误的⻚是在主键上⾯,所以会丢⼏⾏的。
6.找回这⼏⾏的⽅法,有⼏个地⽅: 这个表其他索引⾥⾯有部分字段;undolog;binlog;业务对账。 但是这些都不能保证能够100%拿回全部数据。
⼀般尽量⽤binlog,业务对账已经是属于数据库本身没辙了。
我碰到过这种情况,没有binlog的情况下,做到第5步,业务就满意了。最后总结⼀下 :⼀定要备份,包括数据和binlog。

98、问题⼀:当mysql重启,对于innodb的启动是如何实现的,听⽂章说redo log会很⼤,⽐如
⼀个G,需要全都加载⼀边才能算启动完成吗?
问题⼆:⼆段提交,如果在commit之前crash了,innodb是如何界定这⼀条数据全是丢弃还是不丢弃?因为有两种情况,⼀种是bin log写成功了,但是commit没成功,另⼀个是bin log压根就没写成功。
答:1. 从checkpoint开始到writepos结束,不需要全部4G。

  1. Binlog写成功事务算成功,就提交事务;binlog没写成功就回滚。

99、⽂章提到如果redo log被写满了,需要停下来将数据写⼊磁盘。这个时候,整个数据库是处于阻塞状态吗?当有⼤量数据涌⼊的时候,会存在丢失数据的⻛险吗?
答:更新都会被堵住,但是不算数据丢失,因为事务根本就提交不了。
事务/数据丢失的定义应该是本应该存在的结果没了。但是这时候并没有告知客户端“成功”,也就不算“丢失”。

100、问题⼀:同样都是调⽤引擎接⼝, 为什么写⼊新⾏操作算执⾏器的, 提交事务操作就算引擎的了?具体描述如下:
update 语句时的内部流程时,2.执⾏器拿到引擎给的⾏数据,把这个值加上 1,⽐如原来是
N,现在就是 N+1,得到新的⼀⾏数据,再调⽤引擎接⼝写⼊这⾏新数据。
这⾥调⽤引擎接⼝时, 算执⾏器的操作,5.执⾏器调⽤引擎的提交事务接⼝,引擎把刚刚写⼊的 redo log 改成提交(commit)状态,更新完成。
这⾥调⽤引擎接⼝, 提交事务为什么就算引擎的操作了? 跟上⾯是不是有冲突,还是因为什么原因呢?
问题⼆: 我对redolog的存在原因的理解: redlog存在时为了数据库的内存操作的数据安全。 虽然写⽇志有io成本, 但是⽐每条sql都直接操作硬盘的成本依然还是低,所以综合来说, 保证内存操作,然后⽇志硬盘io,最划算。
问题三:⽐较好奇redolog设计固定⼤⼩4g的历史原因。
答:1、其实真正做数据更新和事务维护的都是引擎。但是引擎接⼝是执⾏器来调⽤的,调⽤关 系。这⾥只需要明确,“加1” 这个操作是在server层的执⾏器做的就⾏啦。在这个流程⾥,逻辑计算在server, 引擎层做读写。
2、理解正确。
3、不是固定为4G哦,可以配置的,只是在现在普遍的⼤内存场景下,要⽀持稳定的写⼊性能, 可以设置4G。

101、客户端发起commit的时候,收到commit成功是在把事务写到innodb_log_buffer_size就返回成功,还是要刷到binlog后才返回成功啊?
答:都要。⽽且不⽌写到log_buffer,还要写到redolog⽂件。

102、write pos 是当前记录的位置,checkpoint 是当前要擦除的位置。

请问⽼师,这个checkpoint 是不是就像图⽚⼀样,当写到3的时候,0已经被擦除了,并且
checkpoint已经在1上⾯了。就是,checkpoint 是预先擦除⼀个⽂件的位置留给 write pos 写?
答:对,但是并不保证能够多出⼀个⽂件,也有可能write pos追上来就接近chenckpoint,这两位置在同⼀个⽂件。

103、关于崩溃恢复 数据库⽬标既然是保持数据⼀致可能还原也可能执⾏ 对于数据库的客户端这时候是什么状态呢 对于这些没有收到成功答复的更新操作,客户端需要⼀直重试吗?
答:代码⾥⾯要有确认的逻辑。

104、既然write pos和checkout都是往后推移并循环的,⽽且当write pos赶上checkout的时候要停下来,将checkout往后推进,是不是意味着,后续的更新请求都会等待,等checkout往后推到⼀定空间后,再继续执⾏。如果上述成⽴的话,在⼤量的更新或插⼊操作的时候,write
pos可能会很快的追上checkout,从⽽再次产⽣等待的过程,这个是不是⼀个在性能上产⽣瓶颈。
答:是的,所以1. 要设置⼤⼀点的innodb redolog的⼤⼩ 2. ⽤好点的磁盘。

105、有⼀个疑问 redo log记录 在某个数据⻚做了什么修改,这⾥数据⻚要怎么理解,数据查询的结果数据?
答:不是,存放数据的地⽅,你可以理解为按块⼉存,⼀块⼉16K。

106、⽂中有两处内容让我迷惑,感觉描述不⼀致,如下:
“具体来说,当有⼀条记录需要更新的时候,InnoDB 引擎就会先把记录写到 redo log(粉板)
⾥⾯,并更新内存”
“3.引擎将这⾏新数据更新到内存中,同时将这个更新操作记录到 redo log ⾥⾯”该过程是先把新数据更新到内存中再写到redo log⾥吗?
答:如果是MySQL 代码⾥的精确顺序,是先更新内存的。这⾥主要是要说明WAL机制,这两个的顺序你可以认为“同时”,会更⽅便理解。

107、⼀天⼀备份, 每份bin log都⽐较⼩, 数据恢复⽐较快, 但是每天数据库备份占⽤的硬盘空间会⽐较⼤, 相⽐每周备份⼏乎多了⼀个数量级。
答:对的,存储成本和RTO的权衡。

108、请问数据库备份是怎么做?可以通过主从数据库,将从库的复制时间设置为1天后复制的
⽅法实现备份么?
答:这个叫延迟从库,你可以在这个库上备份。不过⼀般建议在⼀个普通从库上备份。

109、我采⽤mysqxtrabackup来全量和增量备份数据库,没有备份binlog,这样可以的吧? 答:看你的系统可靠性要求哈。两次增量备份之间如果机器挂了怎么解决的,要有个预案。

110、如果先写binlog 再写redolog ,出现crash时我以之前全备份加binlog进⾏恢复,不需要
redolog了,出现不⼀致也没有问题啊?
答:嗯,你这么做是可以的,但是正常重启innodb 要做崩溃恢复的,它⼀看,没redolog, 这个事务是没有的,但是binlog⾥⾯已经有了,⽤你的⽅法事务就存在,设计上要避免使⽤⽅式的问题出现不⼀致的结果。

111、我这个⼩⽩不懂,字段由0变1时,在“先写redo后写bin”下,写完redo就crush,您说⽤binlog恢复会导致结果是0和原库不⼀致。但那也还没commit啊,原库不还是0,⽤binlog恢复出来数据库也还是0,那不是反⽽保证了和原库⼀致吗?
答:Innodb⾥⾯,redolog写完就算成功了,崩溃恢复以后这个⾏是1。

112、如果将innodb_flush_log_at_trx_commit参数设置为1,那么每次redo log都要被刷新到磁盘,这样效率也会⽐较低吧。感觉就不太符合您讲的“掌柜写粉板提⾼效率”例⼦了。
答:⽇志顺序写盘,⽐直接写数据(随机写盘)还是快的。

113、如果是redo log是直接写磁盘,为什么不把更新的数据直接写磁盘?是因为慢太多? 答:是的,顺序写⽐随机写快。

114、如果在⾮常极端的情况下,redo log被写满,⽽redo log涉及的事务均未提交,此时⼜有新的事务进来时,就要擦除redo log,这就意味着被修改的的脏⻚此时要被迫被flush到磁盘了,因为⽤来保证事务持久性的redo log就要消失了。但如若真的执⾏了这样的操作,数据就在被commit之前被持久化到磁盘中了。当真的遇到这样的恶劣情况时,mysql会如何处理呢,会直接报错吗?还是有什么应对的⽅法和策略呢?

答:这些数据在内存中是⽆效其他事务读不到的(读到了也放弃),同样的,即使写进磁盘,也没关系,再次读到内存以后,还是原来的逻辑。

115、请问如果写完binlog后crash,redolog还没commit,这时候会发⽣什么? 答:重启后崩溃恢复时提交事务。

116、上⾯id为某⼈的关于⼆阶段提交,也就是14/15描述貌似有点问题,preper阶段是刷redo 到磁盘,commit阶段才刷binlog到磁盘,根据淘宝同学提的建议,5.7.之后某⼀个版本后改到了只需要确保刷binlog之前刷redo(根据lsn判断),不知道我描述的对不对,请⽼师确认下!
答:是的,但是两阶段的逻辑没变。

117、如果两种log都写完成了,但是redolog没有写磁盘,物理机挂了,redolog在内存中就丢失了吧,再启动跟磁盘中的binlog就不⼀致,后期恢复数据的时候会有问题吧?
答:是的,这就trx_commit没设置成1的后果。

118、只有redo-log,没有undo-log,innodb应该没有办法做到crash safe,楼主可不可以把这块也稍微讲⼀下呢?⽬前有点⼀知半解。
答:Undo log跟数据⼀样,也是WAL机制写⼊,需要依赖redolog 恢复出来。

119、两阶段提交为了保证binlog和redolog的⼀致性,1 prepare阶段 2 写binlog 3 commit 阶段,假如在3之前崩溃,恢复时,虽没有commit,但prepare和binlog完整,所以重启后会⾃动commit,以此来保证有binlog⼀致性。
既然能这样做,那为什么不把commit阶段去掉,恢复时,只要redolog和binlog完整就认为
redolog有效,否则回滚呢? 是因为效率还是其他的原因?
答: 如果redolog是完整的,( 包括了prepare和commit), 就直接认为成功, 不去判断
binlog。

120、对于“redo log ⽤于保证 crash-safe 能⼒”这个描述,如果在写redo log的时候发⽣异常
(⽐如断电),那重启后该条更新是不是失败了?但是在这种情况下redo log、binlog和磁盘上的数据⽂件⾥都没有该条更新的记录,从数据⼀致性⻆度来看,整个MySQL系统是“安全”的。更新失败的事务可以通知客户端重新提交。不知道我这样理解对不对?

答:这时候崩溃恢复肯定是要回滚了,但是确没办法再“通知客户端”了,因为之前连接就已经断开了。只能靠客户端发起新连接去查数据确认。

121、两阶段提交
1、prepare 2、写⼊binlog 3、commit
如果到2后,系统奔溃,binlog没写⼊,事务回滚, 如果到3后,系统奔溃,binlog写⼊,事务⾃动提交,
那有个疑问在这过程中,事务回滚和提交mysql是怎么判断的? 答:只好靠恢复后,应⽤程序⾃⼰来查数据验证。

122、装库的时候,redo 应该设置成⼏个⽂件,以及每个⽂件的⼤⼩怎么设置的,应该从哪些
⽅⾯去考虑呢?
答:主要看你预设多⼤的TPS,现在SSD机器+32G 以上内存,建议设置4个1G。

123、两阶段提交的第⼀阶段,将变更的记录写⼊redo⽇志,这个阶段是写到了log buffer还是log file?binlog也是⼀样。在第⼆阶段提交,binlog和redo是这个时候进⾏刷盘,还是⽬前已经完成刷盘, ⼆阶段提交确保两者已经刷盘的情况下才会真正意义上完成commit,否则进⾏
rollback。
答:1. 到了提交阶段,就是把logbuffer写⼊redolog duke。
2. 第⼆阶段,其实就是binlog和redolog最终提交的阶段。

124、⽂章写到redolog是物理⽇志,记录在某个数据⻚做了什么修改,binlog是逻辑⽇志,是原始逻辑。我是否可以这样理解,redolog是增加新的内容,binlog是对表中已有数据做改动。回到⽼师的问题,⼀天⼀备份与⼀周⼀在什么指标上更有优势,我的理解是在优化指标上更有优势,备份时间越⻓,之后查询建⽴索引上就需要花更多的时间,所以对优化指标有影响。
答:Redolog和binlog都是记录数据的改动,前者是针对数据⻚,后者是记录修改逻辑(这⼀⾏要怎么改)。

125、两阶段提交那个地⽅⽤了反证法,个⼈认为并不⼀定⽤了两阶段提交就⼀定可靠,写⼊redolog–>写binlog–>提交事务处于commit状态,这个是3步,可能在写完binlog后直接服务器断电,碰到过从库的binlog⽐主库还超前的状况(可能是binlog已通过⽹络传给从库,但是主库断电数据没落盘)。

答:这就是为什么MySQL 在崩溃恢复的时候,会在主库提交这个事务。

126、1 prepare阶段 2 写binlog 3 commit

当在3之前崩溃,重启恢复:虽没有commit,但满⾜prepare和binlog完整,所以重启后会⾃动
commit。备份:有binlog.⼀致。
疑问:虽没有commit,但满⾜prepare和binlog完整,所以重启后会⾃动commit。
–这⾥的binlog完整,通过什么⽅式或机制来判断? 答:每个事务的binlog⽇志都有标准的结尾的

127、问题⼀:
⽼师您回复:WAL机制⾥⾯有“已经提交但是还没落盘”的数据,binlog恢复不了这些。
这是因为⼀开始 binlog 就是设计为归档⽇志,因此恢复不了“已经提交但是还没落盘”的数据么?从图“update 语句执⾏流程”来看,提交前已经写 binlog ⽇志了,即使没落盘,这部分数据仍然是未丢失的。
问题⼆:
⽂章的例⼦:某天下午两点发现中午⼗⼆点有⼀次误删表,需要找回数据
最近⼀次全量备份⽂件 + 重放到误删表之前那⼀时刻,这样⼗⼆点到两点的数据是不是丢失了,恢复不了了?
答:1. 但是binlog并不知道他们没有落盘,这个是引擎内部机制。不会去重新执⾏。
2. 对。 ⼀般是这样,恢复出⼀个临时库是误删前的。然后把⾥⾯的数据按需取出来放到线上的库⾥。

128、update最后⼀步,既修改对应redo⽇志的状态为commit,这个时候,系统crash,会出现什么情况?
答:重启后,崩溃恢复过程中提交该事务。

129、开启了半同步复制after_sync以后,假设⼀个事务在已经把binlog sync到磁盘了,在传输
binlog到从库上时,主库挂了。如果这时发⽣了主从切换,从库是没有这个事务的.但是挂掉的主库 重新启起来,由于binlog已经fsync到磁盘,虽然引擎层未commit,但是会根据binlog来恢复这个事务.这个时候就是主从不⼀致了,那么和after_commit那就没区别呀。请问这个事务是恢复还是回 滚呢?如果是回滚,内部⼜是怎么判断的呢?

答:如果你设置来双MM,启动以后binlog还会传到新主库的。但是如果你设置的是单向的,那就会不⼀致了。

130、最近⽣产环境遇到⼀个问题,show full processlist中抓到⼀个update语句,根据主键更新的,时间是30s,state为updating,但是slow log中没有记录,我知道slow log中只会记录执
⾏时间>long_query_time的查询,不包括lock_time。我的问题是updating过程中做了哪些事情,是有加锁的动作吗?
答:那看来就是被⾏锁锁住了。

131、据之前的理解,应该是commit完成后才会写⼊binlog到binlog cache。看您说的update 过程是先落盘binlog再commit。假设这样的话,在主从复制中,在主库事务未提交的情况下,
binlog就已经被同步到从库了。
答:不是哦,我们说的都是“提交过程”哦。在事务“执⾏计期间”,binlog 没有写盘,如你所说,这时候在binlog cache,没往备库发的。

132、innodb_flush_log_at_trx_commit 和 sync_binlog 都设置成1,那么都是每个事务结束直接写磁盘,那是不是就浪费了WAL的作⽤,这样每⼀次的更新操作都需要写进磁盘,然后磁盘也要找到对应的那条记录,然后再更新,整个过程 IO 成本、查找成本都很⾼。
答:这个是为了保证数据安全必须的成本,但是WAL还是有⽤的。

133、执⾏⼀条Update 语句后, ⻢上⼜执⾏⼀条 select * from table limit 10。如果刚刚
update的记录,还没持久化到磁盘中,⽽偏偏这个时候的查询条件,⼜包含了刚刚update的记录。
那么这个时候,是从⽇志中获取刚刚update的最新结果,还是说,先把⽇志中的记录先写磁盘,再返回最新结果?
答:都不是,直接读内存。

134、执⾏最后commit,innodb的commit具体是做了哪些操作呀?移动checkpoint指针吗? 答:不是,Redolog上打上commit 标签,写盘。

135、在⼀个表上有更新的时候,跟这个表有关的查询缓存会失效,所以这条语句就会把表 T 上所有缓存结果都清空。如果做了主从,把所有的读操作都指到从库上进⾏,主库update表,

从库同步更新表数据,这个时候从库会不会有清空查询缓存的操作? 答:会,数据同步到从库其实也是在从库做更新,相同逻辑。

136、如果把redolog的innodb_flush_log_at_trx_commit设置为1,不就相当于每⼀次都要提交到磁盘,就⽤不到他的记录到⼩⿊板上的功能(先都写到⽇志,找⼀个合适的时间记录到磁 盘),不是把他的优势给弄丢了吗?
答:没丢,顺序写⽐随机写的成本低。

137、我之前是做运维的,通过binlog恢复误操作的数据,但是实际上,我们会后知后觉,误删 除⼀段时间了,才发现误删除,此时,我把之前误删除的binlog导⼊,再把误删除之后binlog导
⼊,会出现问题,⽐如主键冲突,⽽且binlog导数据,不同模式下时间也有不同,但是⼀般都是row模式,时间还是很久,有没什么⽅式,时间短且数据⼀致性强的⽅式?
答:其实恢复数据只能恢复到误删之前到⼀刻,误删之后的,不能只靠binlog来做,因为业务逻辑可能因为误删操作的⾏为,插⼊了逻辑错误的语句,所以之后的,跟业务⼀起,从业务快速补数据的。只靠binlog补出来的往往不完整。

138、我发现ibdata0⽂件⽆限膨胀并且不分裂初ibdata1⽂件,具体会是什么原因导致,有没什么有效的⽅式解决?
答:就是回滚段导致的。
Ibdata不会帮你⾃动⽣成ibdata1,会变⼤是因为有事务没提交,导致别的事务更新的回滚段不能回收,最终把⽂件撑⼤。查⼀下数据库⾥的⻓事务,kill掉。不过这样只能避免继续膨胀,现有的空间不能回收了,实在要回收只能靠把数据逻辑导⼊另外⼀个⼲净的库。

139、如果有⼀个备份库的时间点,最接近误删除的时间点,但这个备份库的时间⽐误删除的 晚,能不能⽤binlog倒过来回溯?
答:binlog都保留了、并且是row格式、并且误删操作是“delete” 才⾏。

140、您说MySQL 使⽤WAL,先写⽇志再写磁盘。请教⼀个问题,执⾏⼀条Update 语句后,
⻢上⼜执⾏⼀条 select * from table limit 10。
如果刚刚update的记录,还没持久化到磁盘中,⽽偏偏这个时候的查询条件,⼜包含了刚刚
update的记录。那么这个时候,是从⽇志中获取刚刚update的最新结果,还是说,先把⽇志中的记录先写磁盘,再返回最新结果?“

答:都不是,直接从内存读⾛,内存⾥可是有修改的最新的结果哦。

141、写⼊新⾏、新⾏更新到内存、写⼊redolog写⼊binlog。这4个操作应该是原⼦性的吧? 这边的写内存,写redolog,写binlog是怎么保证原⼦性的呢?
答:只是逻辑上原⼦,实现上不会加这么⼤⼀个锁哈。

142、写内存的时候查询到的数据是不是最新的数据?如果写⼊内存成功,redolog,binlog失败了,会不会造成读到这个⾮法数据呢?
答:不会,事务没提交呢,MVCC会让别的线程看不到这个内存的修改的。

143、⽬前更新数据的过程是在确保数据能更新成功的情况下就写binlog了,然后事务才提交
commit,如果通过binlog来同步数据,当接收到binlog后还需要获取其它数据进⾏计算,此时
⽴刻去读数据库的会发现有时候读不到刚更新的这条数据,这个有没有什么办法避免?
答:这个是读写分离常⻅的问题…只能做好区分,对延迟敏感的请求,需要到主库上去查。

144、接前⼀个问题
原问题: 更新数据的过程是在确保数据能成功更新的情况下就写binlog, 然后事务才提交
commit,通过binlog来同步数据,当接收到binlog后还需要获取其它数据进⾏计算,此时⽴刻去读数据库会发现读不到刚更新的数据。
您的回复:做好区分,对延迟敏感的请求,需要去主库上查询。

感谢⽼师的回复,之前表达的不完善。这⾥就是接收到主库发出的binlog后,⽴刻去主库查询数据,有时查不到此条binlog对应的更新内容,当sleep 0.5s(或更久)再去获取便能取到正确的数据,初步怀疑是binlog发送时主库的事务尚未commit导致的,不知道有没有什么好的解决⽅法。
答:确实有可能,binlog确实先发了,你还有这种场景啊,
⽐较少⻅到这种⽅案哈,我现在能想起来的就是轮询了。
当然还有可以去查解析出binlog ⾥⾯的线程id 和时间戳, 然后去查select * from information_schema.innodb_trx where trx_mysql_thread_id=$tid and trx_started <
$timestamp;
必须返回空才⾏,本质上还是轮询就是了,不过,都拿到binlog了,看看能不能考虑设置成row 格式,直接解析更更好些。

145、关于先写redo log再写binlog的分析有些不理解。假设redo log写完后binlog还没写完, 这时数据库出现意外宕机,修改数据的物理记录虽然记录到redo log,但是事务还没提交,我理解未提交意味着数据没有最终落盘,您说的c=1是因为innodb的崩溃恢复机制能把未提交的事务恢复到提交状态么?
答:如果redo没落盘,那会回滚的。

146、关于两阶段提交有个问题想请教⼀下。写redo log处于prepare阶段后,如果写binlog失败是会回滚redo log的记录吗?
答:不⽤回滚⽇志,只需要打上“rollback”标签。

147、【update 语句执⾏流程】这张图的update更新流程是不是有点问题啊,既然是WAL了, 为什么是将这⼀⾏的c值加1,写⼊新⾏,然后才是写⼊redo log呢?
答:WAL是指在“数据真正写⼊对应的磁盘数据⻚“ 之前写⽇志。

148、任何事务如果在回滚的时候系统异常重启了或者回滚操作失败了, 那是不是就导致数据状态可能不⼀致? 这样情况会发⽣吗? 如果发⽣了怎么避免?
答:没问题的。回滚中系统异常重启,重启后会继续回滚,是通过扫描redo log状态来发现的。

149、WAL可以保证之前提交的记录不会丢失。所以是不是只能是checkpoint过的记录不会丢失呢?那没有checkpoint过的数据但是已经write到redo log⾥的,在数据库发⽣问题了,应该都丢失了吧。如果发⽣这样的问题,如何恢复呢?
答:不会呀,崩溃恢复的时候,会从checkpoint开始往后主动去刷数据的,没有checkpoint过的并不会丢失,只是崩溃恢复的时候花点时间。

150、问题⼀:如果更新语句涉及到索引,索引是什么时候被更新的,是否跟数据⼀样,先更新 内存,然后再持久化到磁盘?
问题⼆:如果事务失败,回滚的时候,是否也会写redo log和binlog。回滚涉及到索引的更新话,索引如何回滚的呢?
答:1. 索引也是数据的⼀部分哈,⼀样的策略。
2. 会写redolog,打上回滚标签,binlog就直接丢弃啦。回滚都是把没⽤的内存版本删掉。

151、下⾯这段话是不是⽭盾了:
既然redo log 本身就配置了⼀组4个⽂件,每个⽂件的⼤⼩是 1GB,那么这块“粉板”总共就可以记录 4GB 的操作,这个时候不是持久化到磁盘吗?
为什么还要设置innodb_flush_log_at_trx_commit 这个参数设置成 1 的时候,表示每次事务的redo log 都直接持久化到磁盘。
答:innodb_flush_log_at_trx_commit =1 的时候,才是确保redo log⾥的数据是持久化到磁盘的;如果是innodb_flush_log_at_trx_commit=2, 其实只是“写到redo log⽂件,但是没有持久化(fsync)”。

152、⽂中的redo log其实是redo+undo log吧,因为记录更新前和更新后的值都记录了的。答:这⾥主要还是讲“数据更新逻辑”哈, undo log是有的。

153、redo log处在commit状态就是已经在check point了吗? 答:没有,checkpoint是后台线程追上来的。

154、您说“redo log 可以是4组⽂件,每个⽂件⼤⼩1G”,从⼤并发update性能⽅⾯考虑,这个⽂件⼤⼩及⼏组⼀般怎样配置⽐较合适?
答:我觉得如果是1T以上的磁盘&不⼩于64G的内存,都可以按照4*1G来配置redo log。

155、不太理解为何要⽤redo.log, 前⾯举的那个记账的例⼦我可以理解, 但是binlog只是记录变化, 不是应该就是不停的写下去吗, 就不像是账本需要找到⼈, 然后对应的计⼊这个⼈的账⽬…
所以我觉得binlog就是那个粉板了 因为我是初学者, 所以可能很多基础知识有缺失, ⽼师直接指
出搜索哪些相关知识可以帮助我理解这个问题也可以, 我可以⾃⼰去看。
答:redo log和binlog其实都是粉板的概念,只是redo log是专⻔给innodb⽤的,⽽且在MySQL 的实现⾥⾯,是⽤redo log来达到“崩溃恢复”这个作⽤的,binlog⼀直的定位是“归档⽇志”。

156、⽂章中写到:write pos 和 checkpoint 之前是“ 粉板” 上还空着的部分,我的疑问是
checkpoint到write pos 这块内容的⼤⼩是如何定的,是不是⼀直是固定的⼤⼩?
答:不是固定的⼤⼩哦。整个⽂件系统空间是固定⼤⼩的(根据系统配置),write pos是“当前写⼊的位置”;checkpoint是“已经将更新持久化到数据的位置”。所以如果系统TPS短时间内增
⼤,这两个距离就会增⼤;反之如果系统空闲的时候,这两个距离就会减⼩。

157、⽇志写刷盘跟修改库的数据这两者什么关系。也就是粉板的数据什么时候算总账,在没算 账之前如何能查到最新的值?
答:内存⾥是正确的值,直接从内存读⾛就⾏;⽇志和数据的关系,是⽇志记录了数据从状态1 到状态2的过程。这个过程持久化了,状态1也是已经持久化到磁盘的,那么状态2就可以不着急
⼀定要写到磁盘。这个是WAL机制的逻辑。

158、innodb_flush_log_at_trx_commit 设置 1,并且sync_binlog也设置为1,就表示redo log 和bin log都是同步刷盘的,⽽库的数据先写内存,后写磁盘。库的数据保存在内存是安全,因为log中有全量数据可以恢复,请问库的数据何时刷盘,是依赖操作系统的pdflush还是mysql主动调⽤刷盘的系统调⽤?
答:“请问库的数据何时刷盘” 两种情况,redo log在推进checkpoint的时候,和脏⻚被淘
汰出内存的时候。

159、两阶段提交是原⼦性的吧?
答:不是,如果保证原⼦性,就不⽤两阶段啦。

160、两阶段提交的重点是不是应该是redo log bin log 的刷盘,⽽⾮写⽇志? 答:都是重点,不过如果说耗时,确实是刷盘才是主要的耗时的点。

161、更新语句应该是在分析器以后才能确定表名的吧。那它怎么在这之前去清空该表的缓存的 呢?
答:在之前是查缓存,执⾏语句以后才更新(失效)缓存。

162、如果不分为两个阶段,⽽把写redo和binlog放在⼀个事务⾥⾯,如果写redo失败,则失败回滚,binlog写失败,则同样回滚,只有两个⼀同成功才算真正成功,这样也可以保证数据⼀致性,为什么⼀定需要两阶段呢?
答:redolog是innodb⾥⾯的,如果按照你说的这种⽅式,redolog提交后,在引擎内就得算是事务完成了(因为没有第⼆阶段让事务“真正完成”)这样binlog失败的时候,想让引擎回滚就回 滚不了了。

163、关于redo 和binlog写⼊完成但是不commit的情况,此时我断开连接,这个事务应该是失败的。按照上⾯的说法,恢复的时候binlog这⼀条记录存在了,所以这条记录应该存在。

这个观点有问题还是mysql的bug。“此时我断开连接,这个事务应该是失败的。”
----这⾥有问题,此时你主动断开连接,这个事务就是“异常”,⽽不是“失败”。
答:异常的事务,状态可能是提交,也可能是为提交,所以记录存在,是合理状态的⼀种。

164、如果提交事物失败怎么处理,修改redo log状态失败,怎么回滚binlog?
答:binlog这时候还没写到磁盘上的,直接删掉binlog cache李忠的内容就可以。

165、ddl语句也会写⼊redo log吗?⽽且redo log是物理⽇志,同样也要I/O开销吧?另外实在看不懂redo log⾥⾯到底存的是什么,不是更新后的值吗?
答:1. ddl语句会写redo log的。
2.也要io开销,不过因为是顺序写,开销⼩。
3.不是更新后的值,⽽是 “数据⻚的内容是怎么变化的”。

166、看到留⾔⾥⾯说既然恢复的时候可以通过事务 id 去检查 binlog 的完整性,就不需要两阶段提交了。个⼈认为这可能是⼀种优化,如果每次都去检查 binlog 就太低效率了,⽽加上两阶段提交的步骤,进⾏恢复的时候只有当 redo log 没有提交的时候需要去检查 binlog,⽽⼀般在
commit阶段失败的概率都很⼩,所以需要检查binlog完整性的情况也⽐较少。不知道这样理解正确否?
答:虽然两阶段不是因为这个原因设计的,不过有了两阶段,确保了,只要redo log完整就可以直接认为事务有效,确实减少了崩溃恢复的时间。

167 、redo log等系统不忙更新数据的时候, 也是需要先从磁盘读出数据到内存, 然后⽤
redolog⾥⾯的记录更新内存,最后再⼀起写⼊磁盘吗?
答:对,这个就是“推进checkpoint”的过程,当然也有可能redo log要更新的那个数据⻚刚好在内存,就跳过了你说的第⼀步。

168、在redo log commit的时候,想我⼀下,是追加写呢,还是找到prepare的地⽅进⾏修改呢?
答:prepare写⽇志的最后⼀个512字节,会在commit的时候被改掉。

169、redo log为什么要设计成环形的?环形缓冲区的设计⼜有什么好处? 答:我觉得最重要的好处,是可以避免新建、删除⽂件带来的抖动。

170、是不是磁盘中的数据也有事务id,这样如果写完redolog,内存中的数据还没刷到磁盘, 系统奔溃重启后可以⽤redolog来检查当前数据是不是最新的⽤来恢复。
答:你的理解是对的, trx_id是数据的⼀个字段,会持久化到磁盘。

171、参考书《数据库系统实现》中,将redo log的记录格式描述为<t,表,字段,值>,即幂等。这⾥是否和您的操作记录说法有冲突?
答:有表空间id,勉强可以跟“表”对应起来,不过没有 “字段”的。

172、缓存失效是在刷新内存之后呢,还是在commit成功之后呢? 答:你可以⽤事务验证⼀下。
先构造⼀个querycache然后启动⼀个事务begin;
update …
select //看看这⾥还能不能⽤上查询缓存
Commit

173、innodb_flush_log_at_trx_commit 和sync_binlog 两个参数不同的值对mysql 两阶段提交的影响。
答:关了binlog没有两阶段提交,这两个参数只是控制写潘策略,两阶段的每个阶段执⾏步骤都还在的。

174、如果在写⼊redo log之前就异常重启了,数据还是会丢失的。答:那样不算丢失,因为并没有告诉⽤户“成功”。

175、之前好像说redo log是prepare状态,当写完bin log后,才会调⽤commit接⼝,让redo
log变成commit,那么如果bin log写完后,调⽤commit的时候出异常了,是不是bin log和redo log的记录不⼀致了?
答:不会,redo 如果没有commit,这个事务就回滚。

176、这个程序是什么时候获取到返回值的呢 ⽐如我update⼀条正常来说程序是返回1 是在
commit之后获取到的吗?如果是commit之前返回给程序 然后crash了 会给⽤户带来错觉吧?

答:顺序上是这⼀样的:
数据库⾥⾯写⽇志,此时算事务提交; 返回响应包给客户端;
客户端收到响应包
客户端解析响应包⾥⾯的affacted_rows,得到1. 所以没有你说的这种情况哈。

177、问题⼀:redo log写⼊内存的数据,是跟各个事务绑定的。假设 ID为2的数据c=1。
执⾏更新语句 update T set c=c+1 where ID=2; 在更新语句执⾏过程中,写⼊了redo log,但未两阶段提交。此时,是不是只有当前事务才能读取到c=2?
问题⼆:其他留⾔下“即使写进磁盘,也没关系,再次读到内存以后,还是原来的逻辑”,⽼师能否扩展下这个解答。是不是指,即便写到磁盘,磁盘的数据也有事务版本信息来保障?
答:1. 是的,这个事务还没有提交,没有提交的事务的更新,对其他事务是不可⻅的;
2. 是的,⼀个数据⾏的trx_id是⼀个数据的⼀部分,写磁盘的时候也是带着的。

178、看上去数据库正常关闭会清空redolog?
答:不会。就算事务放弃了,redolog也留着,redolog循环写的,下次循环回来就覆盖掉了。

179、既然write pos 会赶上checkout的时候对吧,那我的问题是当粉版满了,暂停后做⼀部分归档 ,那么是不应该有阈值,⽐如达到90%的时候,启动⼀个线程,来做归档,这样就不⽤暂停了吧?
答:⼀直有⼀个线程在归档的。

180、两种交叉写的⽅式依然会出问题啊,假如redolog在没有commit的时候,系统宕机以后, 回复的数据也还是不⼀样的啊?
答:redo如果没有commit,那系统宕机恢复后,事务是会回滚的。其实这样是没问题的。因为 还没有commit之前,你⽤别的线程去查,也查不到数据;崩溃恢复后,也查不到数据。这样就 是⼀致的。

181、更新redo log 和binlog log 成功后 。InnoDB 在空闲的时候才把⽇志⽂件写⼊到数据⽂件。如果查询的时候不使⽤缓存,⽇志⽂件还没有来得及同步到数据⽂件,那么怎么保证查询到的数据⽂件与⽇志⽂件⼀致的呢?

答:这时候查询的时候,直接从内存返回就可以了。内存⾥是正确的数据,磁盘上数据⽂件其实还是旧数据,不过因为请求查不到,所以没关系。

182、怎样让数据库恢复到半个⽉内任意⼀秒的状态?这个是不是还需要redo log啊?为啥我感觉仅仅binlog不够。
答:binlog记录了所有操作,操作记录⾥的时间也是精确到秒的。

183、#⽐如突然停业⼏天,恢复⽣意后依然可以通过账本和粉板上的数据明确赊账账⽬。#
对于这⼀句话,不太清楚.如果崩溃之后,对于没有提交的事务来说,粉板上的记录难道不是不存在 了吗?
答:没有提交的意思是,客⼈买⼀半(⽐如刚点菜完还没上菜),突然失⽕都跑了,这样是不应该记录到粉板上的。

184、今天⾯试遇到了⼀个问题,就是⾼并发的插⼊下,有哪些情况下会导致数据库插⼊变慢。 对于这篇提到的redo log和binlog有关系吗?
答:这两个会影响,不过在⾼并发的问题⾥,锁的影响可能会⼤些。

185、python⾥⾯,autocommit默认是0的,这种情况下,⼀条select语句如果不写begin,是否需要,commit?
答:不commit如果连接保持着,就可能变成⻓事务哦。

186、作为开发负责⼈,为开发⼈员制定规范,避免Long running transcation,
作为DBA,定时检查⽼师提到的information_schema.innodb_trx表,将超过阈值的transaction 终⽌掉,这⼀点看到其他同学说能够定时执⾏+邮件通知,不知道该怎么具体实现?
答:Kill query + thread_id 可以要求⼀个语句终⽌执⾏。

187、⻓事务回滚也会写⽇志吗?是写redolog?之前看理解是需要写⽇志,当⽇志空间不够, 数据库就会挂起不提供服务。
答:回滚就不写到redolog了, 嗯,空间满了的话,binlog写不下去,事务就执⾏不下去。

188、事务的回滚⽇志难道直接写到磁盘吗?我猜想应该是先存在内存然后在某⼀个时间写到磁 盘,那么问题⼜来了?这个时间是什么时候呢?如果猜想正确那是不是意味着⻓事务还会导致短

时间内内存占⽤较⼤?
答:回滚⽇志和数据的写⼊策略⼀样(WAL),但是不⼀定不写盘,⽐如checkpoint 推进过程就可能把undolog的内容从内存写到⽇志⽂件,所以不会因为undolog导致占⽤内存很⼤的情况。

189、怎么理解事务是基于连接的这句话?如果我在多线程中共⽤⼀个连接,并执⾏事务会有什 么后果?
答:多线程不能同时⽤⼀个连接,你说的共同,是不是指连接池复⽤?这时候对⼀个连接是“先 后使⽤”,不是“同时使⽤”。

190、回滚语句就是传说中的undo吧?
答:不是。undo叫做 回滚段,回滚语句就是”rollback”这个命令。

191、在更新语句的时候 会两阶段提交形式写⼊ redo log 和 bin log . 那记录旧版本的 undo
log 是这么时候写⼊的呢?
答: 更早之前了, 写redolog和binlog是提交过程。在此之前更新数据的时候就已经写了
undolog。

192、在事务开启前是不是可以把需要查询的数据查询出来,做好逻辑运算,只有写数据库阶段 才开启事务,是不是可以避免⻓事务了?
答:要注意的是,这时候查询没在事务⾥⾯,所以等开始事务进⾏更新的时候,可能数据已经变了。如果业务逻辑可以接受这么做,那这样是很好的⽅法。也是我⽐较建议的业务优化⽅向。

193、这个read-view⾥存储什么范围的数据呢?rr模式下,事务begin就创建⼀个空的read-
view,然后语句执⾏后,把符合条件的记录存在这个视图中,commit后再释放这个视图,这么 理解对吗?
答:不是begin,⽽是begin之后的第⼀个语句执⾏开始的时候。

194、mysql上千万表数据,如何快速删除⾃增,也就是取消⾃增?
答:如果只是去掉⾃增属性,你可以直接忽略它,每次插⼊数据的时候⾃⼰指定值就可以了。

195、如果⼀个mtr更改多个数据⻚,会对整个b+树上锁?还是只锁住⻚所在的分⽀呢?

答:如果是整个B+树加锁就是表锁啦 InnoDB是⽀持⾏锁的哦。

196、关于⻓事务,我的理解是这样,因为会存在⼤段的回滚⽇志,⽽回滚⽇志可能其他未提交 的事务也有关联,所以当这个⻓事务提交之后,这些回滚⽇志也会被保留,那当其他的事务提交后,这个回滚⽇志不会被删除吗?
答:会被复⽤,但是在ibdata中占⽤的空间恢复不回去了。

197、为什么回滚段删除了,硬盘空间⼤⼩没变⼤呢? 答:因为它占了空间就没法删回去了。

198、如果有主从配置 是不是就不需要备份了?
答:要,错误执⾏的drop table 也会同步给备库的。

199、当隔离级别是“读未提交“的时候,当读数据读到了⼀个未提交事务的值,⽐如⽂章中的
V1=2,但事务B回滚了,实际上V1的值仍然是1,可事务A已经查到结果是2,这样结果不就不
⼀致了? 该如何解决?
答:解决⽅法就是不⽤这个隔离级别,读未提交也称为“脏读”,就是你说的这个场景得名的。

200、我们保证断电数据不丢失,可以在事务每次commit时候直接写磁盘落地,当然这性能太差,我们采取写⽇志的形式,⽇志是顺序写的,性能会⾼些,那这样的话我觉得可以直接使⽤
binlog做断电后的恢复,每次写⼊binlog后再做commit,不写binlog当他没提交,这样理论上和直接写数据感觉是⼀样的,为什么还是要是⽤redo log呢?
答:“当他没提交”表示要回滚对吧,可是没有redolog, 回滚⽇志也丢了,回滚不了了。

201、您在⽂中提到关于“串⾏化"有"后访问的事务必须等前⼀个事务执⾏完成,才能继续执
⾏。”,我想问的是两个事务A和B它们有可能同时启动吗?如果可以同时启动,是不是说先进⾏操作(读或写)的事务就是前⾯提到的“前⼀个事务”,后进⾏操作的事务就是“后访问的事务”?还 是说两个事务不会同时启动,先启动的就是“前⼀个事务”,后启动的就是“后访问的事务”?
答:启动事务⼀定有“先后”的,每个事务要加⼊系统统⼀管理,加⼊的时候加锁,不会精确地“同时”。

202、如何理解“即使⻓事务最终被提交了,回滚段被清理,⽂件也不会变⼩”?为什么清理了⽂件⼤⼩还不变呢?
答:因为默认回滚段是在⼀个⼤⽂件⾥,和数据字段放⼀起。空间不够⽂件膨胀,空间逻辑删 除,⽂件不能随便删除。

203、开始以为⾃⼰懂了,然后想起参数 autocommit⾃动提交 后就⼜迷糊了,事物隔离级别和⾃动提交这俩是什么关系呢?这俩参数会打架吗?
答:⾃动提交只是把事务提交掉,提交结束事务。事务隔离级别是事务运⾏期间能看到的什么数据。这俩不会“打架”呀。

204、您提到: set autocommit=0, 这个命令会将这个线程的⾃动提交关掉……( 中间省略),如果是⻓连接,就导致了意外的⻓事务。
我的问题: 这个命令是针对这个线程有效,还是针对这个连接有效?如果这个线程A结束,⽽连接没有关闭,另外⼀个线程B继续使⽤该连接,这个时候这个事务有没有⾃动提交呢?谢谢! 答:连接有效。不过你这个问题要情况。如果你说的是MySQL 线程池,MySQL 在复⽤的时候
会重新初始化,那线程和连接其实⼀个意思;如果是说中间件(proxy)的连接复⽤,那就要看这个中间件怎么做的。

205、提到的⼀天⼀备,指的是⾃⼰⼿动备份⼀份新的吗,还是可以设置时间点,系统⾃动备 份?
如果可以回到⼀个⽉之前的数据,按⼀天⼀备来算,是否要保存30份binlog⽂件? 答:这个需要外部系统,MySQL ⾃⼰做不了的哈。
是的,30天的⽇志,不⼀定是30个哈,⼀天可能多个,也可能好⼏天⼀个。

206、现在有另外⼀个事务正在将 4 改成 5,这个事务跟 read-view A、B、C 对应的事务是不会冲突的。我想问下,假如现在回滚到事物1最开始中的值1,这是另⼀个事物将值改为5,那么 现在这条记录中最终的值是1还是5呢?
答:这俩应该是⾏锁了,不存在“同时修改”的情况。

207、在业务系统中,往往会存在数据库的并发操作,⽐如对同⼀个⾏数据的update操作。我认为只要数据库的隔离级别在 串⾏化 之上(可重复读或者读提交),那么要防⽌这种并发,往往

是在业务⽅进⾏ ⼀锁,⼆判,三更新的这种操作。如果是串⾏化的隔离级别,那就不需要这么麻烦的操作了。
答:确实是可以这么做,就是并发⾏不够好。对于不冲突的,额外加锁也是有额外消耗,需要看业务冲突率。冲突率⾼就⽤你说的这种⽅法,低低就⽤类似CAS的思想。

208、view A 拿到的值,直接就是1吧?不需要从当前值回滚得到吧? 答:1 不是物理存在的,是算出来的。

209、问题⼀:undo log是在什么时候写⼊的呢,前⾯讲语句更新的流程中没有提到undo log, 是它的写⼊结果对更新操作没有影响吗?
问题⼆:“当系统中没有⽐某⼀回滚⽇志更早的read-view时,回滚⽇志会被删除”。这个操作是定时的执⾏的吗?是不是如果某个数据库在⼀定时间内完全处于空闲状态,undo log就被删除完了?
答:1. 更新过程⼀边更新⼀边⽣成undo log。
2. 是后台,有定时的逻辑。如果空闲很久,确实有可能删除完了。

210、在可重复读的隔离级别下,两个事务都对同⼀⾏进⾏读出,事务⼀把该⾏某字段的值更改 并提交了。事务⼆还在⽤该字段旧值做业务逻辑,这样不会有问题吗?
答:得看业务逻辑,有的业务就需要这个特点… 如果有影响的,就要有对应的策略处理。改隔离级别、加锁读啥的。

211、问题:set autocommit=1,⾃动提交模式下,是不是每个sql都会⾃动提交,最后的commit语句是不是就没⽤了;如果把commit改成rollback,是不是也执⾏不了回滚操作了?
答:⾃动提交的意思是“在没有begin或start transaction 的时候”,⾃动提交。

212、引⽤⽂章原⽂:

  • “在MySQL 5.5及以前的版本,回滚⽇志是跟数据字典⼀起放在ibdata⽂件⾥的,即使⻓事务最终提交,回滚段被清理,⽂件也不会变⼩。”这⾥不是说“回滚段被清理了”,但为什么⽂件不 会变⼩?
    答:这个“清理”的意思是 “逻辑上这些⽂件位置可以复⽤”,但是并没有删除⽂件,也没有把⽂件变⼩。

213、如果每⼀个select都是⼀个完整的事务,那为什么innodb_trx没有数据呢,我是在本机上玩的,按理说我在本地数据库玩过很多,innodb_trx不应该没数据呀。
答:innodb_trx 显示的是“当前正在执⾏的事务”如果语句执⾏完成了,你在innodb_trx是看不到的哈。

214、⽂中示例提出“同时你会发现,即使现在有另外⼀个事务正在将 4 改成 5,这个事务跟read-view A、B、C 对应的事务是不会冲突的。”,如果这个时候将数据改为4的事务1回滚了,另⼀个将4改为5的事务2如何处理?
答:不会出现这种情况。”将数据改为4的事务1回滚了“ 表示这个事务还没提交,那么就有⾏锁,“另⼀个将4改为5的事务2” 这个就会锁进⼊等待,不会让他执⾏的。

215、⽼数据库可以设置事务超时时间吗,⼀般会不会设置?
答:没有直接的“事务时间”,有“语句执⾏时间”和“等待时间”,线上⽐较重要的业务,⼀般都要设置。

216、串⾏化隔离级别的锁是什么粒度的?合理的做法应该是只在读写记录时加锁,但从结果来 看,是对整个事务加锁。如果事务A和事务B不按相同顺序都访问了多个row,会不会出现死锁?
答:1. 串⾏化隔离级别的锁是事务级别,既然是串⾏化,就是保证⼀个事务结束前,其他事务不能访问它锁住的资源;
2. 会

217 、 如何修改 sync_binlog 这个参数? 我⽤的是 mysql 8.0 , 在 my.ini 中找到了
innodb_flush_log_at_trx_commit 参数(默认值为1),但是没有 sync_binlog 参数,是⽤其他参数代替了吗?
答:没有啊。不是每个参数都会默认写在my.ini⾥的,没写的时候就⽤默认值,你可以在
innodb_flush_log_at_trx_commit 的下⼀⾏加上sync_binlog=1就是设置上了。

218、既然读提交每次都要新建⼀个视图,⽽可重复读只建⽴⼀次,那么为什么读提交的效率要
⽐可重复读⾼呢??
答:建⽴视图没什么成本的,就是拷⻉⼀个事务数组;所以性能的差异不是体现在这⾥;

⼀般我们说可重复的效率相对的低(其实也还好,不会低多少),主要还是因为可重复读的锁范围可能更⼤(有gap lock),锁时间更⻓(事务结束才释放),影响并发度。

219、什么是⻓事务?
答:执⾏完语句但是⼀直不断开连接。

220、“读提交”隔离级别,视图在每个SQL语句开始执⾏时创建。这句话结合到⽂中示例,按照我的理解,事务A的“查询得到值V1”会创建新视图,但是事务B的“将1改成2”已经执⾏了,那么 事务A得到的V1应为2才对呀。我觉着是否改成“读提交隔离级别,视图在有其他事务提交后,
⾃身事务中有SQL语句开始执⾏时会创建”,会更准确些?
答:不是的,跟事务是否提交⽆关,这个事务⾃⼰会创建事务视图。

221、根据您的经验,什么场景下⽤RC,什么场景下⽤RR,能否举些场景出来?
答:业务场景⾥⾯需要明确的“可重复读”的能⼒,⽐如我⻅过的⼀些⾦融类的业务⾥⾯需要。
⼤多数时候可以优先考虑 row 格式 + RC。

222、备份是Mysql⾃动为我们做的吗?还是需要做什么操作? 答:不是,需要外部的备份系统。

223、事务的acid特性中,具有隔离性,那为什么脏读中,⼀个事务可以读到另⼀个事务更新但 未提交的数据呢?还有就是为什么会有事务隔离级别呢?
答:acid⾥⾯说的隔离性是指RC或者RR哈,事务隔离还是很必要的吧,脏读的话,很多业务逻 辑都会出问题。

224、假如默认的mysql数据库已经有隔离级别了,为什么还要开启事务呢?没有开启事务,实 际开发中会怎么样?
答:多个语句要作为⼀个原⼦逻辑的时候。

225、⼀条单独insert语句构成⼀个事务吗?
答:如果没有显式的启动事务,⼀条单独insert语句构成⼀个事务的。

226、不知道还能不能被看到,对于作者的两个事务的例⼦,如在事务A提交前有⼀个根据查询 到的v2值更新另外⼀个表的值,如果采⽤rc,那么这个v2就有可能变化了,导致业务执⾏结果答:不⼀致。可以通过在事务A中增加getid for update来解决吧?但是我们在写代码时这种情况⼤多是没有这么去实现的,是会有问题的吧?
如果业务逻辑有这种依赖,就是需要⽤for update去做的;没这么写就会出现bug。

227、为什么说set autocommit =0的时候,会产⽣⻓事务?虽然不能⾃动提交,但是每次执⾏
⼏个sql就commit不就可以了吗,如果它会产⽣⻓事务,那么set autocommit = 1配合显示启动事务也会⻓事务,⼀直不commit,执⾏100个sql再commit,这样不也是会产⽣⻓事务么?
答:这样是没问题的,但是很容易忘记,我经常碰到的情况是,⼤家以为select就不是事务,在
autocommit=0的模式下,执⾏⼀个select,然后连接就那么放着。

228、意味着如果你只执⾏⼀个 select 语句,这个事务就启动了,⽽且并不会⾃动提交。这个事务持续存在直到你主动执⾏ commit。 请问⼀下为什么⼀个Select语句会⾃动开启事务呢?
Oracle从来不会这样的。除⾮DML 语句,以及Select for update。我在5.7执⾏⼀个Select后再事务表并没该语的事务。
答:autocommit=0就会。

229、如果事务执⾏了⼀半后失败了,会⾃动回滚,那回滚就⼀定成功吗,如果回滚也失败了怎 么办?
答:回滚失败⼀种常⻅情况是回滚⼀般crash了, 重启以后继续回滚。

230、如果不begin,那么select语句会开启⼀个事务吗? 答:如果select的是⼀个innodb表,会的。

231、在commit⼀个trx后开始⼀个begin,万⼀这个thread挂掉了怎么办? 答:前⾯提交的事务就提交了,新的事务没有⾛到提交阶段,就回滚。

232、关于可重复读隔离级别下,⼀致性视图的创建时间有点疑惑,⽂中说的在事务启动时创 建,但我实验的结果是在第⼀次查询语句执⾏时创建的。
答:这个是⼀致的哈,事务启动就是在“第⼀次执⾏查询语句”的时候哦。

233、底层数据结构在⻚内是逻辑有序的吗?
答:物理上⽆序的,并且采⽤双向指针,⻚内逻辑有序,这个很精确。物理上,顺序插⼊的数据
⻚连续的概率⼤(但也不是精确的),⾮顺序写⼊的索引基本可以认为物理⽆序的。

234、“N叉树”的N值在MySQL中是可以被⼈⼯调整的么?曾经⾯试被问到过这问题,当时就懵逼了…
答:⾯试中题⾯越简单的问题越暗藏凶险,可⻅⼀斑…
可以按照调整key的⼤⼩的思路来说;如果你能指出来5.6以后可以通过page⼤⼩来间接控制应该能加分吧,⾯试回答不能太精减,计算⽅法、前缀索引什么的⼀起上。

235、如果磁盘中的主键索引已经存储了这个表的全部数据的话,那常说的没⾛索引是遍历整个
B+树还是其他地⽅还有整个表的数据呢? 答:就是遍历这个主键索引的意思。

236、索引本身在mysql⾥⼜是⽤什么数据结构管理的,内存⾥会有副本吗? 答:内存是⼀⼤⽚buffer , 内存的数据内容跟磁盘⼀样的。

237、没有主键的表,有⼀个普通索引。怎么回表?
答:没有主键的表,innodb会给默认创建⼀个Rowid做主键。

238、1、什么情况下创建索引才有意义?有哪些限制?⽐如字段⻓度
2、如何查看索引占⽤多少空间?
3、查看索引数的结构,⽐如多少个层,多少节点?
4、如何查看索引的利⽤率。⽐如我创建了⼀个索引,是否可以有记录这个索引被调⽤了多少 次?
答:1. 有这个索引带来的查询收益,⼤于维护索引的代价,就该建 对于可能变成⼤表的表,实际上如果不建索引会导致全表扫描,这个索引就是必须的。
2.可以估算出来的,根据表的⾏数和索引的定义。
3.跟 2 ⼀ 样 。 如 果 要 精 确 的 , 就 要 解 数 据 ⽂ 件 , 这 个 ⼯ 具 可 以 看 看https://github.com/jeremycole/innodb_diagrams
4.performance_schema.table_io_waits_summary_by_index_usage能看到⼀些信息。

239、⽬前我们在初级阶段设置主键和索引⼀般就是id,也有使⽤到例如⼿机号或者身份证号, 银⾏卡号等作为辅助索引,因为数据量⼩,⼏乎都没感觉到有什么区别,通过⽼师对哈希表,N 叉树的点播,课下再去补充了相关的知识点,对⼀张表的执⾏逻辑和更快节约时间有了进⼀步认识,对于优化数据库有⽤,只不过重建这⼀块还是没懂,我们操作数据库⾏为时通常没写重建这个动作啊。
答:因为我们说不论是删除数据,还是增删过程都有可能导致数据⻚的空间利⽤率降低,所以有重建需求哦。

240、 关于数据库主键的建⽴⽂章中提到⼀般是⾃增较好,我的理解是主要是索引采⽤了树的结构,主要是索引列的有序性,如果⽤uuid或其它string类型做主键,是否⽆法进⾏索引优化呢,或者说 索引会失效呢,分布式系统中是否还是应该采⽤类似于snowflake来保证主键唯⼀的同时⼜具备索引优化的条件,⽽不应该使⽤uuid呢,之前的⼀个系统中,在存储订单的场景中,使⽤了订单表order 和 订 单 明 细 表 order_detail,order_detail 表 通 过 关 联 order 表 的 主 键 order_id,order 表 和order_detail表均为uuid⽣成,这样当数据量很⼤的时候(⽬前已经order_detail表有200多万条记录,order表⼤于10万记录,且不断递增),是否⽆法进⾏相关索引优化提⾼查询效率呢,主要报表统 计时查询很慢,订单明细表是否应该通过订单号order_number(⻓整型数字)来关联从⽽索引列建在订单号上呢?⼩⽩求教,感谢
答:不是的,即使是随机顺序插⼊的索引也是,查询也是能⽤上,也是能提升查询性能的。 只是插⼊数据的时候维护代价⽐⾃增主键⼤。

241、假如说我的查询都是根据身份证号查询的话,如果身份证号是主键索引,不就不⽤回表了嘛, 这样性能会更⾼吧?
答:是,但是身份证⻓19位,如果你还有别的索引,⽐如按照名字查、按照城市查这种,就要 考虑主键值在其他索引的占⽤空间。还有,随机插⼊速度慢些。

242、问题⼀:如果插⼊的数据是在主键树叶⼦结点的中间,后⾯的所有⻚如果都是满的状态, 是不是会造成后⾯的每⼀⻚都会去进⾏⻚分裂操作,直到最后⼀个⻚申请新⻚移过去最后⼀个 值?
问题⼆:之前看到过说是插⼊数据如果是在某个数据满了⻚的⾸尾,为了减少数据移动和⻚分 裂,会先去前后两个⻚看看是否满了,如果没满会先将数据放到前后两个⻚上,不知道是不是有这种情况?

答:1. 不会不会,只会分裂它要写⼊的那个⻚⾯。每个⻚⾯之间是⽤指针串的,改指针就好了,不需要“后⾯的全部挪动。
2. 对,减为了增加空间利⽤率。

243、在 InnoDB 中,表都是根据主键顺序以索引的形式存放的,这种存储⽅式的表称为索引组织表。这句话怎么理解?
索引表内部是B+的结构,那所有索引表在innodb中是怎么的结构,⼀条语句如何快速定位到某
⼀个索引表的?我的猜测是不是所有的索引表也是存在于⼀棵B+树上呢?通过⼀些索引表⾃身的标志?
答:⽐如⼀个innodb 有⼀个主键索引和2个普通索引。这样就三棵树对吧。然后InnoDB只要记录每棵树的根节点的位置就⾏了,有根节点,B+树就能拉出⼀整棵树。

244、有⼀张⽤户表,user_id是唯⼀标识,user_id是随机的字符串,user_id⻓度也不等,表⼤概10来个字段。只需要以user_id为索引进⾏查询。如果以user_id建⽴主键索引,考虑数据量达到千万级别,这种情况下插⼊性能会⽐⾃增主键⼤概会差多少。数据量⼤了以后,本身以字符串做主键是不是也会成为瓶颈。
答:User_id业务有要求必须是唯⼀索引的话,你这种情节就直接⽤来当主键吧。

245、查询出来的结果集默认是有序的吗,还是按使⽤索引的正序? 答:按照索引遍历的顺序,这样不⽤再排序。

246、哈希表这种索引模型,为什么数组后⾯会拉出⼀个链表⽽不是数组呢?是不是就是因为链 表的插⼊⽐数组的时间复杂度要低?
答:其实更重要的原因是不定⻓…⼀开始不知道要开多⼤的数组,每个值后⾯的item个数也不 同。

247、⽂中提到KV场景适合⽤业务字段直接做主键,因为没有其他索引,不会浪费空间,但岂 不是分裂现象仍然严重?因为不能保证业余字段顺序插⼊啊。
答:嗯嗯,⻥和熊掌不可兼得。不过我们是假设业务必须把这个字段设置为唯⼀索引。不把它设置成主键,设置成唯⼀索引也⼀样要⾯对这个问题。

248、⾮聚集索引上为啥叶⼦节点的value为什么不是地址,这样可以直接定位到整条数据,⽽不⽤再次对整棵树进⾏查询。
答:这个叫作“堆组织表”,MyISAM就是这样的,各有利弊。你想⼀下如果修改了数据的位置的情况,InnoDB这种模式是不是就⽅便些。

249、为什么⼆级索引的value是主键索引呢?不这么做,⼀旦数据⻚的分解或合并会更改所有的B+树,特别对于⼆级索引要对很多不连续的数据⻚修改,会降低并发性能。这么做,数据⻚的操作只会影响主键的B+树,但这会影响⼆级索引的查询效率。还有个问题,如果⽤户显示两
⼆级索引建在主键上,那会不会有两份数据?
接着问上次的问题啊,如果主从复制传redo到从机,在回放时,⼀个mtr更改多个数据⻚,除了锁住整个B+树外,还有没有其他能解决B+树分解的原⼦性呢?
答:前⾯的分析很好。不能直接动⼿,要交给IO线程处理并发冲突。所以物理复制是⼀个新技术,要解决很多问题。

250、⾃增主键是不是只对数据插⼊操作会简化索引的维护,如果是对数据的删除操作,因为删 除操作会涉及⻚合并,所以对索引的维护是不是就跟普通主键索引⼀样了?
答:合并不是频繁操作,所以对删除的性能影响是可以忽略的。如果是随机删除数据确实差不 多。

251、KV场景,我们是假设业务必须把这个字段设置为唯⼀索引。不把它设置成主键,设置成 唯⼀索引也⼀样要⾯对这个问题。
我很疑惑:业务字段设成唯⼀索引,不设成主键索引。岂不是唯⼀索引经常分裂,并且还需要回表查询,双重缺点。不如把业务字段设置成主键了吧?还是说讨论的是innodb之外的引擎,不需要⾮得有个主键?
答:还是专指Innodb, 所以说,如果没有别的索引,就特别合适把这个唯⼀索引当主键索引⽤, 没额外成本。如果有别的索引就要权衡⼤⼩的问题。

252、数据库做集群,⽤⾃增索性,需要给每台服务器设置不同步⻓,感觉很麻烦。但⽤⾃⼰⽣ 成的唯⼀id,像⽼师说的可能存储空间和效率没⾃增好,请问这是时候怎么考虑?
答:⾃⼰⽣成唯⼀ID就是实现麻烦⼀点,如果是bigint 类型⾃增的,存储空间和效率其实也不错的。

253、查了⼀下,max_execution_time 是影响查询语句的超时时间:
The execution timeout for SELECT statements, in milliseconds. If the value is 0, timeouts are not
enabled.
max_execution_time applies as follows:
•The global max_execution_time value provides the default for the session value for new connections. The session value applies to SELECT executions executed within the session that
include no MAX_EXECUTION_TIME(N) optimizer hint or for which N is 0.
•max_execution_time applies to read-only SELECT statements. Statements that are not read
only are those that invoke a stored function that modifies data as a side effect.
这样对⻓事务还有影响吗?进⽽思考了⼀下,只读事务内会维护回滚段吗?jdbc客户端驱动说可以⽤Connection.setReadOnly(true) 来提⾼性能,是不是同此有关呢,能有哪些优化?
答:单语句的时间是算在整个事务中的,所以⻓查询也会导致⻓事务。只读事务没更新,不会写回滚段,不过由于有⼀致性读,还是有可能要访问回滚段的。

254、这⾥讲的索引B+树的叶⼦节点是⼀⾏数据,那对于每⼀⾏数据,内部是怎么存储的呢, 有没有⽤到什么数据结构?如果仅仅是顺序写磁盘,那修改表结构的时候是不是每⼀个叶⼦节点对应的磁盘都要改,这样不会很低效吗?
答:叶⼦节点之间是指针链接的关系,改⼀个不⽤改别的哦。

255、如果主键不是递增的,插⼊数据,会导致⻚分裂。那么说,索引在存储引擎中,肯定会有
⼀部分缓存在内存中,这⾥插⼊数据,更新索引的时候,实际上是现在内存中更改索引(⻚满了进⾏⻚分裂)。那么索引的物理存储会有什么变化吗?或者说索引的物理存储格式是怎样的? 答:Innodb ⾥⾯内存和数据⻚是⼀⼀对应的,内存分裂了,等到写回磁盘,看上去就是“磁盘上那棵树的叶⼦节点,也分裂了”。

256、唯⼀索引是和主键⼀样作为⼀级索引吗?
答:不是。这⾥说的唯⼀索引还是⾮主键索引的⼀种。

257、⼆叉树树⾼20就有可能要访问20个数据块,是不是意思每层对应⼀个数据块存储?树⾼ 多少就对应⼏个数据块?n叉树也是这样吗?
答:是,不过N叉树的树更“矮”。

258、在 InnoDB 中,表都是根据主键顺序以索引的形式存放的,⽼师,这句该怎么理解?还有,除了索引组织表,还有别的种类么?
答:就是所有的数据都在索引⾥,有,堆组织表,⽐如myisam 。

259、现在⼀般⾃增索引都设置为bigint,这点⽼师这么看?
答:特别合理,因为现在很多业务插⼊数据很凶残,容易超过int 上限,实际上是建议设置bigint unsigned。

260、⾮主键索引的叶⼦节点内容是主键的值。在 InnoDB ⾥,⾮主键索引也被称为⼆级索引
(secondary index)。我看图上⼀个⾮主键索引只有⼀个叶⼦节点,实际上应该会有多个叶⼦节点吧,内容是主键的值吧?
答:有多个叶⼦节点的。每个叶⼦节点是⼀个数据⻚,每个数据⻚16K,⾥⾯是放多个值的。

261、回表只是普通索引才会有的吗?主键和数据放在同⼀个树中,根据主键查询的时候,就可 以直接获得数据了。那select *from table where id=xx和select id from table where id=xx 的效率是⼀样的吗?(id是主键)
答:这两个语句是都不⽤回表了,在“查找⾏”这个逻辑上是⼀样的,但是select *要读和拷⻉更多列到server,还要发送更多列给客户端,所以还是select id更快的。

262、⽂中说⾮主键索引会存储主键的值,⽽⽂中举例的⾮主键索引值刚好没有重复,所以想请 问下, 如果记录表中 R1~R5 的 (ID,k) 值分别为 (100,1)、(200,1)、(300,1)、(500,1) 和(600,1),那么⾮主键索引k=1的节点,应该记录100,200,300,500,600的值,是这样理 解么?
答:不是,⾮主键索引上有5个值,分别是(1,100),(1,200)… (1,600)。

263、⼆叉搜索树的特点是:每个节点的左⼉⼦⼩于⽗节点,⽗节点⼜⼩于右⼉⼦,那上⾯那个 例⼦要查ID_card_n2的话可不可以理解为先查userB节点,依次向下找,找不到再来找userC节 点?

答:不是呀,跟⽗节点⽐,⼩的⾛左,⼤的⾛右,不需要左右都扫。

264、对于索引树,它是如何决策哪⼀个做为⽗节点,哪⼀个是叶⼦结点呢?
⽐如⽂中的:100,200,300,500,600,700,mysql是根据什么策略来构建这个b+树的,
⼜是在什么时候决定增加树的⾼度呢?
答:核⼼⽬标就是“减少树⾼”,最底层满了就要往下扩展。

265、“以 InnoDB 的⼀个整数字段索引为例,这个 N 差不多是 1200。”这个怎么理解呢?
答:⼀个page的⼤⼩是固定的(默认16k),索引⼤⼩固定的情况下,⼀个page可以放的item 数是固定的,如果是int型,是1200个左右。

266、在插⼊数据的时候,主键类型为字符串,ID为uuid的形式,插⼊时会导致分裂吗? 答:会,特别不建议uuid做主键。。

267、身份证和id为主键选择的问题:⽼师写了这样⼀句话’显然,主键⻓度越⼩,普通索引的叶
⼦节点就越⼩,普通索引占⽤的空间也就越⼩。'但普通索引的引占⽤空间不光是叶⼦节点决定 吧,应该还有中间的索引节点,这⾥如果使⽤id当主键,身份证当普通索引相当于普通索引的索 引节点空间增⼤。其实从空间的⻆度来说应该都差不多吧。
答:是这样的,这个主要是考虑到,⼀个表可能有多个⾮主键索引,这样每个⾮主键索引的叶⼦节点就包含了主键id的值。

268、uuid作为主键索引的,它在插⼊数据时是追加插⼊?还是需要挪动数据? 答:uuid不是持续递增的,中间还是不免会插⼊到“中间”。

269、⾃增主键的插⼊数据模式,每次插⼊⼀条新记录,都是追加操作,都不涉及到挪动其他记 录,也不会触发叶⼦节点的分裂。这表述不对吧,插⼊⽆数个数据,数据⻚早就满了啊,应该分裂啊?
答:不⽤“分裂”,已经有了12345, 要插⼊6的时候,如果叶⼦满了,就申请⼀个空的⻚,然后单独放⼊6,这个过程不叫做“分裂”。

270、请问这个B Tree B+Tree有什么区别?
答:B+Tree是数据只存在叶⼦节点,中间节点都只有索引字段。

271、为什么⼆叉树会访问更多的数据块?
答:因为⼆叉树更⾼,⽐如树⾼如果为20,就可能落在不同的20个数据块上。

272、全表扫描,是扫描整张表;按索引查找,不也是扫描整个数组吗?难道不是类似于扫描整 张表?
答:如果是“按索引查找”,那就不是扫描,⽽是⽤B+树查找,这个是可以快速定位记录的。

273、譬如身份证做主键,可以保证数据唯⼀性。如果⼀个表,是⾃增主键,那么插⼊数据不会 造成⻚分裂,但是如果有⼀天删除⼀条记录,是不是会出现⻚合并的现象,因为主键不连续了 答:删除⼀条不会的,主键没有要求“连续”存放哈,中间有个把空洞没事的,空洞太⼤了才会触发合并。

274、为什么⼀定有⼀个回表过程。因为保存了主键数据量⼩吗?
答:需要回表,是因为使⽤的索引上的信息不够,需要到主键索引上去取数据。

275、既然⾮主键查询还需要回表,那就没有存在的意义了,为什么要有⾮主键的查询呢? 答:索引可以快速定位记录,这个好处还是需要的。

276、请问⽼师是否有讲外键的利与弊?
答:外键可以⽤来做约束,但是这种约束关系是在数据库⾥⾯做的(类似于存储过程,其实是⼀种逻辑)。这种情况下,等于你的数据库⾥⾯也有业务逻辑,这个就要看项⽬管理上做得怎么 样。
如果能够把这些关系也作为代码的⼀部分,其实是可以的。之前很多⼈会觉得说加了存储过程、触发器、外键这些以后,代码逻辑混乱,⼀个原因也是因为没有把数据库⾥的逻辑像代码⼀样管理好。

277、看到n叉树的n是由数据块⼤⼩决定的。既然,⼀般情况下是1000多,那么也就是说⼀个数据块只能存放在⼤概1000多的数据块?
还在想另外⼀个问题?如何保证整个节点刚好在⼀个数据快上⾯,但是刚好分布在不同的数据块上⾯,那岂不是还是要查询两次?

答:“那么也就是说⼀个数据块只能存放在⼤概1000多的数据块” 准确说是 ⼀个数据⻚ 放
1000多个记录。⼀个数据⻚⼤⼩是16k, 如果有的数据超过16k,就会放在多个不同的数据⻚上,查这样的记录是要访问多个数据⻚的。

278、1. 有些资料提到,在不影响排序结果的情况下,在取出主键后,回表之前,会在对所有获取到的主键排序,请问是否存在这种情况?
2. 索引下推那个例⼦,感觉5.6之前的机制很匪夷所思:感觉判断’张%'之后再“看age的值”是顺理成章的事。难道联合索引的底层实现结构在这期间发⽣了变化?
答:1. 有的, Multi-Range Read (MRR) 由于不论是否使⽤这个策略,SQL语句写法不变,就没有在正⽂中提。
2. 不是,是接⼝能⼒发⽣了变化,以前只能传“搜索关键字” 。如果你⽤过5.1 甚⾄5.0, 在从现在的观点看,你会发现很多“匪夷所思”。还有:并⾏复制官⽅5.6才引⼊、MDL 5.5 才有、
Innodb ⾃增主键持久化、多源复制、online DDL。只能说,持续进化,幸甚⾄哉。

279、建⽴了(name, age)的联合索引之后,例⼦中的检索如下:
mysql> select * from tuser where name like ‘张 %’ and age=10 and ismale=1; 如果改为:
mysql> select * from tuser where age=10 and name like ’ 张 %’ and ismale=1; 这样优化器是否能够作出优化调整,还是直接⾛全表搜索?
答:既不会⾛全表扫描,也不会调整优化 。还是跟原来⼀样的执⾏计划。SQL语句的条件是会
⾃动调整的。

280、⼤家都知道建⽴索引可以提⾼查询效率,但是索引太多会导致索引占⽤的存储空间⽐原始 数据都要⼤,请问这两个要怎么去平衡?
答:现在的磁盘其实空间问题⼤多是时候不是主要问题了。所以⼀般会建议优先考虑查询性能。

281、原⽂:在引擎内部覆盖索引在索引 k 上其实读了三个记录,R3~R5(对应的索引 k 上的记录项).
我的问题是在 k 索引树上搜索 k=6时,发现条件不满⾜,不就结束了吗,难道还会回到主键索引去读⼀遍 R5 的值吗? 所以这⾥的读了“三个记录”,是不是有问题?应该是两个吧!
答:覆盖索引本来就不会回表哈,第三次读出来,判错就结束的意思。

282、如果⼀张表有ab两个单独的索引字段,在⼀条查询语句中where a=”” and b=””,优化器阶段会选择哪个索引,还是两个索引都⾛?
答:有三种可能,
1.只选a, 然后⽤b过滤
2.只选b ,然后⽤a过滤
3.选a和b,然后两个单独跑出来的结果取交集优化器选择的策略是数据分布

283、为什么第⼀张图的300/700,3/7 是在上⾯⼀层,另外为什么要跳过了400/4?其实看上
⼀节举例时就有这个疑问。
答:上⾯⼀层是⽤做搜索功能(查询的时候确定往下找哪个⼉⼦节点的)。跳过400是为了说“如果要插⼊⼀⾏(400,4)”

284、结合之前那个where in问题,⽤union all会造成整体sql语句很庞⼤的,这个对效率没影响吗?union all是最优的吗,有没有其他办法,⽬前搞⼯作中遇到了这个问题?怎么解决呢?
答:备注 in后⾯有很多,⾄少上万的了,union all的话,相当于上万条select union all。不需要union all,就⽤in 好了。上万看上去不太美,拆成多个in ()吧。

285、为什么要有联合索引这个东⻄呢?看样⼦是为了配合使⽤“索引下推”,减少回表?
答:索引下推是后来才有的。 就是为了快速定位记录。(name,age)对于where name=“a” and age=10就特别快。

286、视图能做索引吗,或者视图查询效率该怎么优化?
答:视图不能建索引,视图本身是SQL语句,关键就是优化视图定义就是。

287、mysql为什么要查⼀次索引回⼀次表?⽽不是查完索引后把结果存储起来,⼀次回表查询?这样应该更容易⽤到局部性原理。
答:全存起来变成需要临时表。还有,现在还不⽀持“传⼊⼀批ID给主键,’同时回表’ ” 这样的能⼒。

289、思考题:⽼师的回答是ca不必要,cb需要保留,我已经听明⽩了,但是既然有了cb这个 索引,那么c这个索引也没有必要了吧(最左前缀原则)?为什么⽼师没说把c也可以去掉?

答:只有cb的话, where c= N order by a还是要排序的。

290、图4 ID4 ID5 只要取回数据吧,是不是不⽤再取回数据判断,索引⾥有age。答:还要判断ismale,前⾯还是⼀个select * , 所以还是要回表的。

291、linux中tokudb引擎有没有类似alter table T engine=InnoDB这样的操作来释放空间呢? 答:对于server层来说,这个动作是“重建表”,但是重建表会不会收缩空间,就得看引擎怎么做 了,tokudb应该会有效的。

292、为什么还要去k索引树搜索k=6,发现不满⾜条件,循环结束呢?关于这个语句:select * from T where k between 3 and 5 。
答:因为5还满⾜,这个循环得继续找,找到第⼀个不满⾜的呀。

293、在覆盖索引这⼀⼩段,为什么name的字段⽐age⼤,就推荐(name,age)和age这两个索引呢?联合索引的两个字段反过来,然后再单独创建name的索引不可以吗?感觉空间上占⽤的差 不多啊。
答:单独⼀个name字段 和 单独⼀个age 字段,还是后者⼩⼀点。

294、根据最左前缀原则以及索引结构实现原理应该只需要保留ab主键以及cb索引就能满⾜问 题中貌似的查询语句。
答:但是要避免排序,最好有个cab。

295、这个语句(从⽂稿中copy出来的) select * from tuser where name like ‘张 %’ and age=10 and ismale=1; ,“张”字后⾯有⼀个空格,去掉之后,执⾏计划就变了 ,发现查询列只要带上不在索引列中的列,执⾏计划 的 type 就变成了 ALL,rows 也变成了⾏数总和,即使去掉 “and age=10 and ismale=1” ?难道是否全表扫描,不是光看type?还有执⾏计划的每⼀列,究竟是怎么看的呀?
答:可能是数据量太少,你模拟⼀万⾏进去看看,应该还是能⽤上索引的。

296、⽼师讲的索引都是基于有主键的表。如果⼀个表⾥边只有唯⼀索引和普通索引那么唯⼀索 引,联合唯⼀索引,普通索引都是怎么执⾏的呢?效率上⼜会如何呢?
答:这时候Innodb 引擎会⾃⼰创建⼀个内置主键。

297、既然是between 3 and 5,它该查询4吧,怎么会查询6呢,在k索引树中。答:没有4呀,5的下⼀个是6,得判断到“6不满⾜”才结束。

298、在不影响排序结果的情况下,在取出主键后,回表之前,会在对所有获取到的主键排序, 请问是否存在这种情况?不是取出⼀个主键回表⼀次吗,什么情况下⼀次取出多个主键再回表? 答:“存在,在MySQL ⾥⾯叫做“MRR优化”这个是⾃动的,代码版本⽀持就会这么做”。

299、看到某位同学提了个问题。关于的 truncate 与 delete.也就是说 truncate 会删除所有
(数据、索引等⼀系列相关)。delete 虽然删除了数据,但是实际索引依然存在。 这就是他们之间的区别。实际上是 truncate 就⾏是么?能解释⼀下 “”⾃适应哈希索引“”么。
答:Delete其实索引也删除的。就是如果要全表情况,就truncate好了。⾃适应哈希主要是偏理论的,我得想个应⽤场景…"。

300、delete也删除索引, truncate 也删除索引。但是实际他们是删除了 索引内部的内容,⽽
⾮索引是吧?查询了⼀些资料是这么说的,想跟您确认⼀下。答:额…都是删除数据,表结构、索引定义是没删的。

301、select * from T where k between 3 and 5 为啥要找k=6呢? 答:因为要找到6,才知道“后⾯没有符合条件的了”。

302、⾯试官问:说下怎么让mysql的myisam引擎⽀持事务,⽹上搜了下,也没有结果!
答:我怀疑他是想说⽤lock table 来实现,但是这样只能实现串⾏化隔离级别,其它隔离都实现不了。但是因为mysiam不⽀持崩溃恢复,所以即使⽤lock table硬实现,也是问题多多:ACID
⾥⾯, 原⼦性和持久性做不到;隔离性只能实现基本⽤不上的串⾏化;⼀致性在正常运⾏的时候依赖于串⾏化,在异常崩溃的时候也不能保证。这样实现的事务不要也罢。你这么答复⾯试 官,应该能加到分吧。

303、请教下主键索引的存储顺序
CREATE TABLE demo.test_pk_vs_non_unique_index(
id INT NOT NULL,
val INT,

PRIMARY KEY (id),
INDEX idx_val (val)
) ENGINE=INNODB;

INSERT INTO test_pk_vs_non_unique_index VALUES(3, 33); INSERT INTO test_pk_vs_non_unique_index VALUES(1, 11); INSERT INTO test_pk_vs_non_unique_index VALUES(5, 55); INSERT INTO test_pk_vs_non_unique_index VALUES(2, 22);

SELECT * FROM test_pk_vs_non_unique_index; id val
------ -------- 1 11
2 22
3 33
5 55

INSERT INTO test_pk_vs_non_unique_index VALUES(4, NULL); INSERT INTO test_pk_vs_non_unique_index VALUES(6, NULL);

SELECT * FROM test_pk_vs_non_unique_index; id val


4 (NULL)
6 (NULL)
1 11
2 22
3 33
5 55

问题:为什么⾏的返回顺序不是1,2,3,4,5,6?
因为明明ID列是主键,主键索引就是聚集索引,就应该是顺序排列。

为什么另⼀列上的索引上有空值就影响了主键的存储顺序?"
答:你每个语句explain下,看看怎么使⽤索引的,对照再分析看看。

304、 关于“根据身份证查询地址”这个有点疑问,当有索引(身份证号,姓名)这个索引,确实按照最左前缀原则可以⾛索引,但是查询地址是要回表的吧,如果加索引(身份证号,地址) 就不回回表了,但是会有维护索引的成本,那该如何舍取呢?
答:如果第⼆个查询不多,我会选择就只有 (身份证号,姓名)这个索引,然后查地址的语句,可以⽤上这个索引,回表查。

305、在⾼性能mysql这本书中提到索引并不总是最好的⼯具,其中提到对于⾮常⼩的表,⼤部 分情况下全表扫描更⾼效,对于中到⼤型的表,索引就⾮常有效?我的问题是怎么定义⾮常⼩的表呢?怎么定义中到⼤型的表呢?
答:其实就是看扫描⾏数。我觉得⼩于100⾏叫做⾮常⼩的表(⼀般是放些配置参数啥的)。

306、我看到你回复留⾔说N树的N是由⻚⼤⼩,和索引⼤⼩决定的。请问⻚是指什么呢?索引
⼤⼩,是指索引包含的字段数,还是指索引包含字段数的字节呢?
答:⻚就是数据⻚(默认16K,可调),索引定义⾥所有字段字节数的和。

307、请问第12篇专栏为什么表数据删掉⼀半,表⽂件⼤⼩不变 在哪⾥可以看到呢答:表t的数据存在⽂件t.ibd⾥⾯。

308、下⾯两条语句有什么区别,为什么都提倡使⽤2:
1.select * from T where k in(1,2,3,4,5) 2.select * from T where k between 1 and 5" 答:第⼀个要树搜素5次,第⼆个搜索⼀次。

309、【⽂章链接】原⽂章链接地址如下
http://hedengcheng.com/?p=577
但是这个地址有时候打不开有的时候打开很慢,我找到了⼀个转载了这篇⽂章的博客,地址如下https://www.jianshu.com/p/7a4b4f821c8d
【例⼦】

dba@db_test 05:08:05>show create table trade_detail\G
*************************** 1. row *************************** Table: trade_detail
Create Table: CREATE TABLE trade_detail (
id int(11) NOT NULL,
tradeid varchar(32) DEFAULT NULL,
trade_step int(11) DEFAULT NULL,
step_info varchar(32) DEFAULT NULL, PRIMARY KEY (id),
KEY idx_test (tradeid,trade_step,step_info)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

dba@db_test 05:08:14>select * from trade_detail;
±—±---------±-----------±-------------+
| id | tradeid | trade_step | step_info |
±—±---------±-----------±-------------+
| 1 | aaaaaaaa | 1 | cus |
| 2 | aaaaaaaa | 2 | update |
| 3 | aaaaaaaa | 3 | commit |
| 4 | aaaaaaab | 1 | add |
| 5 | aaaaaaab | 2 | update |
| 6 | aaaaaaab | 3 | update again |
| 7 | aaaaaaab | 4 | commit |
| 8 | aaaaaaac | 1 | bbc |
| 9 | aaaaaaac | 2 | update |
| 10 | aaaaaaac | 3 | update again |
| 11 | aaaaaaac | 4 | commit |
±—±---------±-----------±-------------+

SQL1
dba@db_test 05:08:26>desc select * from trade_detail where tradeid>=‘aaaaaaac’ and trade_step>2\G

*************************** 1. row *************************** id: 1
select_type: SIMPLE table: trade_detail partitions: NULL type: range
possible_keys: idx_test key: idx_test
key_len: 104 ref: NULL rows: 2
filtered: 33.33
Extra: Using where; Using index

SQL2
dba@db_test 05:08:47>desc select * from trade_detail where tradeid>‘aaaaaaac’ and trade_step>2\G
*************************** 1. row *************************** id: 1
select_type: SIMPLE table: trade_detail partitions: NULL type: range
possible_keys: idx_test key: idx_test
key_len: 99 ref: NULL rows: 1
filtered: 33.33
Extra: Using where; Using index

【疑问】

mysql在确定First Key的时候为什么对于>=可以继续向下匹配,⽽对>却停⽌匹配了,这个原因是什么?能否从B+树遍历的原理进⾏解释⼀下?
留⾔部分我咨询了这个问题,您说举⼀个例⼦,所以我就举了⼀个例⼦,这个表是引⽤的您其它
⽂章⾥的表和数据。⾮常感谢答复。"
答: 他这个应该只是对于First Key , ⽐如条件是⽐如条件是 trade_id > ‘aaaaaaaa’ and trade_step=3 and step_info=‘update’
这时候优化器第⼀次查询的是 拿到id=1的这⼀⾏,但是因为是⼤于,所以不需要继续判断trade_step=3 and step_info='update’这两个条件,直接往后找 (这个first key逻辑就结束了),
接下来再找就找到 aaaaaaab才算第⼀个开始有效的,
我觉得你认为这时候命中的是 id=3 和 id=4 的这两⾏在idx_test的这两个key的间隙好了然后继续往后扫,扫到找到的值,超过范围为⽌"。

310、⽂中说了利⽤最左前缀原则可以利⽤ 身份证号+姓名 这个索引⽀持“⽤身份证号查询地址”这个需求,⽽这个需要回表⼀次,利⽤身份证号字段索引也需要回表⼀次,这两有什么区别么?
答:在这个查询上没有区别。主要是如果有身份证+姓名的索引(为了使⽤覆盖索引),那么就 没有必要加身份证号单独的索引"。

311、对于最左前缀的理解,⽐如建⽴了⼀个联合索引(a,b,c),有以下查询条件: 1.WHERE a=xxx AND b=xxx
2.WHERE a=xxx AND b=xxx AND c=xxx 3.WHERE a=xxx AND c=xxx
4.WHERE a=xxx
5.WHERE b=xxx AND c=xxx 6.WHERE b=xxx
7.WHERE c=xxx
对于1,2,3,4情况会⾛索引;1,2,4情况不会进⾏回表操作(⽤到索引下沉),效率较
⾼;
3会⽤到索引a,但索引c失效,会进⾏回表操作,效率会低⼀些;
5,6,7情况都不⾛索引,会进⾏全表扫描,效率低,这样理解对吗?"

答:取决于你前⾯是select 什么哦,如果是select *的话,124还是要回表,3只能⽤索引的a部分是对的,567 也对。

312、"C、a索引是不必要的,因为c索引是次级索引,指向主键索引,已经包含了a,可以使⽤
a排序,满⾜第⼀种查询,C、b索引是必要的,在a字段未知的情况下不能使⽤b字段排序,我觉得除了主键索引外,只需要c、b索引就可以了。
答:再有⼀个(c,b)的联合索引,可以避免where c=N order by b这个语句的排序。

313、⽂中提到的索引下推案例:(name,age)
查询 :like ‘张%’ and age=10,会判断索引中的age值。但是我看「⾼性能MySQL」书中提到,对于多列索引,如果查询中有某个列的范围查询,则其右边所有列都⽆法使⽤索引优化查 找。这与⽂章内容有些出⼊,是MySQL版本的问题吗?这是MySQL有索引下推之前才有的缺陷?"
答:这两个没有⽭盾哈。“如果查询中有某个列的范围查询,则其右边所有列都⽆法使⽤索引优 化查找”, 这⾥说的使⽤索引优化查找,应该是指“不能⽤索引快速定位”。索引下推能⼒其实
age也不是⽤来“快速定位”的,只是⽤在索引遍历中“快速过滤”的"。

314、关于覆盖索引这块,我有点疑问,如果select刚好要查询ID,我觉得是可以覆盖索引的, 但是如果我要提取整个⾏数据,该怎么要呢,先查出ID list,然后select where id in ?
答:过程确实就是这样的,先拿到id,然后id去主键索引查整⾏的数据,这时候就⽤不上覆盖索 引啦。

315、问题⼀:身份证号和name做联合索引,⽤身份证查name会快是什么原理? 问题⼆:查询的时候是会在索引数⾥直接读出name,这是下推索引才会这么做吗? 问题三:这时候查询是⾛这个联合索引还是身份证的主键索引,为什么?
答:1. 覆盖索引。
2.这个不是索引下推哈,就是覆盖索引的概念(之所以要纠结概念,是因为覆盖索引5.5就有 了,⽽索引下推5.6才有)。
3.⾛这个联合索引,因为快。

316、联合索引怎么就不回表了,按照前⾯的例⼦只有查询字段是主键索引id的时候才不⽤回表 呀!这个就不好理解了呀!

答:如果联合索引是由a,b组成的,那么 select b from t where a=N 就不⽤回表"。

317、对于索引的选择还是有点疑惑。业务有个表⼤约⼗列数据。其中业务上需要唯⼀索引 X (A, B, C)。但是90%查询语句需要列 (A, B, C, D, E), 为了提⾼性能最好的选择是加⼀个普通索引来覆盖列 Y (A, B, C, D, E),但是这样问题就是索引X和Y的内容重复了。在数据库层⾯上有没有好的解决办法,在不需要唯⼀索引X的办法下来确保(A, B, C)列的唯⼀性,还是说X索引和Y索引都是必须的?
答:就建⼀个X (A, B, C) 好了,X已经是唯⼀索引了,不需要 Y (A, B, C, D, E)了。

318、mysql> select * from tuser where name like ‘张 %’ and age=10 and ismale=1;
为什么这个语句只⾛了name的索引,不⾛age,是只有name like ‘张三’ and age=10 才会⾛
age吗?
答:对的 ,前缀匹配哈。不过不是“只⾛了name的索引,不⾛age”,这是同⼀个索引,⽐较准确的描述是: 使⽤了这个索引的前两个字节做快速定位, 然后由于有index condition
pushdown优化,接下来遍历索引过程中可以⽤age来过滤掉不满⾜条件的记录。

319、b+树中的索引节点应该都是由指针和索引组成。但现在要将磁盘索引节点加载到内存中, 这些指针地址是怎么映射的(磁盘和内存指针的映射)?
答:⾸先磁盘的数据和数据⻚是⼀样的,所以磁盘只能记录 “我的第⼀个叶⼦节点在page_n”在内存⾥⾯也是,然后当把page_n读到内存以后,内存⾥⾯记录的是“page_n”的内存地址在哪
⾥。

320、"对于覆盖索引的疑问,当 select ”⾮主键“ from T where k = ;的时候是不是就不算覆盖索引了,只有查询需求是主键的时候才算覆盖索引。如果是这样的话,还是有回表的操作, 那当查询“⾮主键”时的过程是不是如下:
1.通过索引 k = 123 找到 ID主键 1

2.通过ID主键 1** 找到对应的数据⾏ row;
3.对⽐row中的 “⾮主键” 字段,相等就返回给Server层。
答:不⼀定,也可能是⼀个联合索引的⼀部分,⽐如有⼀个联合索引 (k, a),然后执⾏ select a from t where k= … 也是可以⽤上覆盖索引的。

321、问题⼀:之前⼀般认为range查询⽐如"a > 5 and b = ‘123’“在联合索引(a,b)中b是不起作⽤的,在ICP下是不是意味着b就可以起到作⽤了,我们还是应该尽量将查询中⽤到的字段 放⼊联合索引中。
问题⼆:针对1的问题,“a > 5 and a < 10 and b=‘123’”在ICP作⽤下的执⾏过程是什么样⼦的?”
答:1. 是的
2.流程是这样的:
a)把 a>5 and b='123’传⼊引擎,
b)引擎找到第⼀个a>5的⾏(这⾥是快速定位),如果发现b<>‘123’,找下⼀个,直到满⾜b=‘123’,
c)把找到的⾏返回给server层, server层根据a是否⼩于10决定要不要取下⼀个。

322、alter table T engine=InnoDB 重建索引是否会产⽣⾏锁? 答:5.6版本以后⽀持onlineddl, ⾏锁不会,有mdl读锁。

323、⽹上很多资料提到联合索引 (a, b) ,在查询语句where a = xxx and b = xxx 和 where b
= xxx and a =xxx 都是能⾛到索引的,跟您在⽂中提到的,(age ,name) 不太⼀样。答:能⾛到索引,但是不能使⽤多name+age全字段快速定位。

324、公司有订单表,有些核⼼字段,⽐如订单号.时间(整型,时间戳,范围查找).订单状态(整 型,6个值,可能in,可能=).客户标识(整型,⼏百个值).付款⽅式(整型,5个值),设备号(字符串,有权限需要in),这6个字段后台都会⽤到查询筛选,⽽且不选的情况下条件就不传,按照联合索引最左原则,那么可能要建⼏⼗个索引,这是不可能的,这个表做了按⽉分表, 数据量⼀张表⼤约1000万,不建⽴索引的话,后台选的条件没有建索引就会⾮常慢,强制最多只 能查连续两个⽉的数据(union all),请问⽼师有什么好的解决⽅案么?
答:嗯,这种情况是挺麻烦的。
得按照查询的模式,选最常⻅的来创建组合索引。⽐如如果时间+客户标识⽤得最多,就创建者 两个的联合索引。对于⽐较少⽤的条件,单独给这个字段建索引,然后查id出来跟别的字段的查 询结果,在客户端取交集,也是⼀种思路。
总之,这种需求是最头⼤的。 325、联合索引c,b,where条件⾥为c,order by b,这样的语句,联合索引把c,b都⽤到嘛?

答:会,前提是where c= ,不能⽤⼤于或者⼩于。

326、CREATE TABLE c_test( cid INT,
cnum1 INT, cnum2 INT
);
ALTER TABLE c_test ADD INDEX index1(cnum1,cnum2); DELIMITER ;;
CREATE PROCEDURE idata() BEGIN
DECLARE i INT; SET i=1;
WHILE(i<100000)DO
INSERT INTO c_test VALUES(i,i,i); SET i=i+1;
END WHILE; END;; DELIMITER ;
CALL idata();
SELECT COUNT(*) FROM c_test;
EXPLAIN SELECT * FROM c_test WHERE cnum1>999 AND cnum2=1000;
⽼师这是我的实现过程,我⽤explain看并没有使⽤到索引啊。
id select_type table partitions type possible_keys key key_len ref rows filtered Extra 1 SIMPLE c_test \N ALL index1 \N \N \N 100089 5.00 Using where"
答:表示这时候优化器认为全表扫描⽐较好。

327、⽂中提到考虑空间时,name字段是⽐age字段⼤的,所以建议⽤(name,age)联合索引和age单个索引。不太明⽩这⾥说的⼤,指的是?
答:字段⼤⼩(单个name占⽤的空间⽐age⼤)。

328、mysql⾥的B+树这种索引结构是怎么持久化到硬盘的?数据库的运⾏过程中如果想把某⼀个B+树节点对应的内存⻚存到硬盘中去,具体怎么做呢?在硬盘中怎么维护这种B+树这种结构 呢?
答:内存数据⻚和磁盘数据⻚是⼀⼀对应的,持久化的时候就直接覆盖写进去,内存⾥记了数据
⻚在⽂件中的偏移量;数据⻚从磁盘读⼊内存的时候,记录了内存地址;这样实现互相找到。

329、5.6版本前只搜索了第⼀个字段就回表,那岂不联合索引都没有啥⽤了?
答:是说,⽤不上连续前缀索引的情况下才这样。对于索引(a,b,c)如果是a=1 and b=1 and c=1 还是可以都⽤的,但是如果是a=1 and b<>1 and c=1就只能⽤第⼀个。

330、我发现我司的数据库(Oracle)⼏百张表的主键ID都是32位的UUID。看完本章,是不是说明我们的设计有问题;相⽐⾃增(Oracle⽤的是序列)ID,在同样多的数据下,UUID索引的
⽂件要⼤⼤的增加。因为现在线上的数据⽐较庞⼤(表的数据动辙千万级别的),表与表之间的关联关系都是通过UUID来关联的,⼀张表的修改就会牵动全身,请问有没有好的优化⽅案?
答:Oracle的原理我不够熟悉,不能乱说哈。
如果是在MySQL ⾥,既然都⽤UUID关联了,要做改动就要很⼩⼼。可以考虑保留UUID,并在这个字段上创建⼀个索引。同时创建⼀个字增主键ID。

331 、 假如有⼀张表, 有多个状态的字段, ⽐如 T(id , … delete_flag, order_status,
handle_flag), 因为这些字段值域的区分度不⼤,⽐如都是0~3,所以按照之前的对索引的理解,针对这⼏个字段建索引效果应该不是很好,但是如果数据量⼤的话,按上⾯的条件查询等于全表扫描,会⽐较慢,实际⼯作中的场景,请问有什么好的优化建议?
答:这个其实是要看查询怎么写。
⽐如语句就是 select count(
*) from T where delete_status= … 那也只能给delete_status 创建上索引, 不过如果
delete_status的区分度很低,这个语句跟全索引扫描的效果也差不多。还是要从业务设计上优化。

332、order by是对结果集排序的,与ca和cb索引关系不⼤。答:还是有关系的,索引合理的话不需要排序了。

333、覆盖索引必须要覆盖所有的查询条件中的列吗,我经过试验发现,覆盖索引不能只覆盖要 查询的列,同时必须将WHERE后⾯的查询条件的列都覆盖,能解释⼀下这个原因吗?
答:你的发现是对的,因为覆盖索引的⽬的就是“不回表”,所以只有索引包含了where条件部分 和select返回部分的所有字段,才能实现这个⽬的哦。

334、组合索引顺序选择的时候,为什么字段⼤的排在前⾯会更省空间呢? 答:(a,b) + (b)
如果是(⼤,⼩),就需要⼀个额外的(⼩)索引;
如果是(⼩,⼤),就需要⼀个额外的(⼤)索引。

335、问mysql为什么没有采⽤最右原则呢?
答:只能选⼀个嘛,⽽且最左前缀才能⽀持⾃然的range查询。

336、没分析出来⼆级索引的叶⼦节点的数据结构,不知道是不是(key,value)形式?并且这个相同的key来说,value还是有序的?
答:可以理解为还是key,value结构(其实是树形式组织的),不以value的顺序来组织,⽽是
key的顺序,key是没有“相同”的,你把主键值考虑进去就能发现了。

337、我不明⽩为什么执⾏select * from T where k between 3 and 5是为什么还要执⾏第5步在 k 索引树取下⼀个值 k=6,不满⾜条件,循环结束 因为我理解的是找到3 然后找到5 就能把这⾥对应的数据找出,不应该再去往后请求了。
答:不是唯⼀索引的话,有可能5后⾯还有⼀个5。

338、覆盖索引这种情况,是不是查询的必须是主键,且是声明了主键的表。
答:只要查询的条件和返回字段中的,所有字段都能被⼀个⾮主键索引覆盖,就能⽤覆盖索引。

339、我刚才⾯试的时候 ⾯试官说mysql索引除了叶⼦结点要进⾏磁盘io之外 其他的结点都存放在内存 你觉得这样的说法对吗 这样内存不会不够放 我的观点是每个结点会进⾏⼀次io
答:额 两种都不太准确。应该说⾮叶⼦节点也可能放在磁盘上,取决于内存⼤⼩。只是⼤多数数据量下,⾮叶⼦节点不多,留在buffer pool的概率⾼。

340、⼀次查询为啥不能只回表⼀次呢?⽐如范围查询通过⼆级索引定位到全部符合条件的主键 在回表⼀次。
答:这样还需要地⽅把”满⾜条件的所有主键值“存起来,就要⽤到临时表了,性能反⽽更差。

341、建⽴索引会增加insert和update时的消耗,但是能加快查询,如果做读写分离,主库不建
⽴索引,从库建⽴索引,带来的⻛险是主从同步的时候延迟会更加⼤,不知道这种想法靠不靠谱呢?
答:有这么⽤的哈。主从延迟其实也不会增⼤很多,后⾯章节会介绍到 change buffer的优化, 只是说如果从库重做的时候,系统要记得把索引再给加上。

342、专栏中说“那么,如果既有联合查询,⼜有基于 a、b 各⾃的查询呢?查询条件⾥⾯只有b 的语句,是⽆法使⽤ (a,b) 这个联合索引的,这时候你不得不维护另外⼀个索引,也就是说你需要同时维护 (a,b)、(b) 这两个索引。”我是有疑惑地。
建表及索引语句如下:
create table abc(id int primary key,a int,b int,c int) ALTER TABLE abc ADD INDEX idx_a_b_c (a,b,c)
这是我的执⾏计划:explain select * from abc where c=1;
按理说where c=1 不满⾜最左匹配原则,可是我的执⾏计划⾥⾯⾛了索引,很困惑。1 SIMPLE abc index idx_a_b_c 15 1 100 Using where; Using index
答:using index表示,使⽤了覆盖索引,也就是说确实使⽤了这个索引,但是执⾏的时候,是在这个索引⾥“全索引遍历”,这个索引没有起到加速的作⽤。

343、对session c的写锁会阻塞session d的读锁有个疑问,因为之前session a没有释放读锁, 那么session c应该⼀直取不到写锁,这个时候session d是不是应该有机会取到读锁先于session c执⾏?
答:没有,session C 在等待的时候就开始阻塞后来的请求了,不是拿到(拿到以后反⽽很快执
⾏就放过了)。

344、mysql 5.6不是⽀持online ddl了吗?也就是对表操作增加字段等功能,实际上不会阻塞读写?
答:Online DDL的过程是这样的:
1.拿MDL写锁

2.降级成MDL读锁
3.真正做DDL
4.升级成MDL写锁
5.释放MDL锁 1、2、4、5如果没有锁冲突,执⾏时间⾮常短。第3步占⽤了DDL绝⼤部分时间,这期间这个表 可以正常读写数据,是因此称为“online ”
我们⽂中的例⼦,是在第⼀步就堵住了。

345、在对⼀个表做增删改查操作的时候,加MDL读锁;当要对表做结构变更操作的时候,加
MDL写锁。⽼师,加了MDL读锁之后,还能增删改吗?不⼤明⽩。 答:可以,因为增删改执⾏都只要求MDL读锁,读锁之间不互斥的。

346、添加⼆级索引会加什么锁?
答:属于DDL,变更表结构,过程做要加MDL写锁。

347、update table set xx;这种更新全表的锁是mdl吗?感觉应该是第⼀种全表锁。答:也不是,还是⾏锁,只是这个表的所有⾏。

348、MDL只有在事务中才会被加上么 , 如果没有开启事务直接进⾏增删改 MDL不会默认加上么。 ⽐如我没有加事务, 去执⾏⼀个更新语句, ⾸先会先去找这个数据,然后在进⾏更新
。 整个过程被拆成两步进⾏,这个期间没有锁的么?
答:是没有“没加事务”的概念的,你不显式地启动事务,MySQL也会按照“单语句事务”处理 的。

349、⽐如某台服务器空间硬盘为1T,在上⾯部署⼀台单机的mysql。随着时间的增⻓,mysql 空间不够了。然后再给这台机器挂⼀个1T的磁盘。请问mysql怎么使⽤这个1T的磁盘?我对服 务器这块⽐较⼩⽩,是不是需要将这1T的硬盘变成服务器的系统盘才能使⽤?
答:⼀般⽐较简单的做法是把binlog 路径配置到新磁盘。

350、在从库执⾏readonly 会不会影响主从复制,也就是说会不会导致从节点⽆法写⼊master 提供的数据了?
答:不会,readonly对super权限⽤户⽆效,执⾏binlog的线程是super权限。

351、问题⼀:上⾯的那个因为mdl锁把整个库搞挂的例⼦⾥,如果⽤pt⼯具来操作,会出现同 样的情况吗?
问题⼆:那个例⼦⾥显示select语句前加了begin,是不是select的时候不加begin,就不会出现同样的情况呢?
问题三:online ddl 的copy⽅式和inplace⽅式,也都是需要 拿MDL写锁、降成读锁、做DDL、升成写锁、释放MDL锁吗?
答:1. Pt的过程也是有操作表结构的,所以会类似。
2.对,没有begin的话,这样select执⾏完成以后,MDL就⾃动释放了哦。
3.是,是否online都是第三步的区别,另外四步还是有的。

352、⽂中讲到了⼩表加字段需要注意的地⽅,那么⼤表加字段呢?如果⼤表的访问量和新增记 录很频繁,是否⽆法做到灵活加字段呢?
答:⼤表⼀般反⽽⼤家会注意,⼤表在线更新现在⽤⽐较多的是gh- ost。

353、已经有cb索引了,为什么还要保留c呢?前缀索引不是可以复⽤吗? 答:有c的话,c=N order by a就需要排序。

354、⽼师如果⽤的是where A 再 group by c 那我主键是不是可以⽤a, c还是单独c? 答:如果你有⼀个索引是(a,c), 这个查询语句可以避免排序的。

355、为什么我在⾕歌还有其他⽹⻚上搜索死锁的处理⽅法只有kill进程呢?您提到的commit也 能解决死锁适⽤于什么场景呢?
答:不是commiit,是rollback. 当然,kill就是⼀种主动放弃的做法,是实现rollback的⼀种操作。

356、第⼀关于全局锁,在做备份的时候,为什么要让让全局只读的状态,我们⽤⽼师说的
Mysqldump 的single transaction ⽅法不是更好吗? 第⼆是在进⾏Lock tables t1 read ,t2
write,为什么其他线程不能写t1,读t2,A在执⾏Unlock tables 之前,为什么可以读t2? 答:1. 如果库⾥⾯有不⽀持事务的表。
2. 前半句因为锁就是这么定义的;后半句,write⽐read权限⾼。

357、Online DDL的过程是这样的:
1.拿MDL写锁
2.降级成MDL读锁
3.真正做DDL
4.升级成MDL写锁
5.释放MDL锁
1、2、4、5如果没有锁冲突,执⾏时间⾮常短。第3步占⽤了DDL绝⼤部分时间,这期间这个表 可以正常读写数据,是因此称为“online ”
问题:
如果第四步升级为MDL写锁的时候,这个表的MDL锁有其他事务占着,那么这个事务会阻塞, 等到可以拿到MDL写锁是吧"
答:对。⽽且如果不幸⼀直拿不到,最后锁超时了,就只好回滚这个DD了操作。

358、按您的说法:select会加MDL读锁,⽽增删改查也会加MDL读锁,读锁互不排斥,那么未在事务中全表扫描过程中,可以执⾏dml操作。
以前我⼀直以为
1.select不会加锁,所以dml操作可以执⾏;
2.ddl操作被阻塞的原因是,有对该表的DML或DDL操作未执⾏完;没成想事务中select语句也能对DDL操作造成堵塞。
还有个疑问:如果select没在事务中执⾏,那么在select执⾏过程中,应该也是不可以执⾏DDL 操作的吧。
答:语句执⾏过程中肯定是加了MDL读锁的,所以会被锁。

359、在mdl锁这⼀块有⼀点不清楚。 a获取mdl读锁,这时候c要获取mdl写锁会被阻塞,c被阻塞是因为要等a释放,才能获取写锁, 为啥c在被阻塞的时候,其他的查询语句也会被阻塞,不是说读锁不互斥吗?还是说我理解错 了,应该理解为c获取写锁之后,在没释放写锁期间,其他的读锁会被阻塞。
答:你没理解错,5.6的实现确实是“⼀个DDL,由于被其它线程的MDL读锁堵住,⾃⼰⽆法执
⾏,但是会堵住后续MDL读锁的申请”。

360、您说的这个读锁之间不互斥,因此你可以有表锁多个线程同时对⼀张表增删改查。可是下
⼀章⼜说表锁同时只能有⼀个做更新操作,是否⽭盾?

答:没有⽭盾,Innodb做数据的增删改查的时候,不需要对表加表锁。

361、原⽂:举个例⼦, 如果在某个线程 A 中执⾏ lock tables t1 read, t2 write; 这个语句,则其他线程写 t1、读写 t2 的语句都会被阻塞。同时,线程 A 在执⾏ unlock tables 之前,也只能执⾏读 t1、读写 t2 的操作。连写 t1 都不允许,⾃然也不能访问其他表。
问题是最后⼀句,线程 A 解锁前,不允许写 t1,⾃然也不能访问其他表。为什么不允许线程 A 访问其他表,读其他表的操作也不允许么?这样设计的⽬的是为了规避啥问题吖?
答:这个不可考据了。不过我觉得有⼀个好处,就是可以让当前线程尽快执⾏unlock tables, 它⾃⼰操作不了别的表,会尽快去释放锁,否则⼀直锁着,影响系统并发度。

362、DML锁是不是只针对事务啊,⽂中说锁是在commit后才会释放,如果autocommit=1设置了,select之前不显示的开启⼀个事务,也没有相应的commit是不是就不存在DML锁了?
答:DML⼀般指的是增删改。不过若果select … lock in share mode; select … for update也会有⾏锁。

363、您说索引 ca 和索引 c 的数据是⼀模⼀样的。是否不太准确?索引 ca 是先按 c 排序,再按 a 排序。⽽索引 c 只是按 c 排序。因为有 order by a,所以删除索引 c 更为合适。是这样么,⽽索引 c 只是按 c 排序。
答:不是的,索引c是按照 cab排序的,跟索引ca⼀样。

364、MDL作⽤是防⽌DDL和DML并发的冲突,个⼈感觉应该写清楚,⼀开始理解为select和
update之间的并发。
答:嗯,特意写了是MDL“读锁”。

365、关于MDL写锁,有个更好的处理⽅法不知道是否稳妥?先基于原表创建⼀个临时表,完成字段更新修改;然后对原表加MDL写锁,加锁成功后把临时表更名为原表,这样加锁时间很短。
答:最早的ddl内部实现就是这么做的。不过加锁时间也不短。“先基于原表创建⼀个临时表,完成字段更新修改;” 这个过程,要包含“把原表的数据导⼊临时表”吧?导⼊的过程还是对原表加
MDL锁的。

366、既然⽂中说,session C被阻塞之后,后⾯需要MDL读锁的session也都会被阻塞。那么只要前⾯的A,B session的⻓事务执⾏完了,C就能执⾏掉(我想应该是按先到先得的顺序获取锁的吧),为什么后⾯⼜说“即使把⻓事务kill掉也未必管⽤,因为新的请求⻢上⼜来了”,为什么 新的请求不是排在session C后⾯呢?这样按理不会拖跨才对啊?就算是⼀些已经拿到MDL读锁的session在事务中的发送新sql,也应该为了避免死锁不需要再排队了吧。
答:“kill 也未必管⽤”是说,如果没有超时机制,我们只能先kill,再执⾏ddl,这样才保证不会堵住后⾯的请求。但是先kill完,可能ddl还没执⾏,⼜来新的查询,导致前⾯的kill⽩做了。是这个意思。

367、索引 cb 的组织是先按 c 排序,在按 b 排序,同时记录主键–c–|–b–|–主键部分a–主键部分为什么是a ⽽不是ab呢?
答:因为我们定义为(c,b),其实就是告诉MySQL,要按照c,c有序的规则来组织索引。如果 改成cab,那么cb就不⼀定保证有序了。

368、索引问题答案解释这个是不是再详细⼀点,还是搞不清楚为什么c索引和ca索引⼀样。
答:InnoDB会把主键字段放到索引定义字段后⾯,当然同时也会去重。所以,当主键是(a,b)的时候,定义为c的索引,实际上是(c,a,b);定义为(c,a)的索引,实际上是(c,a,b)。你看着加是相 同的。
Ps:定义为(c,b)的索引,实际上是(c,b,a)。

369、对于⼤表的ddl,是不是会⼀直阻塞着增删改查呢,对于⼤表的ddl,⼀般如何解决mdl锁阻塞的问题?
答:5.6以后⽀持online DDL,不会堵塞增删改查了。mdl锁是没办法的,不过mdl锁对正常使⽤没影响,因为增删改查申请对是mdl读锁,⽽ddl执⾏对⼤部分时间,占着的也是mdl读锁。

370、“读锁之间不互斥,因此你可以有多个线程同时对⼀张表增删改查。”这⾥是否写错了,应该是“你可以有多个线程同时对⼀张表执⾏查”,⽽不是“增删改查”,增删该不是写吗?
答:这⾥说的是“MDL读锁”,对数据表做增删改查的时候,加的都是“MDL读锁”。

371、使⽤ "mysqldump -single-transaction " 做备份,会不会存在备份期间有些数据被修改,⽽备份线程⼜⽆法读到最新数据,导致备份出来的数据跟真实数据之间存在差距呢?
答:对于备份⾏为来说, “备份出来的数据跟真实数据之间存在差距”是没关系的。

备份表示的是“在某个时刻,这个系统的状态”, 只要备份出来的数据,跟“这个时刻”是⼀致的就可以了,不需要跟“备份完成后”的时间点⼀致。

372、是在执⾏器执⾏时 ⽣成快照 进⾏锁表对吧?
答:要看是执⾏什么语句哈。基本都是在执⾏器锁的,但是不同的语句阶段不同,⽐如lock table ,就没有⽣成快照,直接锁表了;如果是myisam的更新语句,是在引擎内第⼀次使⽤表的时候加。

373、mdl锁那⾥读锁之间不互斥,因此你可以有多个线程同时对⼀张表增删改查;要是对同⼀⾏ 数据进⾏处理呢,会互斥吗?
答:对同⼀⾏数据进⾏处更新,会互斥,但是互斥的不是mdl锁,⽽是⾏锁。

374、例题中的订票系统,影院的余额表可不可以⽤流⽔的⽅式来记录,每天闲时汇总⼀次,这 样就没有update只有insert和select sum了。
答:嗯,如果是没有边界条件,⽐如⼀直加钱,这种可以的。但是如果有“退款”的逻辑,就不
⾏了。只记⽇志可能会给扣成负数。

375、为减少死锁检测,⽤中间件或者改源码,让更新同⼀条记录的的操作进⼊引擎层前排队, 也就是把并发转为串⾏,这样结果返回时间是不是就慢了?我的意思是假如同时很多⼈买⼀样东
⻄,排在前⾯的⼈余额扣的快⽴⻢可以看到,后⾯的得等待⼀段时间才能刷新出来?
答:相⽐与死锁检测吃CPU,这还是快的。⼀般⼀秒能处理5、6千次update,出现热点更⾏可能 就剩100不到。

376、 1⾏级锁只有update的时候会⽤到吗?insert的时候会⽤到?2 死锁检测时间复杂度是
O(n)是怎么计算的 ,100万级 3 死锁检测发现有死锁都是先回滚再在客户端重试,数据库有对死锁检测之后的处理吗?
答:1. Insert 也有的,⽐如你要插⼊两个相同的id值的记录。
2.每个线程在判断有没有死锁的过程中,都要扫⼀下所有其他⼈。
3.回滚了⼀个事务,剩下的就继续⼯作了。

377、设置⼀致性试图命令之前和之后区别,做保存点前后有啥不⼀样?

答:⼀致性视图之后对DDL暂时没影响,保存点设置之后也是。只是“回滚到保存点”这个操作会释放MDL锁。

378、⽂章中提到⾏锁是在InnoDB 事务中需要的时候才加上,那不加事务的DML语句(⽐如update)会不会加上⾏锁啊?
答:没有DML就不需要加⾏锁哈。

379、主动回滚死锁链条中的某⼀个事务,让其他事务得以继续执⾏,这个回滚是回滚后⾯的这 个事务吗?
答:系统会选,同等权重(⽐如相同类型的事务操作),会优先回滚最后的这个,成本最低。

380、如果有我有多个node,其中⼀个node 对数据加了⾏锁,结果挂掉了,还是只能等待
InnoDB⾃动50S释放锁吗?
答:如果客户端断开,对应的那个事务会回滚,锁就会放弃了。

381、针对其他同学提到的,对⽆索引的字段做更新,会锁定全表。试过对 A事务执⾏:begin; update t set t.num=t.num+1 where t.code=‘10001’; # t.code⽆索引
B事务执⾏:begin; insert into t values (null, ‘10002’, 100); # 第⼀列为id列确实,B事务在等待状态。⽽分析INNODB_LOCKS表,发现:
lock_id | lock_type B | RECORD
A | RECORD
为何 A 锁住了全表了,lock_type 不是TABLE ⽽是 RECORD 呢? 答:这⾥“锁全表”,表示的就是“锁了全表的所有⾏”。

382、第三讲说过,在可重复读隔离级别下,事务启动时会创建⼀个逻辑视图,整个事务存在期 都会⽤这个视图。那为什么在事务启动时如果没有加WITH CONSISTENT SNAPSHOT,假设时刻2 DDL语句到达,导出数据的时候不会报table definition has changed。为什么DDL在show create table之前到达不会报这个错误,show create table是做了什么事情呢?
答:Show create 表示这个语句“看过了”表结构。表结构数据和表的数据不同,不⽀持⼀致性读。

383、本节课讲的不⽀持⾏锁的引擎,只能使⽤表锁,⽽表锁同⼀张表在同⼀时刻只能有⼀个更 新。但是上节课讲的表级锁中的MDL锁,dml语句会产⽣MDL读锁,⽽MDL读锁不是互斥的, 也就是说⼀张表可以同时有多个dml语句操作。感觉这两种说法有点⽭盾!
答:不⽭盾,MDL锁和表锁是两个不同的结构。
⽐如:你要在myisam 表上更新⼀⾏,那么会加MDL读锁和表的写锁;然后同时另外⼀个线程要更新这个表上另外⼀⾏,也要加MDL读锁和表写锁。第⼆个线程的MDL读锁是能成功加上 的,但是被表写锁堵住了。从语句现象上看,就是第⼆个线程要等第⼀个线程执⾏完成。

384、在同⼀时间不是应该只有⼀个线程拿到锁吗?锁释放之后由其他线程争夺锁权限,这样的话 为什么会出现事务A等待B释放锁,事务B⼜会等待事务A释放锁?
答:锁是针对“⾏”的,他俩现在锁的不是同⼀⾏哦。

385、如果reset以后,是不是就失去了⻓连接的意义了呢?相当于再次进⾏连接。
答:不会,reset_connection只是复位状态,恢复到连接和权限验证之后的状态,没有重连。

386、select * from t where id>=1 and id<=4. id是主键,看⽼师之前的⽂章谅解, 这条语句是在主键索引⾥分别查询id等于1,2,3,4这⼏条数据,也就是在主键索引b+树中查找4 次。b+树叶⼦节点是有序且⽤链表的⽅式链接起来,为什么不在叶⼦节点直接查找。上次⽼师讲的是⾮主键索引,是这样的当时,不知道主键索引是否是我说的这样查找
答:如果是你的语句这样写,是不⽤找四次的,找到第⼀个(id=1)然后向右遍历就⾏。之前说的不⾏是说,在回表过程中,回表只能⼀个ID⼀个ID回来查。

387、update t set k=k+1 where id=1,这样⼀个语句是先拿读锁,读到k,然后再拿写锁去将数据改成k+1么?还是说⼀看到update这样的关键字就直接拿读锁和写锁?如果是第⼀种情况, 那2条同样的update语句,可能出现同时读到k的数据,然后其中⼀条更新后另外⼀条再更新, 这样数据就有问题了?
答:没问题呀,后加写锁的就等待。

388、如果是并发插⼊,会有死锁问题吗,他锁的哪些⾏呢? 答:可能会死锁,⼀个表有多个索引的话。

389、有以下情况 帮忙分析下会锁表不

update a,b set a.name = b.name where a.uid=b.uid and b.group=1; update c,b set c.age=b.age where c.uid=b.uid and b.group = 1;
如果两个语句同时执⾏期间 是不是有个执⾏不了 要等b解锁。还是说没有更新b的字段b不会锁,两个可并⾏执⾏?
答:这个你得同时贴表结构。还有,会不会锁,不是验证⼀下就可以吗,两个都⽤begin + 语句,两阶段锁协议会帮助你。

390、针对⾼频热点账户,问题⼀:可否根据账户id排序,来解决死锁问题,当然有可能热点账 户排在前⾯,导致持有锁时间变⻓,或者加钱账户可更新,减钱账户余额不⾜的情况。
问题⼆:可否将事务拆解,将加减钱的数据库事务拆解成减钱账户update,流⽔记录insert⼀个数据库事务,然后异步补偿加钱账户⽐如影院账户,达到最终⼀。
答:1. 这个场景⾥其实没有死锁的。不过⼤家都按照同样的顺序加锁,确实是避免死锁的⽅法
2. 这个在没有边界条件的时候是可以的。⽂中说到,其实影院也可能扣款,⽐如退票。那扣款是可能扣成0的。要做边界条件判断的时候,⽇志补偿的模式就满⾜不了需求了。

391、你说时刻2,时刻3,时刻4是代码执⾏的时刻,那能请问是Q2代码,Q3代码,Q4代码执
⾏的时刻麽?
答:不是,就是代码的注释⾥⾯写着的时刻2、时刻3…这些。

392、每个新来的被堵住的线程,都要判断会不会由于⾃⼰的加⼊导致了死锁,这是⼀个时间复 杂度是O(n)的操作。假设有1000个并发线程要同时更新同⼀⾏,那么死锁检测操作就是100万这个量级的。⽼师,为什么1000个并发线程,死锁检测是100万量级?
答:你先考虑,假设现在已经有999个线程在同⼀⾏等锁,新来⼀个请求也要访问这个⾏,他要 判断有没有死锁要判断多少次?然后这个结果乘以1000。

393、关于死锁检测innodb_deadlock_detect我想请教⼀下,是每条事务执⾏前都会进⾏检测吗?如果是这样,即使简单的更新单个表的语句,当每秒的并发量达到上千的话,岂不是也会消耗⼤量资源⽤于死锁检测吗?
答:如果他要加锁访问的⾏上有锁,他才要检测。这⾥⾯我担⼼你有两个误解,说明下:
1.⼀致性读不会加锁,就不需要做死锁检测;
2.并不是每次死锁检测都都要扫所有事务。⽐如某个时刻,事务等待状态是这样的:

B在等A,
D在等C,
现在来了⼀个E,发现E需要等D,那么E就判断跟D、C是否会形成死锁,这个检测不⽤管B和
A

394、平时⼯作中经常对数据库操作,对有些重要的业务库进⾏增删改的操作时,为了减少错 误,偶尔会在命令⾏⽤begin/commit来启动⼀个事务,结果查询⽆误了才提交。这种情况是不 是也会⽣成MDL或者⾏锁?是不是不应该使⽤这种⽅法?
答:这样在MDL问题不算⼤,这个操作会阻塞DDL,但是你们应该不会出现“你在操作的时候, 刚好有另外⼀个同学在改表结构”的操作吧?正常⼤家都会知会⼀下避免的。这种情况,怕的是
⼿⼯操作期间,从begin到commmit之间时间太⻓,如果是业务更新⾼峰期,就不好。

395、我理解同⼀个⾏记录 在每个事务⾥⾯ 只出现⼀次update,是不回出现死锁的吧? 还有⼀个问题,⼀个事务⾥⾯修改同⼀条记录两次,不是也会死锁了?
答:多个事务更新同⼀⾏,只是锁等待,如果还有更新别的⾏,可能造成循环依赖,导致死锁。
⼀个事务⾃⼰跟⾃⼰不会死锁。

396、之前遇到⼀个死锁的问题:
【描述】⼀个表,在2个字段上都加了索引,字段:roomId,extnumId; 然后有这样的更新语句: update set … where extnumid=? and roomId in(1,2,3); 在并发的场景下发⽣了死锁。后来查阅了资料,得到的知识点如下: 1. InnoDB 的 ⾏锁建⽴在索引的基础上,锁的是索引。
2. 上⾯的语句先锁⾮主键锁,再锁主键锁去更新。 不知道这2点结论对不对?另外,我上⾯那条update 是按照什么顺序锁索引的? 后来我的解决⽅式是:改成按主键更新解决的问题.
答:1. 前⾯结论对的.
2. 你是不是有重叠条件。就是不同的语句⾥,有出现相同的room id?⾏锁挂在索引上,如果使
⽤有索引的字段更新;同时⼜有另⼀个事务更新同⼀⾏没有索引⾏会发⽣什么?⾏锁挂在索引上是什么操作?
所以你举的例⼦,在扣⼀个总账户数额⼀条记录的时候 应该是不会出现死锁的,就是锁等待, 只是队列很⻓。

397、innodb⾏锁是通过锁索引来实现,如果id字段加索引,update 更新id = 1 和 id = 2记录,在更新id = 2时会进⾏阻塞吗?

答:同⼀⾏才阻塞

398、如果开启事务,然后进⾏死锁检测,如果发现有其它线程因为这个线程的加⼊,导致其它 线程的死锁,这个流程能帮着分析⼀下么?
答:好问题。理论上说,之前没死锁,现在A加进来,出现了死锁,那么死锁的环⾥⾯肯定包含
A,因此只要从A出发去扫就好了。

399、对于 回滚到 SAVEPOINT sp,在这⾥的作⽤是释放 t1 的MDL锁,这⼀句有疑问。
那么如果DDL语句在Q4之前到达了,会正常执⾏,但是这⾥已经执⾏了snapshot,然后后⾯⼜ 会回滚到之前的SAVEPOINT sp。中间有个DDL的,这样也回滚了了??我不是很明⽩回滚到SAVEPOINT sp 这个点。
答:没有回滚DDL哦,只是释放了mdl锁

400、“如果在 Q4 语句执⾏之前到达,现象:没有影响,备份拿到的是 DDL 后的表结构。”
其他的都能理解,就是这点很困惑:Q2已经建⽴了快照,为什么还能备份快照后,对表结构修 改后的表呢?不应该备份快照时的表结构吗?不然备份不就乱了吗?
答:Q2是启动了⼀致性视图,但⼀致性视图不包含表结构哦

401、⼤事务会影响主从延时吗?例如⼀个⼤事务⾥⾯执⾏更新语句,记得⽇志系统那章说,两 段提交,redo log的prepare和commit之间已经写binlog了。这样未commit之前已经有binlog 了,那事务未完成就可以发⽣主从复制了吧。还是说redo log的commit跟事务的提交是两码事?
答:⼤事务⼀般都会导致主从延迟,就在binlog写完之后才开始发。

402、使⽤ in 条件批量更新如何加锁?
答:In这个,如果你说的是后⾯是常数字列表,那就是多个等值查询那么处理; 如果带⼦查询,就复杂些,还需要根据隔离级别来判断的。

403、row 格式下,delete ⼀个⽆主键 innodb ⼤表,主库很快完成,可从库每删除⼀⾏都需要全表扫,导致延迟很⼤,除了加主键外,还有其它的⽅法吗?
答:有没有普通索引,MariaDB 可以根据普通索引优化的。不过还是要形成习惯,表⾥带主键哈。

404、上讲降到dml时会产⽣读MDL锁(表锁),也就是update会持有读MDL。读和读不互斥。但是对于⾏锁来说。两个update同时更新⼀条数据是互斥的。这个是因为多种锁同时存在时, 以粒度最⼩的锁为准的原因么?
答:不是“以粒度最⼩为准”,⽽是如果有多种锁,必须得“全部不互斥”才能并⾏,只要有⼀个 互斥,就得等。

405、⽼师看评论⾥说,⾏锁是锁在索引上的,那么如果存在两条sql,通过不同的索引去锁定 同⼀⾏记录,innodb会怎么处理呢?我实验的结果是会阻塞,但是不太明⽩怎么阻塞的。
答:如果是update语句,⼀定会在主键索引上也加上⾏锁,这两个在主键索引上形成等待关系。

406、减少锁冲突的思想是分治法吗?类似分库分表,把压⼒分摊,同时增加容灾能⼒.
答:要看是什么场景。如果是⽂中提到的所有线程都在更新同⼀⾏,分表也没⽤过了,就不是分治;如果是那种分散的更新压⼒,可以⽤分治。

407、表U的钱包记录表是会负数的 消费就是负数 充值就是整数 所以求和之后是余额 这样和在
⽤户表单独加⼀个余额字段有什么劣势吗?
答:但是很多业务其实是不能接受负数的,所以⼀般都是要实时判断,当然如果没有“边界条件 限制”的,是可以的。

408、本课举的例⼦,预售⼀年电影票导致CPU占⽤率100%,这怎么可能是死锁导致的呢?这 种场景会出现互等锁的情景吗?
答:不会出现死锁,就是普通的单向锁等待。但是我们知道不会死锁,InnoDB可不知道,所以它得做死锁检测才能得到“没有死锁”这个结论,我们说吃CPU的就是死锁检测这个逻辑。

409、⾯试官问了⾏锁的原理, 我:⾏锁的实现原理?
⾯试官:对
我:不知道。。。
回来⼜看了⼏遍事务隔离的这⼏篇⽂章,发现确实不知道,丁⽼师能简单讲讲么?

答:主要是不知道⾯试官想知道多深的原理。我觉得08篇的内容可以讲讲的,每次更新产⽣⼀ 个新的数据版本,数据版本上加了锁。 如果来了另外⼀个更新,就会在这个新版本上被锁住。然后看看⾯试官怎么追问。

410、直接⽤update miaosha_goods set stock_count = stock_count - 1 where goods_id = #{goodsId} and stock_count >0的⽅式不就解决超卖了吗,为什么有⼈说并不能完全解决,与今天⽼师讲的⾏锁相违背。
答:主要是并发的时候有性能问题

411、有些资料看到的:InnoDB⾏锁是通过给索引上的索引项加锁来实现的,只有通过索引条件检索数据,InnoDB才使⽤⾏级锁,否则,InnoDB将使⽤表锁。⽼师,这个是正确的吗?
答:不对。你可以验证下,select * from t limit 1 lock in share mode, 看怎么锁的。
mysql不会先分析本次事务所需要的锁,⼀次性获取吗?这样会避免死锁。这样并发性能不好。

412、⽼师在⽂中说:所以,在执⾏事务 B 的 Q1 语句的时候,⼀看⾃⼰的版本号是 101,最新数据的版本号也是 101,可以⽤,所以 Q1 得到的 k 的值是 3。
1.这⾥不参考up_limit_id了吗?
2.如果参考,事务B的up_limit_id是在执⾏update语句前重新计算的,还是在执⾏Q1语句前重新计算的?
答:1. 判断可⻅性两个规则:⼀个是up_limit_id ,另⼀个是“⾃⼰修改的”;这⾥⽤到第⼆个规则
2. 这时候事务Bup_limit_id还是99。

413、可重复读情况下,事务c的102早于事务b的101,如果事务c再get k,那不是就取得101 的值了?
答:咱们例⼦⾥⾯,事务C是直接提交的,再执⾏⼀个GET 就是另外⼀个事务了。如果你说的是⽤begin 来启动⼀个多语句事务,那么事务c在更新后查询,还是看到row trx_id是102的。
【注意:如果它还没提交,101根本⽣成不出来,因为事务B被⾏锁挡着呢】

414、⽂中说: “读提交是在每条语执⾏前计算up_limit_id”,如果不计算会怎样?就变成了读未提交吗?
答:为啥不提交,bug 吗 ?⽆关不计算也不判断,就是读未提交;如果不计算,但是判断,那就是⽤事务的up_limit_id, 就是可重复读。

415、在重复读级别下,b事务中,执⾏update t set k=k+1 where k=1这句时,where条件中得到的k=1 ,k+1以后,不应该是2,么,为什么是3,您的⽂章⾥说,如果是2,c事务就失效了,所以 执⾏where语句和执⾏k=k+1不是在同⼀个逻辑⼀致性下执⾏的?那这样可重复读的语义是不是没有保证?
答:Update语句是“当前读”的逻辑,所以它拿到的k 是2哦。

416 、事务启动是begin 就记录up_limit_id 还是begin 之后的第⼀条select 语句开始记录
up_limit_id?我测试中,客户端A 执⾏begin后但没执⾏select,客户端B开始事务修改提交后, 客户端A再次select会读到这个修改值。⽽另⼀种begin后直接select,此后客户端B开始事务修改 后,客户端Aselect就是第⼀次Select的值。
答:Begin之后的第⼀个语句算启动事务。

417、假设我的T表很⼤,第⼀种⽅法只需要全表扫描⼀次,第⼆种需要全表扫描20次吧? 答:有Limit ,都是总共锁10000⾏。

418、在可重复读隔离级别下,事务在启动的时候就“拍了个快照”。
⽼师,这句话有点疑问,在可重复读隔离级别下,事务视图应该是在事务第⼀个查询执⾏时建⽴的吧,在下⾯这个例⼦中
demo1
A1: start transaction; A2: select * from t;
B1: start transaction;
B2: insert into t values (4, wangwu); B3: commit;
A3: select * from t;

demo2
A1: start transaction;
B1: start transaction;
B2: insert into t values (4, wangwu); B3: commit;

A2: select * from t;

demo1的A3和A2读到的数据是⼀样的即读不到B事务新插⼊的数据,但是demo2⾥的A2却可以读到B事务新插⼊的数据,能解答⼀下么?
答:我跟你是⼀致的,如果是 begin; select … 这种⽤法,事务是在select语句才正式启动。

419、对于可重复读,查询只承认在事务启动前就已经提交完成的数据;这个应该是第⼀次查询 时,不是事务启动时吧?
答:Begin不算事务启动哦,所以咱们俩是⼀致的。

420、是不是只有commit了之后的数据才会被加到有历史版本和当前版本的记录中呢 没有commit 是不是就是⾃⼰知道 存在内存的某个⻆落呢?
答:没commit也写,也⽣成版本,但是是未提交的,还加着锁。

421、对于读提交,查询只承认在语句启动前就已经提交完成的数据;⽽当前读,总是读取已经 提交完成的最新版本。⽼师请问下读提交和当前读有什么区别吗,我理解这两个都是读到已提交的最新版本?如果是的话,那为什么读提交这种隔离级别不直接基于当前读做呢,每次去算⼀下up_limit_id不麻烦吗?
答:不⼀样,读提交隔离级别下,⾸先是不加锁的。还有,你考虑下,⼀个语句开始执⾏的之 后,执⾏期间别的事务修改了数据的情况。

422、对于⽂中的例⼦假设transaction id为98的事务在事务A执⾏select(Q2)之前更新了字段,那么事务A发现这个字段的row trx_id是98,⽐⾃⼰的up_limit_id要⼩,那此时事务A不就获取到了transaction id为98的事务更新后的值了吗?
换句话说对于⽂中"之后的更新,产⽣的新的数据版本的 row trx_id 都会⼤于 up_limit_id"这句话不太理解, up_limit_id是已经提交事务id的最⼤值,那也可能存在⼀个没有提交的id⼩于
up_limit_id的事务对数据进⾏更新?还是说transaction id⽐up_limit_id⼩的事务都是保证已经提交的?
答:Innodb 要保证这个规则:事务启动以前所有还没提交的事务,它都不可⻅。但是只存⼀个已经提交事务的最⼤值是不够的。 因为存在⼀个问题,那些⽐最⼤值⼩的事务,之后也可能更新(就是你说的98这个事务)。所以事务启动的时候还要保存“现在正在执⾏的所有事物ID列表”,如果⼀个row trx_id在这列表中,也要不可⻅。

423、最⼤事务ID是保存在系统⾥⾯的⼀个全局变量吗?是excution_gtid?如果并发写很⼤,这个产⽣事务id的⽣成器会不会成为瓶颈?
答:是全局变量,事务启动后拷⻉为这个事务⾃⼰的内部变量的。不是gtid 哈,trx_id是innodb 的机制,gtid 是server 层的。

424、⼀个历史row版本可以删除的依据是什么,您⽂中提到的是没有事物再使⽤这个版本了, 感觉这句话还是很空洞,能不能具体说⼀下?
答:如果所有事务的⼀致性读的结果,都不需要再⽤到它的时候。⽐如我们例⼦中,Q2执⾏前, row trx_id=101 102的两个版本,就不能删除,因为事务A还需要依赖他们。

425、如果commit之前就⽣成了历史版本,当前版本的记录,那么如果之前的⼀个update没有
commit,没有释放锁,那后来新的⼀个事务select会不会因为那⼀⾏还有锁⽽提取全表数据等待呢?⼀致性读的时候读不是不加锁的吗?那这种情况怎么办呢?
答:⼀致性读的时候,只有未提交的版本都直接认为不可⻅(包括可重复读隔离级别和读提交隔离级别)。

426、 另起⼀个事务在当前事务更新前修改 , 如果新起的事物没有提交 ,那么当前的事物没有办法执⾏更新语句 ,会⼀直堵塞。如果新起的事物提交后 ,当前事务再更新,那么当前事务更新的数据⾏是最新的 ,再去查询也应该是当前事务更新的值,所以为什么会看不到新的值还是没有理解。
答:当前事务再更新,成功了的话,就是就是看到⾃⼰更新的,⾃⼰更新⽣成的版本⾃⼰是要可
⻅的(transaction_id=row trx_id)。

427、执⾏事务B的Q1语句时候,⼀看⾃⼰版本好是101,最新数据版本号不是102?是在事物
C基础上读到新的值。上⾯描述是不是错了?
答:执⾏Q1的时候,事务B已经更新过了,(1,3)已经⽣成出来了。

428、原⽂”如果落在⻩⾊部分,那就包括两种情况
a.若 row trx_id 在数组中,表示这个版本是由还没提交的事务⽣成的,不可⻅;
b.若 row trx_id 不在数组中,表示这个版本是已经提交了的事务⽣成的,可⻅。”中,b段落, 为什么row trx_id不在数组中,不是未来提交事务,⽽不是已提交事务?这个row trx_id如果是

已提交事务,怎么⼜在⻩⾊区域,不在绿⾊已提交区域?
答:在⻩⾊区域是因为,这些事务是在“低⽔位事务之后”开启的,但是⼜在“当前事务启动 前”提交的。

429、当开启事务时,需要保存活跃事务的数组(A),然后获取⾼⽔位(B)。我的疑问就是,在这两个动作之间(A和B之间)会不会产⽣新的事务?如果产⽣了新的事务,那么这个新的事务相对于当前事务就是可⻅的,不管有没有提交。
答:好问题,代码实现上,获取视图数组和⾼⽔位是在事务系统的锁保护下做的,可以认为是原
⼦操作,期间不能创建事务。

430、“InnoDB ⾥⾯每个事务有⼀个唯⼀的事务 ID,叫作 transaction id。它是在事务开始的时候向 InnoDB 的事务系统申请的,是按申请顺序严格递增的。”
⽼师您好,请问是begin/start transaction语句就申请到了trx_id,还是执⾏了第⼀个操作数据表的语句才有了trx_id呢?
答:都是“事务启动”的时候申请,所以是第⼀个操作表的语句。也可以是执⾏start transaction with consistent snapshot这个语句的时候。

431、想到⼀个业务上的问题:减库存的场景当前库存:num=200,假如多线程并发:
AB同时开启事务,A先请求到⾏锁, A:
start transaction;
select num from t where num>0;先查询当前库存值(num>0) update t set num=num-200; 库存减量
B:
start transaction;
select num from t where num>0;先查询当前库存值(num>0) update t set num=num-200; 库存减量
----结果—
A:查询到num=200,做了库存减量成了0 B:事务启动后,查询到也是200,等 A 释放了⾏锁,B进⾏update,直接变成 -200 但是 B 查询时,时有库存的,因此才减库存,结果变成负的。

⽼师,对于这种场景,怎么避免减成负值?给 select 加读锁或者写锁吗 ?这种select 加锁,对业务影响⼤吗?
答:这是个好问题,也是并发业务常⻅的问题。⼀开始Select 加锁虽然可以,但是会⽐较严重地影响并发数。
⽐较简单的做法是update语句的where 部分加⼀个条件: where nun >=200 .
然后在程序⾥判断这个update 语句的affected_rows, 如果等于1 那就是符合预期; 如果等于0,那表示库存不够减了,业务要处理⼀下去,⽐如提示“库存不⾜”。

432、在数据可⻅性规则那⼀部分中的第三种可能的b情况: "若 row trx_id 不在数组中,表示这个版本是已经提交了的事务⽣成的,可⻅。"对于这部分内容我开始不是很理解, 后来反复思考了⼀下, 可⻅性规则这部分是不是在说明这种情况: 因为数据的row trx_id是依次递增的, 但是事务由于创建和提交的时间不可预期所以transactionId可能是跳跃的, 所以假如有事务A, ⽐A的
transactionId⼤的数据的row trx_id对于事务A⼀定不可⻅, 但是⽐A的transactionId⼩的数据的row trx_id也可能在A的事务数组中, 所以要判断⼀次。不知道这么理解对不对?
答:是的,最后这个“可能”说得很好,可能在,也可能不在,就⽤“是否在数组中”来判断。

433、数据库show processlist存在⼤量opening tabale的情况,此时数据库接近瘫痪,这个跟阻塞有关么?
答:阻塞⼀般就提示为阻塞了,也可能根本就是并发太⼤。

434、在事务A中update语句set部分c读取到事务B更新后的值,⽽where 部分id读取的c是事务
A⼀致性视图中的值,由于两者不⼀致导致更新记录为零的对吗?
答:不是,整个update语句⾛的当前读,因此where读的就是事务B修改后的值啦。

435、我在mysql实践中遇到个问题,就是同⼀个后台服务的⽅法A中执⾏了插⼊⼀条数据成功后,然后⽅法B中去查询发现没查到这条数据,其中⽅法B是⼀个异步⽅法(另⼀线程),看⽇ 志发现查询是在插⼊后⼏⼗微妙,这是不是出现幻读了,有什么办法解决吗?隔离级别是Repeatedable Read,数据库没有读写分离,查询条件也是唯⼀索引的。
答:这个不是幻读呀,这个是写⼊了查不到。你看的“查询在插⼊之后⼏⼗微秒发起”,这个⽇志是客户端⽇志,还是数据库⽇志?数据库除⾮特别定制,否则没有微秒单位,如果是业务发送端的话,有可能查询是后发先到哦,毕竟才差⼏⼗微秒。

436、⽼师,有三个事务,事务A先启动tranid是100,事务B再启动tranid是101,都对id=1的
⾏操作,B先操作然后提交,A再操作然后提交,此时C开启事务执⾏查询id=1的⾏,那它应该拿的是tranid为101的数据,这样不就读不到最新的了吗
答:不是啊,此时C拿到的是A操作后的结果哦。

437、我现在理解是:当A对⼀⾏进⾏操作时,开启事务,创建transid,然后B再对这⼀⾏操作时,必须要等到A提交释放锁后啊才可以继续操作,所以说明transid是严格递增的,查询⼀⾏数据时只查找transid最⼤的数据就可以了。
答:不是,你想这个序列: A启动,更新id=1,
B 启动更新id=2,提交;
A更新id=2,再提交;

438、引⽤问题:
今天重新看了⼀下这章您的修改地⽅,有个地⽅不明⽩
落在⻩⾊区域未提交事务集合部分怎么还要分类,低⽔位+⾼⽔位不就是这个数组了吗,之前说,这个数组是记录事务启动瞬间,所有已经启动还未提交的事务ID,那不应该是未提交的事务吗,不就应该是不可读的吗?
之前说的是启动时会获取⼀个最⼤row trx_id,所有⼤于这个id都不认,这个id肯定是已经提交了的事务的才对啊,这个id不才应该是数组的⾼⽔位吗,这⾥有点懵了。
作者回复:你设计⼀个“⽐低⽔位⼤,但是在当前事务启动前,就已经提交了的例⼦。

⽆法理解,在当前事务启动前就已经提交了,不是已经不活跃了吗,怎么还在快照数组中答:在当前事务启动前就已经提交了,不在数组中,但是⽐低⽔位⾼。
你想下这种场景,按照时间顺序: 事务A启动 ,假设trx_id是1;
事务B启动,假设trx_id是2;
事务B提交;事务C启动,这时候C的低⽔位是1,因为A还没提交。所对于C的事务数组⾥是没 有2的,但是“2>低⽔位(也就是1)”。

439、⾼⽔位不是当前事务⾃⼰吗? 当前事务启动瞬间 它的线程号应该是最⼤的啊,“当前系统
⾥⾯已经创建过的事务 ID 的最⼤值加 1 记为⾼⽔”为什么不是?

答:事务启动时申请⾃⼰的trxid,但是创建视图列表的时候,是“稍微晚⼀点”的,这⾥有时间差,中间可能有别的事务也提交啦。

440、“在实现上, InnoDB 为每个事务构造了⼀个数组,⽤来保存这个事务启动瞬间,当前正在“活跃”的所有事务 ID。“活跃”指的就是,启动了但还没提交。”
“如果落在⻩⾊部分,那就包括两种情况
a.若 row trx_id 在数组中,表示这个版本是由还没提交的事务⽣成的,不可⻅;
b.若 row trx_id 不在数组中,表示这个版本是已经提交了的事务⽣成的,可⻅”
这两段话有点⽭盾,前⼀段定义了“活跃”事务是启动了但还没提交,后⾯⼀段⼜说不在数组中是已经提交了的事务⽣成,那么⻩⾊段是个区间还是活跃事务数组呢?从后⾯解释来理解是个区间。
答:这两个概念的关系是:视图数组是活跃事务ID的集合,这个集合⾥,最⼩的事务id记为
Min_id, 最⼤的事务id记为Max_id⻩⾊区间就是 [Min_id, Max_id] 这个区间。
PS:Min_id就是低⽔位。

441、undo log 有缓冲吗。像redo log那样。
答:Undo log的写⼊策略和数据是⼀样的(WAL)。

442、评论区有⼈说⾼⽔位就是当前trx_id, 我也是这样认为,看你回复的是可能⾼⽔位会略⼤⼀些,我想了很久,除了⽂章中提到的⾼⽔位是最⼤trx_id + 1之外,还是没想明⽩这个略⼤是出现在什么场景下,⽼师能给举个例⼦吗?
答:1. 事务A申请到trx_id
2.事务B启动,申请到 trx_id
3.事务C启动,申请到 trx_id
4.事务A创建视图数组

443、对于row trx_id, 当事务启动的时候,会⽣成保存所有活跃事务id的数组,以及⽣成⾼⽔位。按稳重⾼⽔位定义,当前系统⾥⾯已经创建过的事务 ID 的最⼤值加 1 记为⾼⽔位,⽽事务
id是按申请顺序严格递增的, 那是不是可以理解为 “⾼⽔位 = 当前启动事务id + 1” ? 答:不是,因为在计算⾼⽔位之前,可能别的事务也创建出来了。

444、原⽂:“若 row trx_id 不在数组中,表示这个版本是已经提交的事务⽣成,数据可⻅”

疑惑之处:既然是已提交的事务,该事务⼜怎么会处于⻩⾊区域呢?
答:事务A启动,创建事务A的视图;事务B启动,更新,提交;这时候在事务A的视图数组⾥
⾯,没有B,但是B已经提交了。

445、在RR级别下,事务获取到的⼀致性视图数组是静态的么,⾼⽔位是当前创建的最⼤事务
id+1,可否认为是当前事务的id+1(事务创建的时候,视图数组和⾼⽔位难道不是和事务创建的时候⼀起确定的么)?在RC级别下,事务获取的⼀致性视图是动态的,事务中可能出现多次 查询,期间会产⽣别的事务,所以⾼⽔位只能是当前创建的最⼤事务id+1⽽不能是当前事务
id+1。
答:RR和RC在⼀致性视图上的差别是:RR的视图是在事务启动的第⼀个语句创建的,之后事 务存续期间都不变;RC是在每个语句开始执⾏的时候,都创建⼀个视图,每个视图只管⾃⼰⼀ 个语句。

446、只读事物不分配trx_id,那么事务A为什么 事务Id为100啊?
答:只读事务不分配id,是5.6以后的优化;其实也不是不分配id,只是不分配⾃增的id,随机分配的那个也是事务id的。这⾥简化为同⼀个机制(等同于都是按照⽼版本来说),⽐较便于理解哈。

447、若 row trx_id 不在数组中,表示这个版本是已经提交了的事务⽣成的,可⻅。这句话不明⽩,能解释⼀下吗?
答:事务提交后,他的trx_id就会从数组中拿⾛。

448、主键id也是唯⼀索引吧? 那我们的新增操作如何利⽤ change buffer呢?
答:所以主键索引⽤不上,都是对于那些⼆级索引的才有效。 ⼀个insert语句要操作所有索引的嘛,收益在⼆级索引。

449、commit对change buffer的影响是什么?是仅仅改变redolog记录的提交状态吗?
答:好问题,是的。对于WAL 机制来说,change buffer就是数据的⼀种,在commit的时候处理机制是和数据⻚⼀样的

450、问题⼀:change buffer相当于推迟了更新操作,那对并发控制相关的是否有影响,⽐如加锁?我⼀直以为加锁需要把具体的数据⻚读到内存中来,才能加锁,然⽽并不是?

问题⼆:purge⾏为之后应该不会再产⽣redo log了吧?从应⽤开发的⻆度看,还是由数据库保证唯⼀好。
答:1. 锁是⼀个单独的数据结构,如果数据⻚上有锁,change buffer 在判断“是否能⽤”的时候,就会认为否
2. 是这样的,这个问题你分成两步来考虑。第⼀步,merge其实是从磁盘读数据⻚到内存,然后应⽤,这⼀步都是更新的内存,同时写redolog。
现在内存变成脏⻚了,跟磁盘数据不⼀样。之后就⾛刷脏⻚的流程。刷脏⻚也不⽤写。是否使⽤唯⼀索引,这个要看业务有没有保证,和性能是否有问题。

451、之前第⼆章说redo-log是优先记录的,想问下既然redo-log也记录了charge-buffer的操作,为什么还需要写⼊系统表空间?
答:内存不够的时候会淘汰的(还是跟数据的机制⼀样)。

452 、 INSERT INTO … ON DUPLICATE KEY UPDATE TID = 2,…
能详情解释下这个SQL的执⾏过程吗?
答:插⼊数据,⽆关没有主键冲突就插⼊,如果有,就对冲突的第⼀⾏修改它的TID值。

453、业务要求数据库中某个字段是唯⼀的,当请求并发的时候,除了在数据库层吗设置成唯⼀ 索引,还有别的办法吗?
答:要看你的唯⼀主键怎么来的。⽐如有⼀个唯⼀主键的全局产⽣器。

454、⽼师回复:“其实是恢复change huffer的内容,但是不怕丢失,因为change buffer也确保不会丢哈”这个我明⽩,因为redo控制它不会丢失,但是change buffer 进⾏purge合并的时候不会记录redo了,这个时候如果形成的脏数据还没落盘然后就异常宕机了,redo恢复的应该还是change buffer没合并之前的数据吧,应该不会直接恢复成合并后的脏数据吧
答:进⾏merge合并的时候会记redolog的,这样如果之后异常重启了就会通过崩溃恢复,恢复 回来。

455、正常运⾏中的实例,数据写⼊后的最终落盘,是从redo log更新过去还是从buffer pool 呢?
答:Buffer pool, 其实redo log并没有直接写磁盘数据⻚的能⼒,因为它没有整⻚的数据。

456、数据更新时,写⼊change buffer,⽴⻢读取这⼀条 不是应该直接从内存⻚读取吗,有必要把整个⻚读⼊内存吗,⽽且innoDB怎么知道应该加载哪⼀⻚到内存,我这⼀条记录并没有在数据⻚上有任何标示位啊。
答:1. 就是数据⻚没在内存,才能⽤上change buffer。
2. B+树有序的。能找到(你想,数据库⼀开始启动的时候,要找⼀个磁盘上的记录是怎么找到,⼀样的过程)。

457、前提:
1.order表包含Id,orderId,channelId三个字段,其中id为⾃增主键,orderId和channelId为联 合⾮唯⼀索引(idx_order_channel),
事务隔离级别为Repeatable Read 2.表中包含三条数据:
id OrderId channelId

1678 JY005 1005
1679 JY010 1047
1680 JY015 1023
3.由于idx_order_channel为⾮唯⼀索引,下单时需要按照orderId_channelId删除数据,再插⼊数据。
现在有两个同时下单的请求,执⾏的事务如下:
事 务 1 : begin;delete from order where orderId = ‘JY007’ and channelId = 1007;insert… (‘JY007’,1007);commit;
事 务 2 : begin;delete from order where orderId = ‘JY008’ and channelId = 1025;insert… (‘JY008’,1025);commit;
请问这种情况会发⽣死锁吗?会有抢夺锁的情况发⽣不? 答:会死锁,有间隙锁gap lock冲突。

58、系统表空间跟数据表空间这两个概念各是什么意思?
答:系统表空间就是⽤来放系统信息的,⽐如数据字典什么的,对应的磁盘⽂件是ibdata1, 数据表空间就是⼀个个的表数据⽂件,对应的磁盘⽂件就是 表名.ibd。

459、不知道理解的对不对,⽐如update t set name=xxx where k=xxx,其中k是⼆级索引,如果是以下⼏种情况:

1.对于k是⼆级普通索引的情况,如果索引的数据⻚不在内存中,则写⼊到change buffer中,后续某些条件下(⽐如查找),会将该索引数据⻚读⼊内存,然后再回表更新记录的数据⻚中的name字段为xxx?
2.对于k是⼆级普通索引的情况,如果索引的数据⻚在内存中,然后回表更新记录数据⻚中的name 字段为xxx?
3.对于k是⼆级唯⼀索引,如果索引的数据⻚不在内存中,将该索引数据⻚读⼊内存,然后回表更新 记录数据⻚中的name为xxx?
答:如果⽤where⾥⾯⽤k索引的话,还是要把这⼀⻚读出来的,所以k这个⻚⽤不上change buffer , 但是如果name 也建了普通索引, 那么更新name 值这个⾏为可能被放⼊change
buffer。

460、在pool⾥做更新,然后merge到磁盘,那么⾏锁是什么时候加上的? 答:更新之前加的了,⾏锁没有存在数据⻚⾥哦。

461、关于change buffer 主要节省的则是随机读磁盘的 IO消耗这个点,我的理解是如果没有change buffer机制,那么在执⾏更新后(写⼊redolog),读取数据的时候需要从次磁盘随机读取redolog合并到数据中,主要减少的是这部分消耗?
答:不是,如果没有change buffer, 执⾏更新的“当时那⼀刻”,就要求从磁盘把数据⻚读出来
(这个操作是随机读)。Chsnge buffer省了这个。

462、可不可以这样理解,change buffer在merge时是将内存中的数据更新到最新,⽽redo log 是将磁盘中的数据更新到最新?在内存中的数据失效之前,redo log是不是必须要将数据写盘, 有这样⼀种机制来保障吗?
答:第⼀个理解很对,第⼆个,内存中的数据淘汰之前,需要写盘。这⾥两个说明:
1)内存⽆所谓失效,就是被LRU淘汰了,数据⻚才会移出内存
2)这时候不需要redolog做什么的,内存写到磁盘,直接就写了

463、随机io写,和顺序写有什么区别呢?为什么顺序写更加⾼效呢?⽽且这⾥的随机,顺序, 指的是什么呢?
答:顺序写⽐随机写⾼效,是磁盘的机制决定的。顺序就是下⼀次写从上⼀次结束的位置继续。

464、如果更新后⻢上就要查询,使⽤change buffer就有点得不偿失,那这个更新后多⻓时间查询,change buffer才能真正提升效率呢?有个⼤概的范围吗?或者跟哪些因素有关?
答:读写⽐例吧。⼀般是⼆级索引多的、主要是insert数据场景的库,⽤change buffer效果好。

465、change buffer记录的是新增或修改的数据,那么merge操作是针对⼆级索引还是数据。如果说因为唯⼀索引需要确保数据唯⼀,那么普通索引应当也是需要的,毕竟插⼊操作避免不了要带去主键(或系统⽣成)。
答:普通索引不需要哦,就是说插⼊的时候,操作主键ID索引的变更确实不能⽤change buffer, 但是普通索引可以。

466、 关于主键索引和⼆级索引数据同步的问题不太明⽩:
id = 1, k = 5; id = 2, k = 5
执⾏delete from T where k = 5;
假设k对应的page⻚不在change_buffer中, 这时候会在change_buffer中记录 delete from T where k = 5;
1、主键索引是本来就不能⽤上change buffer机制的,那么这时候是直接在主键对应的B+tree 上删除 id in (1, 2)的数据? (随即写?)
2、如果执⾏delete from T where id = 1; 这时候会⽤到change_buffer, 在change_buffer中做标记delete from page2 where id = 1 ? 类似这样吗? 如果这样 会⾸先把id=1的page⻚读到内存中吧(否则不知道删除的k = ?), page2 这个是怎么知道的? 会去K对应的B-Tree中找吗?
答:1. 是的,但是不是in(1,2), 都是⼀个个回表的删除的
2. 你说的Page2 是放普通索引的page吗?流程是这样,id=1主键索引取出来,读到k值,再去k 索引树上删。如果发现page2 在内存就直接删除。如果发现不在,就往chang buffer中记删除动作。还是要在k索引找的,不找不知道在不在内存⾥。

467、普通索引的更新记录会放到change buffer中,这个过程也会存到redo log ,redo log会怎么处理这条记录?在往磁盘更新的时候忽略掉吗?
答:这条记录如果没有碰上崩溃恢复啥的,就看chepoint推进到它的时候,有没有写了盘,如果没有,就把change buffer 内容写盘(其实跟数据是⼀样的策略)。

468、场景如下:

假设表t有id,k 两列。id是主键,k是⼆级索引。
有数据(3,300),(4,400)。(3,300)存放在数据块page3,(4,400)存放在数据块page4。这两⾏数 据均不在buffer pool中。
现在做update操作,修改(3,300)为(3,400),这时会把修改操作写⼊到到change buffer中; 并在change buffer merge之前做查询操作:select k from t where k=400;
因为数据不在buffer pool中,会到磁盘上读取数据,根据索引会读取到page4,并把page4数据加载到buffer pool中,然后应⽤change buffer时,发现遗漏了page3,再重新去取page3。
请问这个过程的推导是否正确,是否会出现这种多次IO的情况
答:按照你的描述,应该是假设“做update操作,修改(3,300)为(3,400)” 这个操作,是要在
page3删除这个记录,然后往page4插⼊⼀⾏对吧?
这两个操作都可能被放到change buffer⾥⾯。接下来如果查询是 where k=400, 就只会访问page 4,page 4从磁盘读⼊内存的时候,做merge操作,返回结果⾥⾯有两条满⾜k=400的记录,就完成了。这个查询过程跟page3没关系,不会发现“page 3遗漏”这个逻辑哦。

469、添加数据时主键问题。 看您在评论区中回复: insert的时候, 写主键是肯定不能⽤change buffer了。 那请问什么情况下insert能⽤到?因为表中,基本都有ID主键,那insert语句是不是没有⽤上change buffer的情况了。
答: ⼀个insert语句会更新主键索引和其他普通索引,更新普通索引的时候,就有可能可以⽤上change buffer了。

470、针对同⼀个字段,建了唯⼀索引,还需要建普通索引吗?
答:尝试对同⼀个字段建了唯⼀索引和普通索引,跑SQL时发现两个索引都⽤上了,不是两个索 引都⽤上了,是只⽤了⼀个,如果建了唯⼀索引,就不⽤再同样的建⼀个普通索引了。

471、由于mvcc机制,更新数据需要记录undo log,此时应该把要更新的数据⻚读⼊buffer⾥, 如果记了change log 不读内存,之前还未提交的事务如何能拿到需要的数据呢?
答:如果能⽤上change buffer, 那么就不更新这个数据⻚,不需要记这个⻚的undo 了。

472、不是太明⽩读数据时是怎么判断当前要读的数据是否在change buffer中存在待merge的数据?
答:有个hash表,读出来的时候顺⼿判断。

473、“更新⼀个数据⻚时,在不影响数据⼀致性的前提下,innodb会将这些更新操作缓存在change buffer中。”请问具体有哪些操作呢?我想了⼀个场景,⾮唯⼀索引的更新操作,需要判断该⾏数据是否存在,这样还是需要将磁盘中的数据⻚读取到内存中呢。
答:不⽤判断,这⼀⾏⼀定存在,(除⾮出bug了)。

474、在没有索引的情况下,cahnge buffer会起作⽤吗?我觉得是会。答:不会没有索引意味着就只有⼀个主键索引。

475、数据读⼊内存是需要占⽤ buffer pool 的,所以这种⽅式还能够避免占⽤内存,提⾼内存利⽤率。后⾯⼜说change是占⽤pool内存的,那到底占不占⽤buffer pool的内存?
答:1. change buffer本身是占⽤内存的;
2. 但是chage buffer本身只是记录了“更新过程”,远远⽐数据⻚(⼀个16k)⼩。相⽐于把数据
⻚读⼊内存,这个⽅式还是省了内存的。

476、什么是数据表空间和系统表空间?
答:⽐如有⼀个表a,那么a.ibd 就是数据表空间;“表a的表结构”这个信息存在ibdata1的系统表空间⾥。

477、update操作不是先读后写吗?如果是先读的话,不是应该把数据已经读到内存了吗?那这样的话直接更新内存不就好了,为什么还要写change buffer?
答:change buffer就是为了避免先读后写,因为读有随机io的消耗。

478、⽂中讲change buffer中存的内容是“在某个数据⻚更新什么”,但是在update/insert时, 确定这条记录更新/插⼊在哪个数据⻚,不也是有⼀个查找的过程么?(肯定有⼀个⼀层层查找 的过程,会路过很多数据⻚啊)为了确定在哪个数据⻚操作⽽遍历过的数据⻚也会读进内存作缓存吗?
答:是的,查找过程避不开,但是⼆级索引树的⾮叶⼦节点没多少,主要在磁盘上的还是叶⼦节点。

479、【对于唯⼀索引来说,需要将数据⻚读⼊内存,判断到没有冲突,插⼊这个值,语句执⾏ 结束】那么这⾥是只读【索引⻚】呢,还是会连带【数据⻚】⼀起读⼊内存?
答:如果是判断唯⼀索引的,就只读【索引⻚】就可以的,

但是因为primary key也有唯⼀约束,所以这个”索引“也需要判断冲突。⽽主键索引上其实就是数据了。

480、建了唯⼀索引 changebuffer不会⽣效.然后要查询的数据也不在内存中 这时候 是不是就要合并redolog的数据 才能得到正确的结果了(假设这时候redolog还没刷盘)
答:这两个概念有点混淆了哦。如果数据没有刷盘,就⼀定会在内存中,查询过程不会需要去读
redolog的(否则这个速度就太慢了)。

481 、我⽤存储过程插⼊100000 条数据特别慢, 后来我set autocommit=0, 每1000 条才
commit,这样就快了。我想不出来这是为什么。
答:Redo log 和 binlog刷盘次数少了,你把100000个事务变成了100个事务。

482、redo log 是实时写⼊磁盘的吗?是不是还有⼀层所谓的“redo log buffer”?
答:Redolog buffer是在事务执⾏过程中,先把要写⼊的内容在内存⾥存起来,在commit阶段,⼀次性写⼊redolog file

483、我的理解是由于B是查找(50000,100000),由于B+树有序,通过⼆分查找找到b=50000的 值,从50000往右扫描,⼀条⼀条回表查数据,在执⾏器上做where a(1,1000)的筛选,然后做判断是否够不够limit的数,够就结束循环。由于这⾥b(50000,100000)必然不存在a(1,1000),所以需要 扫描5W⾏左右.但是如果把a改为(50001,51000),扫描⾏数没有变。那么是因为优化器给的扫描
⾏数有问题还是执⾏器没有结束循环?为什么不结束循环?
(好像rows能直观展示limit起作⽤,必须在执⾏器上过滤数据,不能在索引上过滤数据,不知道为什么这样设计)。
答:是的,加了limit 1 能减少扫描多少⾏,其实优化器也不确定,【得执⾏才知道】,所以显示的时候还是按照“最多可能扫多少⾏”来显示。

484、对于枚举属性(像:类型、标记这种只有少量值的字段)字段,按照⽂中的意思这样的字 段建⽴索引的话区分度很低,基数也很⼩。那适合在这样的字段上建索引吗?我能想到的不合适的理由是:⾏级锁的范围会很⼤。
答:这是⼀个原因,还有就是区分度不好。业务上要尽量避免只有这⼀个条件的语句(如果实在有,那也没办法还是要建)。

485、假如要查 A in () AND B in (), 怎么建索引? 答:where A in (a,b,c) AND B in (x,y,z)会转成
(A=a and B=x) or (A=a and B=y) or (A=a and B=z) or (A=b and B=x) or (A=b and B=y) or (A=b and B=z) or (A=c and B=x) or (A=c and B=y) or (A=c and B=z)

486、下⾯这条SQL order_id 和 user_id 都是 int 类型,都加了索引,在看 EXPLAIN 的时候执⾏只使⽤了 order_id 索引, 为什么 user_id 索引没有采⽤呢?如果WHERE 条件后⾯都有索引是否都会执⾏、还是优化器会选择最有效率都⼀个索引执⾏? 将两个调整成组合索引也没有效果, 如果 force index(user_id) 则全表扫描。
SELECT count(1) FROM A a WHERE EXISTS(SELECT 1 FROM B b WHERE
b.order_id = a.order_id AND b.user_id = a.user_id);
答:只能⽤⼀个索引,如果两个都⽤就是merge-index算法,⼀般优化器很少采⽤。

487、在评估前缀索引⻓度的时候,如果是新表的话,表中还没有数据,这个时候没法估计区分 度,只能按照业务先去设计,后续再优化吗?还有其他什么评估办法吗?
答:跟业务负责⼈确定数据分布和查询模式。其实这个不⽌是评估前缀索引⻓度时需要,整个表的索引该怎么设计,都是应该事先根据数据分布和查询来决定的。

488、char 和 varchar 可以设置⻓度,这个⻓度是⼲什么的,对于不同字符集⼜有什么影响? 答:char(N)表示“最⻓存N,但是如果字符串⼩于N,⽤空格补到N”,
varchar(N)表示“最⻓存N,如果字符串⼩于N,按照实际⻓度来存”。

489、“内存不够⽤了,要先将脏⻚写到磁盘“和“redo log 写满了,要 flush 脏⻚”可以理解为⼀个脏⻚本身占⽤内存,释放内存需要将脏⻚写⼊到磁盘才能释放。⽽redo log写满只有当redo
log对应的脏⻚flush到磁盘上才能释放对应空间。有⼏个问题:
1、“内存不够⽤了,要先将脏⻚写到磁盘“redo log对应的空间会释放嘛?“redo log 写满了, 要 flush 脏⻚”对应的内存⻚会释放嘛?
2、将脏⻚flush到磁盘上是直接将脏⻚数据覆盖到对应磁盘上的数据?还是从磁盘上取到数据后取根据redo log记录进⾏更新后再写⼊到磁盘?
3、redo log是怎么记录对应脏⻚是否已经flush了?如果断电了重启导致内存丢失,前⾯⼏章说通过redo log进⾏数据恢复那redo log⼜怎么去释放空间?

答:1. Redolog 的空间是循环使⽤的,⽆所谓释放。 对应的内存⻚会变成⼲净⻚。但是等淘汰的时候才会逐出内存;
2.好问题,前者;
3.不⽤记,重启了就从checkpoint 的位置往后扫。 如果已经之前刷过盘的, 不会重复应⽤redi
log。

490、当内存不够⽤了,要将脏⻚写到磁盘,会有⼀个数据⻚淘汰机制(最久不使⽤),假设淘 汰的是脏⻚,则此时脏⻚所对应的redo log的位置是随机的,当有多个不同的脏⻚需要刷,则对应的redo log可能在不同的位置,这样就需要把redo log的多个不同位置刷掉,这样对于redo
log的处理不是就会很麻烦吗?(合并间隙,移动位置?)
另外,redo log的优势在于将磁盘随机写转换成了顺序写,如果需要将redo log的不同部分刷掉
(刷脏⻚),不是就在redo log⾥随机读写了么?
答:其实由于淘汰的时候,刷脏⻚过程不⽤动redo log⽂件的。这个有个额外的保证,是redo
log在“重放”的时候,如果⼀个数据⻚已经是刷过的,会识别出来并跳过。

491、我理解 redolog 和 undolog 是在有数据更新时同时写的,redolog 作为 crash-safe 机制,每次全部读⼊做恢复就好。但是像在做脏⻚flush 或者 mvcc版本回退的时候,难道是按照涉及的数据⾏来定位⽇志⾏的吗?如果是,是怎么做到这种“定位”的呢?如果不是,InnoDB 的机制⼜是怎样的?
答:Flush 和 mvcc不能放⼀起说哈,不⼀样的。每⼀⾏存了⼀个位置可以直接找到undo log, 这个MVCC的基础,Redolog⾥⾯记录了“这是哪个⻚⾯的修改”。

492、innodb是如何知道⼀个⻚是不是脏⻚的,是有标记位还是通过redolog的ckeckpoint来确定的?
答:每个数据⻚头部有LSN,8字节,每次修改都会变⼤。对⽐这个LSN跟checkpoint 的LSN,
⽐checkpoint⼩的⼀定是⼲净⻚。

493、您提到通过对⽐数据⻚的 LSN 和 checkpoint 的 LSN,⽐checkpoint LSN ⼩的就是⼲净⻚,那在重放 redo log 时是如何跳过那些由于内存不⾜⽽刷脏的⻚⾯的呢?刷脏的时候会将
LSN 重置为0之类的?
答:判断⼀下发现是⼲净⻚就跳过。不是重置为0,是把磁盘上数据⻚的LSN也⼀起改掉了。

494、在⼀个事务回滚操作⾥如果有DDL操作会怎样。例如如下步骤:1.开始事务及设置⼿动提 交;2.修改表Aid为2的⾏;3.动态创建⼀张表B;4.修改表C⾥id为5的⾏;6.提交事务及回滚代 码编写。如果在步骤5时发⽣异常数据库会怎样做?
答:第三步会⾃动提交事务的。如果你是autocommit=1的话,其实4也是单独事务提交了。

495、系统内存不够 和 缓冲池内存的关系是 ? 缓冲池可以无限扩大内存将系统内存沾满么? 答:不能扩大,buffer pool size 设定好就只能用这么多,不会再多吃系统内存。

496、⽤fio测了⼀下,iops 在1500左右,MySQL的innodb_io_capacity的缺省值为200,但还有⼀个参数是innodb_io_capacity_max的参数,值是2000。这种情况下,系统算正常吗?max 参数是怎么算出来的?
答: Max 只是⽤来控制 innodb_io_capacity 的值不能设置超过这个值, 真正⽣效的还是innodb_io_capacity。

497、访问某条记录时,存储引擎是如何判断这条记录所在的数据⻚是否在内存当中,这个查内 存机制是如何实现的?
答:每个⻚⾯有编号的。拿着编号去内存看,没有,就去磁盘。

498、WAL 将随机写转换成顺序写,这⾥顺序写是指 redo log 的顺序写,还是还会把多个脏⻚调整成顺序写。如果是会把脏⻚调整成顺序写,这个是操作系统本来就有的功能,还是 MySQL 实现的。(操作系统学得⼀般:()
答:这两个顺序写都是WAL机制的收益,刷脏⻚是Innodb 实现的,没有依赖操作系统。

499、我们需要考虑的是内存脏⻚flush 速率,但是如何避免出现擦⿊板,对应的是数据库的check point 移动造成的整个系统⽆法更新呢?
答:就是io_capacit不要设置太⼩。如果是磁盘性能问题导致checkpoinnt推进太慢,就得换磁盘了。

500、每个数据⻚上都有Lsn,数据库运⾏时数据⻚上的lsn应该是千差万别的吧?若是数据库正 常关闭,然后重新启动,这是所有数据⻚的lsn是⼀致的?还是保持数据库关闭时千差万别的? 答:每个数据⻚的lsn不同的,表示“更新这个page的最后⼀个lab”, 不需要相同。

MySQL 精选问答 500 题相关推荐

  1. 数据库系统原理与应用教程(077)—— MySQL 练习题:操作题 168-172(二十一):综合练习

    数据库系统原理与应用教程(077)-- MySQL 练习题:操作题 168-172(二十一):综合练习 168.分组统计(1) 该题目使用的表和数据如下: /* DROP TABLE IF EXIST ...

  2. Leetcode 500题AC的刷题总结(C与C++)

    文章目录 前引闲聊 500AC达成截图 + 力扣刷题每日频率 前引闲聊 哈哈 又到了我的闲聊时间了 与其这篇说是500题AC的总结 不如说是我的闲聊时间 我记得上一篇关于Leetcode AC记录 还 ...

  3. 数据库系统原理与应用教程(070)—— MySQL 练习题:操作题 101-109(十四):查询条件练习

    数据库系统原理与应用教程(070)-- MySQL 练习题:操作题 101-109(十四):查询条件练习 101.判断空值(1) 试卷答题记录表:exam_record(uid:用户ID,exam_i ...

  4. 数据库系统原理与应用教程(074)—— MySQL 练习题:操作题 141-150(十八):综合练习

    数据库系统原理与应用教程(074)-- MySQL 练习题:操作题 141-150(十八):综合练习 141.求名次(1) 该题目使用的表和数据如下: /* drop table if exists ...

  5. 腾讯精选练习 50 题(Leetcode)笔记 PDF下载!

    昨天在知识星球中立了一个Flag,第一步采取的行动就是把以前刷的"腾讯精选练习 50 题"重新梳理一下,就有了今天这本170多页的小册子. 这本小册子即可以作为学习数据结构与算法课 ...

  6. js逻辑训练题_二建冲刺必刷300题!精选历年真题+母子题+模考易错题!

    订阅公众号,回复[口诀],获取完整版实务口诀 你是不是常常疑惑为什么同样在做题,同样熬通宵,同样很努力,为什么有人顺利拿证,有人却因几分之差黯然落榜? 因为二建不仅拼努力的程度,更要拼对精准二建信息的 ...

  7. 2019网络教育计算机统考模拟试题,最新2019年网络远程教育《计算机应用基础》统考模拟题库500题(含答案)...

    2019年网络远程教育统考<计算机应用基础>考试 题库500题[含答案] 一.选择题 1.启动ExCEl2003应用程序后自动建立的工作簿文件的文件名为_______. A.工作簿 B.工 ...

  8. CSDN问答——精选问答Vol.7

    <精选问答>挑选CSDN问答频道每周最新最热的优质回答,为大家提供可信赖的优质解答,点击查看更多已解决问题 目录 1.想把javaScript变量传递asp页面 存入数据库获取值 不是空 ...

  9. 脱式计算机在线使用,三年级数学脱式计算500题可直接打印

    文件大小:   资料格式:  下载:198次 资料等级:     所需点数:0 课件类型:/word 注册本站会员,享受高速下载,立即注册会员 三年级数学脱式计算500题可直接打印部分内容预览 25+ ...

最新文章

  1. Apache Thrift - java开发详解
  2. 蓝桥杯利用字母可以组成一些美丽的图形,
  3. 安装Windows Nano Server虚拟机
  4. 关于 Blazor Server Side 的一些杂项, 感想
  5. docker -v -it -p 详细说明
  6. Flink 集群搭建安装 CentOS 7.x 版本
  7. linux bash -,linux bash 总结
  8. php朋友圈上传多个图片不显示不出来,求教!文章分享到微信朋友圈,链接的缩略图不显示怎么解决?...
  9. Android事件总线(四)源码解析otto
  10. Q91:真实地模拟透明材质(Realistic Transparency)(3)——A Glass of Water
  11. Cocos数据篇[3.4](3) ——XML数据操作
  12. 北京交通大学第六届新生程序设计竞赛题解
  13. DataWorks调度配置最佳实战
  14. meaven插件通过绑定生命周期_容器镜像服务联手 IDE 插件,实现一键部署、持续集成与交付...
  15. ASP.NET页面间的传值方法(2)
  16. ESP8285烧写问题备忘
  17. 统计学常用知识-Pearson相关系数-显著检验-置信区间
  18. Matlab中图形对象属性gcf使用
  19. 【Linux学习】实现石头剪刀布游戏
  20. 互融云工业品电商系统上线:科技助力互联网与实体经济深度融合

热门文章

  1. proguard 配置简介
  2. VC++开发垃圾文件清理软件(下)
  3. 端口号被占用的解决办法
  4. 2011年最差的CES(消费电子展)
  5. 微信小程序实战练习(仿五洲到家微信版)
  6. 猿团君分析-程序员如何成功的提高影响力2.0
  7. STL源码剖析-第一章STL概论与版本简介
  8. Oracle 小数点特殊处理
  9. SDUTOJ(2109)找女朋友
  10. linux实现数据完整性检查工具 tripwire