InnoDB引擎是MySQL插件引擎的其中一个,由许多内存块组成一个内存池,主要负责:
- 维护所有进程/线程需要访问的内部数据结构
- 缓存磁盘数据,方便快速的读取,
- 重做日志(redo log)缓冲
- InnoDB存储引擎是多线程引擎,后台不同的后台线程负责处理不同的任务
相关的线程
- Master Thread
核心的后台线程,负责将缓冲池数据异步刷新到磁盘,保证数据的一致性,包括脏页刷新,合并插入缓冲,UNDO页回收等 - IO Thread
主要负责IO请求的回调处理,1.0.x版本后read和write thread分别扩大到4个,分别使用
innodb_read_io_threads和innodb_write_io_threads来设置个数。可以用SHOW ENGINE INNODB STATUS指令来查看IO Thread信息。
IO Thread 0是insert buffer thread,IO Thread 1是log thread,之后根据读写线程的个数来分别设置读写线程,读线程的ID小于写线程。 - Purge Thread
回收已经使用并分配的undo页,以减轻Master Thread的工作量。1.2版本支持多个Purge Thread,这样能够进一步加快undo页的回收,设置个数参数为 innodb_purge_threads. - Page Cleaner Thread
1.2.x版本引入的线程,作用是将之前版本的高速缓存中的脏页刷新到磁盘的操作放入这个线程来单独处理,以减轻master thread的工作量,提高引擎性能。
缓冲池
InnoDB引擎是基于磁盘存储的,存储的基本单位是页(16KB),由于CPU和IO速度差异的问题,需要使用高速缓存来提高性能。
- 读取数据库中的页操作:
页如果在缓冲池中有,直接读,否则将磁盘的页放到缓冲池再读 - 数据库页的修改:
首先修改缓冲池中的页(涉及undo,redo等),然后再以一定的频率刷新到磁盘上(脏页刷新,通过checkpoint机制)
缓冲池的大小可以通过参数innodb_buffer_pool_size来设置,缓冲池涉及到的页种类有:
- 索引页
- 数据页
- undo页
- 插入缓冲(insert buffer)
- 自适应哈希索引页
- 锁信息页(lock info)
- 数据字典信息
缓冲池实例的个数也可以通过参数 innodb_buffer_pool_instances来配置,可以通过INNODB_BUFFER_POOL_STATS命令来查看各个缓冲池的使用情况。
LRU List,Free List,Flush List
InnoDB通过这几个list来对缓冲池中这么多的页进行管理。
一般缓冲池通过LRU(latest recent used)算法来进行管理(和计算机cache的管理算法类似),最频繁使用的页在LRU列表的最前端,最少使用的页在最后端。自适应哈希索引,lock信息,insert buffer等页不用LRU算法来维护。
InnoDB在LRU list中插入新页不是插入到最前端,而是插入到midpoint位置,midpoint位置默认为列表长度的5/8处,可以通过参数innodb_old_blocks_pct来控制,把midpoint之前的表称为new list,之后的old list。这样做的原因是放到队首的话某些SQL操作会让缓冲池中的脏页被刷新出,并将热点数据页从LRU List中移除,而下次读取该页时需要再次访问磁盘,影响引擎效率。
为了让新加页延迟进入热点数据页,可以通过参数innodb_old_blocks_time来设置延迟进入时间。
LRU list管理的是缓存中已经读取的页,数据库刚启动时缓冲池中的页都放在free list,当需要某个页时如果free list有空闲页,就从free list中删除这段内存信息并添加到LRU list中,否则淘汰LRU list末端的页,并将该页的内存给新页来用。
压缩页
1.0.x版本后支持压缩页功能,将页压缩为1,2,4或者8KB,非16KB的页不由LRU list来管理,而是通过unzip_LRU list来管理。
unzip_LRU建立4个不同的链表来分别管理1,2,4,8KB的压缩页,内存分配通过伙伴算法来管理:
//伙伴算法
//要申请4KB的内存来存页
if 4KB unzip_LRU list有空闲页内存:
直接用
else:
if 8KB unzip_LRU list有空闲内存页:
分成2个4KB页放入到4KB的list中,再用
else:
从LRU列表中申请16KB页,分成1个8KB,2个4KB的页,放入对应的list
脏页
在LRU list中页被修改后,被称为脏页,这时缓冲池中的页数据和磁盘的页数据不一致了,脏页数据会统一被Flush list管理,通过checkpoint机制来把脏页数据刷新到磁盘中。注意LRU和Flush list都会保存脏页信息。
重做日志缓存
InnoDB引擎的内存区除了缓冲池还有redo log缓存,InnoDB会将redo日志信息放入到这个缓冲区,再按一定平律将其刷新到磁盘redo log文件,该缓存的大小由参数innodb_log_buffer_size控制,默认8MB。redo log缓存会在一下情况把缓存中的内容刷新到外部磁盘的redo log文件中:
- Master thread 每秒会把redo log缓存刷新到redo log 文件
- 每个事务提交时
- 当redo log的缓存剩余空间小于1/2时
额外的内存池
在对一些数据结构本身的内存分配时,会从额外的内存池进行申请,不够时则从缓存池进行申请
checkpoint技术
如果缓冲池中没有一个页变化,就刷新到磁盘,这样花销太大,而且在从缓冲池更新到磁盘时发生宕机的话,存在数据丢失的问题,所以一般采取write ahead log策略,当事务提交时,先写redo log,再修改磁盘的相关页,这样就用redo log作为备份。
checkpoint技术就是将缓冲池中的脏页刷新到磁盘上,主要解决:
- 缩短数据库的恢复时间
- 缓冲池不够用时,将脏页刷新到磁盘上
- redo log不够用时,刷新脏页
InnoDB有两种checkpoint:
- Sharp Checkpoint:在数据库关闭时将所有脏页刷新到磁盘,是默认的工作方式,innodb_fast_shutdown=1
- Fuzzy Checkpoint:运行时刷新脏页到磁盘,