InnoDB存储引擎是基于磁盘存储的。由于CPU速度和磁盘速度之间的鸿沟,InnoDB引擎使用缓冲池技术来提高数据库的整体性能。
在数据库中进行读取页的操作时,将从磁盘读到的页存放在缓冲池中,下一次读取相同的页时首先判断该页是不是在缓冲池中,如果在,就称该页在缓冲池中被命中,直接读取该页。对于数据库中页的修改操作,首先修改在缓冲池页中,然后以一定的频率刷新到磁盘。这里需要注意的是,页从缓冲池刷新回磁盘的操作并不是在每次页发生更新时触发,而是通过一种称为checkpoint的机制刷新回磁盘,这也是为了提高数据库的整体性能。
InnoDB存储引擎的缓存机制和MyISAM的最大区别就在于InnoDB缓冲池不仅仅缓存索引,还会缓存实际的数据。InnoDB存储引擎可以使用更多的内存来缓存数据库相关的信息,这对于内存价格不断降低的时代无疑是很吸引人的特性。
InnoDB Buffer Pool是InnoDB性能提升的核心,不像Query Cache仅存的SQL对应的结果集,Buffer Pool上可以完成数据的更新变化、减少随机I/O的操作、提高写入性能,而Query Cache最忌讳表的数据更新,会导致相应的Cache失效,带来额外系统消耗。在实际中,尽可能增大innodb_buffer_pool_size的大小,把频繁访问的数据都放到内存中,尽可能减少InnoDB对于磁盘I/O的访问,把InnoDB最大化为一个内存型引擎。
如图2-2所示,innodb_buffer_pool_size参数用来设置InnoDB的缓冲池(InnoDB Buffer Pool)的大小,也就是缓存用户表及索引数据的最主要缓存空间,这个对InnoDB整体性能影响也最大,一般可以设置为50%到80%的内存大小。在专用数据库服务器上,可以将缓冲池大小设置得大些,多次访问相同的数据表数据所需要的磁盘I/O就更少。在MySQL 5.7版本之前,调整innodb_buffer_pool_size大小必须在my.cnf配置里修改,然后重启mysql进程才可以生效。如今到了MySQL 5.7版本,可以直接动态调整这个参数修改Buffer Pool的大小,方便了很多。尤其是在服务器内存增加之后,运维人员更不能粗心大意,要记得调大innodb_buffer_pool_size这个参数。在调整innodb_buffer_pool_size期间,用户的请求将会阻塞,直到调整完毕,所以请勿在业务高峰期调整,最好在凌晨两三点低峰期调整。
图2-2
在InnoDB存储引擎中,缓冲池中页的大小默认为16KB,通过LRU(Letest Recent Used,最近最少使用)算法来进行数据页的换进换出操作。也就是说,最频繁的页放在LRU列表的前端,而最少使用的页放在LRU列表的尾端。缓冲池不能存放新读取到的页时,会释放LRU列表中尾端的页。
InnoDB存储引擎对传统的LRU算法做了一些优化。因为如果直接将最新读取的页放到LRU列表的首部,那么某些SQL操作(比如全表扫描或者索引扫描)如果要访问很多页,甚至是全部页(通常来说仅在此次查询操作中需要,并不是活跃的热点数据),很可能会将活跃的热点数据挤出LRU列表。在下一次需要读取这些热点数据页时,InnoDB存储引擎则需要再一次从磁盘读取。所以InnoDB存储引擎的LRU列表中还加入了midpoint位置,即新读取的页并不是直接放到LRU列表首部,而是放到LRU列表的midpoint位置。这个算法在InnoDB存储引擎下称为midpoint insertion strategy,默认该位置在LRU列表长度的5/8处。midpoint的位置可由参数innodb_old_blocks_pct控制,如图2-3所示。
图2-3
在图2-3中,innodb_old_blocks_pct默认值为37,表示新读取的页插入到LRU列表尾端37%的位置。在InnoDB存储引擎中,把midpoint之后的列表称为old列表,之前的列表称为new列表。new列表的数据可以简单理解为都是活跃的热点数据。InnoDB还有一个参数innoDB_old_blocks_time,表示页读取到midpoint位置后需要等待多久才会被加入到LRU列表的热端。
在MySQL 5.6之前的版本里,如果一台高负荷的机器重启后内存中大量的热数据被清空,buffer中的数据就需要重新预热。所谓预热,就是等待常用数据通过用户调用SQL语句从磁盘载入到内存,这样高峰期间性能就会变得很差,连接数就会很高。通常要手动写一个脚本或存储过程来预热。
MySQL 5.6之后的版本提供了一个新特性来快速预热buffer_pool缓冲池,如图2-4所示。参数innodb_buffer_pool_dump_at_shutdown=ON表示在关闭MySQL时会把内存中的热数据保存在磁盘里ib_buffer_pool文件中,其保存比率由参数innodb_buffer_pool_dump_pct控制,默认为25%。参数innodb_buffer_pool_load_at_startup=ON表示在启动时会自动加载热数据到Buffer_Pool缓冲池里。这样,始终保持热数据在内存中。
图2-4
只有在正常关闭MySQL服务或者pkill mysql时才会把热数据dump到内存,机器宕机或者pkill -9 mysql时是不会dump的。