15 | 答疑文章(一):日志和索引相关问题
15 | 答疑文章(一):日志和索引相关问题
讲述:林晓斌
时长20:20大小18.57M
日志相关问题
追问 1:MySQL 怎么知道 binlog 是完整的?
追问 2:redo log 和 binlog 是怎么关联起来的?
追问 3:处于 prepare 阶段的 redo log 加上完整 binlog,重启就能恢复,MySQL 为什么要这么设计?
追问 4:如果这样的话,为什么还要两阶段提交呢?干脆先 redo log 写完,再写 binlog。崩溃恢复的时候,必须得两个日志都完整才可以。是不是一样的逻辑?
追问 5:不引入两个日志,也就没有两阶段提交的必要了。只用 binlog 来支持崩溃恢复,又能支持归档,不就可以了?
追问 6:那能不能反过来,只用 redo log,不要 binlog?
追问 7:redo log 一般设置多大?
追问 8:正常运行中的实例,数据写入后的最终落盘,是从 redo log 更新过来的还是从 buffer pool 更新过来的呢?
追问 9:redo log buffer 是什么?是先修改内存,还是先写 redo log 文件?
业务设计问题
小结
上期问题时间
赞 110
提建议
精选留言(257)
- 萤火虫置顶2018-12-17林老师的每次更新我都会跟着看 跟着学 已经坚持15节课了 受益良多 只是心里有时会反问自己 底层原理有那么重要吗? 会用不就行了吗? 自己不知道该怎么推翻这些想法 加上自己有个不好的习惯 就是容易放弃 希望自己能够坚持到最后。
作者回复: 加油。 说下我自己的理解。 我在带新人的时候,要求大家在写SQL语句的时候,心里是有数的,知道每个语句执行的结果,以及这些代码会消耗什么资源、如果慢了会慢在哪里、每个语句执行会占用哪些锁等等。 有的新人会问“为什么需要这么麻烦,我执行一下,看看结果对不对,对了就行,不对就改,是不是也可以?” 我说不可以。因为如果这样,我们就会受到很多局限,即使我们定位自己是业务开发人员。 这里我说一个限制: 这会限制基于数据库的业务架构能力。一个语句可以试,一个五个语句的事务分析就要试很多次,一个复杂业务系统的数据库设计,是试不出来的。 原理可以帮我们剪枝,排除掉那些理论上明显错误的方案,这样才有精力真的去试那些有限的、可能正确的方案。 我们不需要100%精通MySQL(我自己离这个目标也相去甚远),但是只要多知道一些原理,就能多剪一些枝,架构设计就能少一些错误选项的干扰,设计出来的项目架构正确的可能性更高。 我自己特别喜欢这个剪枝的过程和感觉,他表示我用以前学习的时间,来节省了现在工作的时间。 当然,“原理”是一个很大的概念,有的原理更接近实战,有的远一些。这个专栏我挑的是跟平时使用相关的原理,以便大家可以有机会边学边用。 一起加油吧🤝
共 16 条评论516 - 某、人置顶2018-12-17孔乙己来到酒馆大喊一声老板来二两酒赊着,酒馆生意太好,老板把孔乙己的欠账记录记到小黑板上并记录了孔乙己点的菜单。孔乙己跟别人吹了会牛,忘了叫的几两酒了。又给老板说,老板把酒改成二两。老板也不确定孔乙己叫没叫酒,就去查菜单,发现孔乙己确实点了酒,但是本来就二两,也就难得麻烦了,又要修改小黑板,又要改菜单。直接就给孔乙己说已经改好了。😄
作者回复: 老板看完板,正要告知孔乙己今日总账是赊账二两酒, 小二连忙过来拦住,“老板,刚刚孔乙己刚又赊账了一碟茴香豆。” 老板大惊,“差点亏了我一碟豆子!我怎不知?” 小二道,“老板你方才看板的之时没拿记账笔,我看记账笔没人使用,按店规自然可用。老板你自己没看” 老板惊呼,“亏的你小心”。 暗地想店规确有不妥。 于是把店规“变账须用记账笔。” 改为 “改帐均须动笔。纵为不变之帐,仍需覆写之” 😄
共 10 条评论502 - Gavin置顶2018-12-17课后问题: 在命令行先执行以下命令(注意不要提交事务): BEGIN; UPDATE t SET a=2 WHERE id=1; 新建一个命令行终端,执行以下命令: UPDATE t SET a=2 WHERE id=1; 从新建的命令行终端的执行结果看,这条更新语句被阻塞了,如果时间足够的话(InnoDB行锁默认等待时间是50秒),还会报锁等待超时的错误。 综上,MySQL应该是采用第3种方式处理题述场景。 对于MySQL为什么采用这种方式,我们可以利用《08 | 事务到底是隔离的还是不隔离的?》图5的更新逻辑图来解释:假设事务C更新后a的值就是2,而事务B执行再执行UPDATE t SET a=2 WHERE id=1;时不按第3种方式处理,即不加锁不更新,那么在事务B中接下来查询a的值将还是1,因为对事务B来说,trx_id为102版本的数据是不可见的,这就违反了“当前读的规则”。 以上是我的理解与分析,不是很确定准确与否。展开
作者回复: 漂亮
共 16 条评论229 - null置顶2018-12-18看到自己的问题上榜,这是对自己的最大鼓励。 学习专栏之前,自己只是一个 CRUD boy,平时同事间讨论 MySQL 的问题,自己完全搭不上话,因为对 MySQL 底层原理完全不懂。对 MySQL 的认知就仅限一点:索引能提高查询效率。但是为什么能提高?不知道!! 现在回想,以前犯过很多错误: 1. 主键使用 UUID,非自增主键。 2. 滥用索引,其实可以通过“最左前缀原则”来精减索引。 3. 不管 SQL 语句是否合理,只要能返回结果集就是好 SQL。 4. 建表时字段类型拿捏不准。 现在都会反复学习专栏的每一篇文章,每次学习都有不一样的收获。 第一次可能是:喔,原来有这么个知识点,但对它的实现原理一知半解。 第二次却是:对它的实现原理有了更深的认识,加强对知识的理解,基本会形成一个比较清晰的逻辑。 第三次是,MySQL 的这种实现原理,是为了解决什么问题等等。 现在感觉有点“走火入魔”了,以前执行查询语句,关注的多久能返回结果集。 现在关注的却是:连接器、分析器、优化器、执行器和 InnoDB 引擎。 连接成功后,获取我的权限,查询缓存,命中缓存直接返回,否则进行后续的操作。(记得老师留言区回复过:连接器取权限,执行器用权限。而编写留言到这产生了一个疑问:查询缓存前,应该会校验权限,所以连接器也会用权限?) 分析器阶段进行词法分析,解析关键字,字段名,表名等。语法分析判断语法是否正确。(记得第一篇《基础架构》留言提到语义分析,今晚要找资料学习下)。 优化器阶段生成执行计划,选择索引(这时会怀疑 MySQL 选择的索引是否最优),能否使用索引下推和覆盖索引减少回表查询,提高性能。 执行器阶段调用引擎接口查询数据,Server 层要啥,引擎给啥,InnoDB 只给必要的值。 查询结束后,返回结果集,并将结果集放入查询缓存。 更新语句的关注点是隔离性,视图,MVCC,回滚日志,redo log,binlog,两阶段提交等。 写业务代码时,会考虑事务内的 SQL 语句,能否调整 SQL 语句的顺序,减少更新表时行锁对性能的影响。 在建表的时,会反复推敲这个索引是否合理。使用普通索引还是唯一索引更为合适。能否通过“最左前缀原则”来减少创建索引的个数。如果索引字段的类型是字符串并长度太长,如何优化使用前缀索引,减少空间占用,提高查询性能。 学习专栏后,基本上涉及到 MySQL 的内容,这些知识点都会浮现在脑海中。昨天还差点应用这些知识,帮同事优化他的 SQL 语句。昨天跟往常一样,当写代码写累了,就跑到同事那溜达溜达。 他正在线上的备库测试查询百万数据要多久,另一位同事建议他使用 force index 强制索引,这次执行 5 秒,再执行零点几秒。 他惊乎,为啥这次这么快。我说,这次查了缓存。我还想帮他看看 SQL 语句,是否 MySQL 选择错了索引,导致使用 force index 显式指定索引。说不定使用 order by field 就解决了呢,哈哈哈哈。后面有事,没有继续跟进他这问题了。 非常感恩,跟着老师学习,让我体会到了学习是一件自然而又充满魅力的事情,也让我从一个基础不牢固的小白,一步步地充实了自己的知识库,另外老师非常尽责,经常半夜回复答疑,希望老师保重身体。谢谢!!展开
作者回复: “我说,这次查了缓存” 哈哈,这个场景好棒,这个画面感,有一种扫地僧的感觉👍🏿 一起加油
共 11 条评论162 - 力挽狂澜爆炸输出的臭...置顶2018-12-20针对不能只用binlog完成数据恢复我的理解: 按照文中这个话题下的示例,因为MySQL写数据是写在内存里的,不保证落盘,所以commit1的数据也可能丢失;但是恢复只恢复binlog失败的也就是commit2的数据,所以数据会丢失。 这样理解对吗?
作者回复: 是的,binlog一来时机控制不好(就是你说的这个),二来内容的能力不足(没有页面信息) 👍🏿
共 9 条评论31 - 于海2018-12-17在极客时间也学了不少课程,林老师是这其中最认真负责的,好的课程是用“心”写出来的
作者回复: 谢谢🙏 希望大家都有收获
64 - 陈新仁2018-12-17【操作符“|”是逻辑或,连同最后一句insert语句里...】 老师,“|” 这应该叫位运算符的按位或操作符,逻辑或是“||”吧? 这里的幂等性原理就是:A < B: relation_ship = 2 | 1; A > B:relation_ship = 1 | 2;重复插入 3 | 1 或者 3 | 2 。位运算: 2 | 1 == 1 | 2 == 3 | 1 == 3 | 2 == 3。感觉这里想法很巧妙。
作者回复: 你说得对,是按位或,看得很细致👍🏿 我发个堪误
共 9 条评论53 - Eric2018-12-17老师,您实在是太良心了。整理这些问题应该很费时间吧。看完答疑之后感觉又加深了一遍印象。像很多知识点都需要反复理解才能真正掌握。答疑来的很及时,感谢!44
- WL2018-12-22关于刷脏页有两个问题请教老师: 1. 当redo log空间不足时, 按照redo log的顺序把脏页更新到磁盘, 那么假如一个脏页在第1条redo log中已经被持久化到磁盘, 后面第1000条redo log又有这个关于这个脏页的信息, 那么innoDB是直接丢弃掉这条redo log的记录吗? 还有这个时候, 是要把redo log上的全部内容更新到磁盘吗, 还是更新一部分? 2. 当内存不足开始刷脏页的时候, 以内存为刷脏页发起点, 这时是把最久不使用的数据页从内存中淘汰掉, 那么这个时候, 对应的额redo log是不是也会清除? 而对于该脏页的操作可能在redo log中并不是连续的记录, redo log有只能顺序读写, 那么redo log是怎么清除掉关于这个脏页的所有记录的?展开
作者回复: 1. 刷脏页是只把内存上最新版本的数据页写到磁盘上。 第一个碰到的redolog会把这个脏页刷下去,注意redolog并没有修改内存数据页的数据(这个不是崩溃恢复过程哦) 后面再碰到这个页面的redolog,这个页面是干净页了,不用刷直接跳过 2. 第二个问题的两个问号是一个答案:不用清除。 下次redo log来刷的时候看到是干净页就直接过了。 好问题。
共 14 条评论37 - 刘志兵2019-04-01老师您好,读了很久其他问题都明白了,还是没太明白只用binlog为啥不能恢复数据,根本原因还是对binlog和redolog都记录的内容没掌握清楚,我的理解跟评论区里“得不到de颜色”说的一样,就是binlog也同样记录了一行数据,但是不知道该从哪里回放,所以没法恢复,但是redo log可以从check_point开始扫描所以能恢复; 看到你回复的是binlog没有能力做出来一页的数据,反正缺失的数据都记在binlog里了,如果能有办法知道binlog从哪里回放,回放一遍就可以了,为啥一定要做一页数据出来,麻烦老师解答一下,谢谢展开
作者回复: 是这样的 一个事务的binlog如果回放,就是重做这个事务,一个事务更新的可能不止一个page。 比如一个事务更新了page ABC 然后崩溃回复了,B坏了,AC没问题,而且AC还落盘了。 这样如果重做事务,B好了,AC又坏了
共 11 条评论28 - melon2018-12-17思考题:应该是第三种,因为两个事务并行执行该update,有一个会卡住,说明有加锁,而且update语句执行后,查看ibd文件和redo log文件的修改时间都更新了。通过show engine innodb status 进一步验证,查看LSN确实增加了,而且Number of rows updated 也加+1了。17
- 信信2018-12-17如果图1的“写入redo log”是写内存,当时刻B发生crash,重启后这部分redo log都丢失了,那么何谈判断redo log是否有完整的prepare还是commit标志呢?
作者回复: 不是哦, 在事务执行期间是在redo log buffer. 在图中写binlog之前,就已经都写了盘并且fsync了
共 13 条评论15 - greatcl2019-04-25看评论里有人说足够有钱把redo log设置无限大,老师已经回复说不能设置无限大。 在MySQL的一个文档里https://dev.mysql.com/doc/refman/5.6/en/innodb-init-startup-configuration.html#innodb-startup-log-file-configuration看到全部redo log文件的大小是不能超过512GB的。 文档里建议redo log全部文件总大小的设置要能满足业务系统一个小时写操作的量,当然如果磁盘足够这个是能尽量大点就大点的。 二刷能学到更多的知识^_^展开共 3 条评论14
- Cy1906222019-02-25老师有几个问题,请教一下: 疑惑点: 1. change buffer:存在buffer pool中的数据库缓存数据,里面的数据呈数据页形式存在, 2.redo log;将change buffer 操作数据库的语句按照顺序记录,后再适当的时候一次提交。 我的理解为另一种缓存。 3.flush 操作:就是在刷新change buffer中数据页上的新数据。 merge 将change buffer中的数据同步到数据库,并获取新的数据页的过程。 问题:1.麻烦老师指一下前面有错误的地方 2.感觉merge 和flush两个过程很象,这两个有什么区别? 最后感谢老师的耐心解答,谢谢您。展开
作者回复: 1. “flush 操作:就是在刷新change buffer中数据页上的新数据。” 除了这句不太明确啥意思, 其他对的。 2. merge是change buffer应用到数据页, flush是数据页从内存写到磁盘
共 3 条评论11 - 小白2019-04-17老师,请教个问题,为什么要比较AB的大小,这快逻辑没有看懂 如果是 a喜欢b 那就insert into `like`(user_id, liker_id, relation_ship) values(A, B, 1) 如果是 b喜欢a 那就insert into `like`(user_id, liker_id, relation_ship) values(B, A, 1) 不是应该这个逻辑么?我理解错了哪里老师共 8 条评论8
- leozhang2019-09-04只要binlog做宕机恢复,不知道我理解对不对,请解答下,我看留言中很多有和我类似的疑问! 假如现在有内存中的数据页 pageA,pageB,pageC, 假如此时pageA 有id=1,name=liu; pageB有id=2,name=liu; pageC有id=3,name=liu三个记录, 同事和数据库磁盘数据保持一致.假设所有liu的初始money为0 此时执行update t set money=money+1 where name=liu; 这个时候写了binlog,假设statement模式则log日志是 update t set money=money+1 where name=liu; 同时binlog日志写磁盘成功, 现在去吧pageA,pageB,pageC刷到磁盘。 刷盘的过程中突然断电,pageA,pageB成功,pageC失败了 此时就悲催了,pageA和pageB的liu money为1,pageC的liu money为0 假设有了binlog。事物提交,那么肯定就不对了,因为pageC数据是错误的 假如现在重做事物,也就是再一次执行刚刚binlog的重做日志。 这个时候如果重做事务,结果也不对了,是不是pageC的 money就为-1了,请指教,可能不对 其实最根本的还是,当初设计binlog的时候就没想crash恢复, 再就是binlog是没有记录数据页的改动的。展开共 2 条评论7
- Geek_maxwell2019-04-11看了一点留言,有几个问题我思考后老师帮我看看是否正确: 1 binlog不能恢复数据,这个是因为binlog是不清楚存储引擎有多少增量没有处理,因为存储引擎对它是透明的,所以binlog设计之初就认为要恢复就是回放所有的binlog,或者从某个时间点从库备份开始恢复剩下所有binlog 2 innodb具有恢复,这个主要是因为它将数据分为存量(落盘) + 增量(changeBuffer+内存中脏页),而这个增也正好是WAL中的redolog + checkpoint两个持久化的数据以及存量联合得到,那么这个增量的恢复就是一个可行性很高,代价很小的操作,因此就是可以具有数据恢复能力 3 redologBuffer, 这个我感觉主要作用不是说为了prepare阶段加速,因为prepare必然要sync 磁盘,因为二阶段sync也是具有持久化的保证,那么它的加速应该是为了事务过程中对于快速得到多版本信息吧,因为需要用到多版本的情况下,所需要回溯的数据总不能读redolog,那就是随机读了,而且多版本一般情况下存在的量不大,可以在内存中存在,这是为了优化随机读展开共 2 条评论7
- linqw2019-01-27由于工作的原因最近一段时间,没有看,为此这周末赶紧恶补了一下,在redo log和binlog的答疑中,写下自己的理解,老师帮忙看下哦 1、redo log和binlog采用两阶段提交,目的是为了双方能多一个选择,在mysql宕机时,如果redo log处于commit,事务直接提交,如果redo log处于prepare,binlog完整事务也提交,只有在binlog不完整时,事务会回滚,以前更新数据页会丢失。 2、redo log如何和binlog联系在一起,XID,在服务重启启动时,会扫描redo log中xid字段到binlog中,找相对应的事务,判断binlog的状态是否需要提交 3、能否在redo log写完整在写binlog么?不行,因为redo log写完,表示事务已经提交,如果在binlog写入的过程中出现异常,会导致redo log和binlog的数据不一致的问题,如果使用binlog恢复库,会导致主库和从库的数据不一致,因为redo log已提交,binlog没有写入成功,为此需要采用两阶段提交。 4、redo log和redo buffer的联系? 即在数据更新时,会先写redo buffer,因为这样可以在写redo log时,先把数据保存起来,在commit时在把数据写入redo log中,因为这样更新的时候会更快,直接更新内存,在事务处于cmmit时,才将内存中的数据写到redo log中。 5、数据页回复是使用buffer pool还是redo log? redo log只有在数据页出现异常的时候,将其拿来恢复上次更新的数据页,但是redo log没有记录完整的数据页数据,为此在正常的情况下,都没有使用redo log,是使用buffer pool将起内部以前的数据刷新到磁盘中,其实想说,脏(这个字不会打,尴尬的一批)数据? 6、redo log一般设置多大?这个老师上面说的很清楚,如果磁盘的容量大,就多搞点,如果小的话可以向公司申请买好设备,要是不同意的话,就得问你们老板了,项目已si,开玩笑,哈哈哈。 7、只用redo log?其实你要是公司机器够大,可以设置成无限大,额,可以假装binlog,额,开玩笑的,因为redo log只有循环写,没法当做binlog,只要有钱就不好说了。 8、只用binlog?不行,为啥不行,百度啊,不行谷歌,好啦,开玩笑,发一下烧,因为如果在一个事务里面,同时写binlog,你看看会有什么问题,一个成功执行,一个执行失败,那会导致一个binlog可以回复数据页,一个不行,为此,导致数据页不一致,好了,今天不知道为啥那么兴奋,额,可能是,你猜吧,好了,写完我也忘了,一起拍砖展开
作者回复: 1. 两阶段提交主要还是因为有了两份日志😆,两份日志的历史原因多些; 2. 对的,XID 3. 是,要考虑binlog和数据的一致性; 4. 对的 5. 是脏页哈 6. 😅 7. 不能设置无限大 8. 是不是暗恋的妹子向你表白了😝
7 - 某、人2018-12-17老师提几个问题: 1.事务在执行过程中,binlog是否像redo log一样记录到binlog_cache里? 2.为什么把redo log buffer设置成全局参数,binlog cache设置为事务级别参数? 3.为什么一般是binlog落盘比redo log更耗时? 4.如果sync为1,dump线程是等到binlog 成功flush,再从binlog cache中把binlog event发送给从库?如果非1,是在最后xid写入就从binlog cache中把binlog event发送给从库?展开
作者回复: 1. 嗯,它有单独的内存,redo log buffer 2. Binlog cache size也是global 的呀,我还去确认了5.5~5.7,你用的是哪个版本? 3. 这个数据是怎么得到的😄 4. 写完磁盘就发,然后再回来flush。 不是,放在binlog cache表示“这事务还没做完”,不发的
共 9 条评论6 - 念你如昔2019-01-24对redo log buffer 来说,我认为他存在的必要就是仅仅是做个缓存使用而已,也就是让在事务中执行dml快点而已,毕竟写到buffer比直接写到redologfile快。而redo log buffer 是有触发机制的,一个大事务中,如果生成的redolog很多,在事务未提交之前,log 是还会从 redolog buffer写到 redolog file里的。
作者回复: 是的,所以它叫做“buffer”,还算是顾名思义的
5