想要优化写的速度,我们首先会想到什么?
随机IO转为顺序IO,单次写转为批次写。
缓存
常见的写逻辑优化,先写缓存,然后刷盘。
逻辑:
- 如果页在内存中,直接修改。如果不在,读到内存,再修改。
- 定期刷盘。
数据一致性
如果命中了缓存,读到的是准确的。内存淘汰的时候,刷盘了,再读磁盘的时候数据也是准确的。
问题
这样有两个问题:
- 写多读少,就会导致命中缓存的概率比较低,每次都要先读到内存。
- 如果崩溃了,内存的数据就丢了。
redolog
Innodb建立了redolog,即预写日志。写内存不变,添加了一步写redolog的步骤。刷盘的时候就把日志清掉。如果崩溃了,就从日志读取。
WAL技术,WAL的全称是Write-Ahead Logging,它的关键点就是先写日志,再写磁盘。
redolog里面存的是结果值。也就是将change buffer同步到日志了。(binlog写的是流水日志)
这样就转化成了一次读磁盘,一次写内存操作和一次追加日志写的操作,都挺快。
change buffer
还有个问题就是写必须先读。
我们将更新的数据存到change buffer中,有人读则将读到的数据和change buffer中的数据一起返回。刷盘的时候将数据marge后刷盘。
这样就优化成了一次写内存+一次日志追加写。
但是要注意:
不支持唯一索引页。为什么?我不读盘怎么知道你有没有冲突呢?
什么时候刷盘?
- 系统内存满了。
- MySQL比较闲
- 正常关闭
这些都比较好理解。
还有个下面的几种:
redolog写满了。
注意redolog不是无限写的,而是一个圈。
- write pos是当前记录的位置,一边写一边后移,写到第3号文件末尾后就回到0号文件开头。
- checkpoint是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件。
- write pos和checkpoint之间的是还空着的部分,可以用来记录新的操作。
- 如果write pos追上checkpoint,表示满了,这时候不能再执行新的更新,得停下来先擦掉一些记录,把checkpoint推进一下。
定期刷新脏页或者LRU淘汰脏页
内存和磁盘数据一致的,称为干净页;反正,称为脏页。将内存数据写入磁盘,则便一致,称为刷脏页。
两种坑:
- 某个查询需要刷新过多脏页,则很慢。
- redolog写满,所有更新都会卡住。
那么,怎么提高刷脏页的效率?
- 正确地设置innodb_io_capacity参数,判断写盘速度。
- 脏页比例,innodb_max_dirty_pages_pct。
在准备刷一个脏页的时候,如果这个数据页旁 边的数据页刚好是脏页,就会把这个“邻居”也带着一起刷掉;而且这个把“邻居”拖下水的逻辑还 可以继续蔓延。
innodb_flush_neighbors 参数,为0就是只刷自己。
疑惑
很多人可能会问,到底是从redolog刷到磁盘,还是从内存刷到磁盘。
回到redolog创建的原因,是为了异常崩溃的时候。
所以正常刷盘是从内存刷到磁盘,redolog满了也是从内存刷到磁盘然后清理redolog。异常崩溃呢?也是从redolog写到内存中,先恢复现场,再走正常流程。
结语
- 如果有不对的地方欢迎指正。
- 如果有不理解的地方欢迎指出我来加栗子。
- 如果感觉OK可以点赞让更多人看到它。
相关阅读:
- 学MySQL的第一座大山—索引
- 学MySQL的第二座大山—锁,事务
- 学习mysql的最后一座大山—表设计
- MySQL为什么要用B+树?
- MySQL怎么缓解读的压力的?—buffer pool
- MySQL怎么缓解写的压力?— change buffer
- MySQL架构概览