Innodb 的内存结构主要分为 3 个部分: Buffer Pool、Change Buffer、Adaptive HashIndex,另外还有一个(redo)log buffer。我们可到再官网看到 InnoDB 的内存结构和磁盘结构。
1.Buffer Pool(缓存池)
首先,InnnoDB 的数据都是放在磁盘上的,InnoDB 操作数据有一个最小的逻辑单位,叫做页(索引页和数据页)。我们对于数据的操作,不是每次都直接操作磁盘,因为磁盘的速度太慢了。 InnoDB使用了一种缓冲池的技术,也就是把磁盘读到的页放到一块内存区域里面。这个内存区域就叫Buffer Pool。
下一次读取相同的页,先判断是不是在缓冲池里面,如果是,就直接读取,不用再次访问磁盘。
修改数据的时候,先修改缓冲池里面的页。内存的数据页和磁盘数据不一致的时候,我们把它叫做脏页。InnoDB里面有专门的后台线程把Buffer Pool的数据写入到磁盘,每隔一段时间就一次性地把多个修改写入磁盘,这个动作就叫做刷脏。
Buffer Pool 缓存的是页面信息,包括数据页、索引页。
SHOW STATUS LIKE '%innodb_buffer_pool%'; -- 查看服务器状态中 Buffer Pool 相关信息
SHOW VARIABLES like '%innodb_buffer_pool%'; -- 查看参数(系统变量)
![](https://i-blog.csdnimg.cn/blog_migrate/c74b2b098ae600346976bed40f7c9473.png#pic_center)
这些状态都可以在官网查到详细的含义,用搜索功能。 Buffer Pool默认大小是128M(134217728字节),可以调整。
内存的缓冲池写满了怎么办?(Redis 设置的内存满了怎么办?)InnoDB 用 LRU算法来管理缓冲池(链表实现,不是传统的LRU,分成了young和old),经过淘汰的数据就是热点数据。
内存缓冲区对于提升读写性能有很大的作用。
思考一个问题:当需要更新一个数据页时,如果数据页在Buffe rPool中存在,那么就直接更新好了。否则的话就需要从磁盘加载到内存,再对内存的数据页进行操作。也就是说,如果没有命中缓冲池,至少要产生一次磁盘IO,有没有优化的方式呢?向下看…
1.1 Change Buffer(写缓存)
如果这个数据页不是唯一索引(注:唯一索引就是在同一字段下不能有相同值),也就不需要从磁盘加载索引页判断数据是不是重复(唯一性检查)。这种情况下可以先把修改记录在内存的缓冲池中,从而提升更新语句(Insert、Delete、Update)的执行速度。
这一块区域就是 Change Buffer。5.5 之前叫Insert Buffer 插入缓冲,现在也能支持delete和update。最后把 Change Buffer 记录到数据页的操作叫做 merge。
什么时候发生 merge?有几种情况:在访问这个数据页的时候,或者通过后台线程、或者数据库shut down、redo log写满时触发。
如果数据库大部分索引都是非唯一索引,并且业务是写多读少,不会在写数据后立刻读取,就可以使用Change Buffer(写缓冲)。写多读少的业务,调大这个值:
SHOW VARIABLES LIKE 'innodb_change_buffer_max_size';
max_size 代表Change Buffer 占 Buffer Pool 的比例,默认25%。
1.2 Adaptive Hash Index
InnoDB会根据访问的频率和模式,为热点页建立哈希索引,来提高查询效率。InnoDB存储引擎会监控对表上各个索引页的查询,如果观察到建立哈希索引可以带来速度上的提升,则建立哈希索引,所以叫做自适应哈希索引。
自适应哈希索引是通过缓冲池的B+树页构建而来,因此建立速度很快,而且不需要对整张数据表建立哈希索引。其有一个要求,即对这个页的连续访问模式必须是一样的,也就是说其查询的条件(WHERE)必须完全一样,而且必须是连续的。
2.Log Buffer(Redo log)
MySQL 在更新数据时,为了减少磁盘的随机 IO,因此并不会直接更新磁盘上的数据,而是先更新 Buffer Pool 中缓存页的数据,等到合适的时间点,再将这个缓存页持久化到磁盘。而 Buffer Pool 中所有缓存页都是处于内存当中的,当 MySQL 宕机或者机器断电,内存中的数据就会丢失,因此 MySQL 为了防止缓存页中的数据在更新后出现数据丢失的现象,引入了 redo log 机制。
当进行增删改操作时,MySQL 会在更新 Buffer Pool 中的缓存页数据时,会记录一条对应操作的 redo log 日志,这样如果出现 MySQL 宕机或者断电时,如果有缓存页的数据还没来得及刷入磁盘,那么当 MySQL 重新启动时,可以根据 redo log 日志文件,进行数据重做,将数据恢复到宕机或者断电前的状态,保证了更新的数据不丢失,因此 redo log 又叫做重做日志。它的本质是保证事务提交后,更新的数据不丢失。——用它来实现事务的持久性。
这种日志和磁盘配合的整个过程,其实就是 MySQL 里的 WAL 技术(Write-Ahead Logging),它的关键点就是先写日志,再写磁盘。
1)Redo log 的内存部分:Log Buffer
当然 redo log 的内容也不是每一次都直接写入磁盘,在 Buffer Pool 里面有一块内存区域(Log Buffer)专门用来保存即将要写入日志文件的数据,Innodb_log_buffer默认大小为8M,它一样可以节省磁盘IO。
SHOW VARIABLES LIKE 'innodb_log_buffer_size';
2)同样是写磁盘,为什么不直接写 db file,而是先写日志?
我们先来了解一下随机I/O和顺序I/O的概念。磁盘的最小组成单元是扇区,通常是512个字节。操作系统和内存打交道,最小的单位是页Page。操作系统和磁盘打交道,读写磁盘,最小的单位是块Block。
如果我们所需要的数据是随机分散在不同页的不同扇区中,那么找到相应的数据需要等到磁臂旋转到指定的页,然后盘片寻找到对应的扇区,才能找到我们所需要的一块数据,一次进行此过程直到找完所有数据,这个就是随机IO,读取数据速度较慢。
假设我们已经找到了第一块数据,并且其他所需的数据就在这一块数据后边,那么就不需要重新寻址,可以依次拿到我们所需的数据,这个就叫顺序IO。
刷盘是随机I/O,而记录日志是顺序I/O,顺序I/O效率更高。所以,先不断把内存中bufferpool的数据修改写入日志,保证不会丢失,然后等到一个适当的时机(系统比较空闲)将操作记录更新到磁盘里面。达到延迟刷盘时机的目的,进而提升系统吞吐。redo log存在的意义主要就是降低对数据页刷盘的要求。
3)Log Buffer 什么时候刷盘?
那么,Log Buffer什么时候写入log file,或者说什么日志什么时候刷盘?log buffer写入磁盘的时机,由一个参数控制,默认是1。
注:在我们写入数据到磁盘的时候,操作系统本身是有缓存的。flush就是把操作系统缓冲区写入到磁盘。
值 | 含义 |
---|---|
0(延迟写,延迟刷) | logbuffer 将每秒一次地写入 logfile 中,并且 logfile 的 flush 操作同时进行。 该模式下,在事务提交的时候,不会主动触发写入磁盘的操作。 |
1(默认,实时写,实时刷) | 每次事务提交时 MySQL 都会把 logbuffer 的数据写入 logfile,并且刷到磁盘中去。 |
2(实时写,延迟刷) | 每次事务提交时 MySQL 都会把 logbuffer 的数据写入 logfile。但是 flush 操作并不会同时进行。 该模式下,MySQL 会每秒执行一次 flush 操作。 |
SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit';
4)总结一下 redo log(重做日志)有什么特点?
1)redo log 是 InnoDB存储引擎实现的,并不是所有存储引擎都有。
2)不是记录数据页更新之后的状态(某一行或某几行修改成怎样怎样),而是记录当前页做了什么改动,属于物理日志。它用来恢复提交后的物理数据页(恢复数据页,且只能恢复到最后一次提交的位置)
日志分为物理日志和逻辑日志
- 物理日志就是直接记录数据,记录被修改的page的偏移量,优点就是不依赖原page的内容,用日志的内容可以直接覆盖到磁盘上面,而缺点就是占用的空间太多,比如新增一个btree索引或者一个update操作。
- 逻辑日志只是记录关系表上面的元操作,比如update一行数据,delete一行数据等,优点就是比较简洁而且占用的空间要小,缺点就是需要依赖原page内容,而且会有部分执行和操作一致性的问题。
3)redo log 分成内存和磁盘两部分:
- 内存部分:Log Buffer,存在刷盘操作。
- 磁盘部分:对应于 /var/lib/mysql/目录下的ib_logfile0和ib_logfile1,每个48M。
4)因为redo log 实际上记录数据页的变更,而这种变更记录是没必要全部保存,所以 redo log 的大小是固定的,前面的内容会被覆盖。如下图所示:
再强调一次,redo log的内容主要是用于崩溃恢复。磁盘的数据文件,数据来自 buffer pool。redo log 写入磁盘的日志文件,不是写入数据文件。
以上就是MySQL的内存结构。总结一下,分为:Buffer pool、change buffer、Adaptive Hash Index、 log buffer。