mysql缓存机制——读缓存篇

    最近进行了一系列mysql相关的研究,了解了一遍mysql的缓存机制,在这里集中总结一下。
    本文是基于mysql-innoDB的缓存机制解析
    缓存机制是一种常用的机制,在操作系统中,因为不同存储介质的读写效率天差地别,所以采取缓存机制来加快系统的读写效率,将一些常用的数据放在读写效率较高的缓存里,避免每次读写都直接对磁盘进行操作
    作为专门存储数据的MySql系统,也有他自己的缓存策略,对于MySql这样的数据库,磁盘IO通常是它的最大瓶颈,MySql做了读缓存和写缓存这两个机制来有效减少磁盘IO,提高数据库的执行效率
    这里来解析一下innoDB的缓冲池机制
    缓冲池有什么作用?
    最终目的是为了加速访问,将磁盘上的数据加载到缓冲池(也就是内存)当中,由于系统访问内存比访问磁盘要快速很多倍,所以当一条数据库请求抵达的时候,若是能够在缓存中命中,就不需要去访问磁盘读取数据,当数据库请求很频繁很密集的时候,这种缓存策略能够极大地增强数据库的响应速度,从而更高效地承载业务
    缓冲池有什么局限?
    缓冲池因为是建立在内存当中的,所以并不可以非常大,读取速度快的代价是容量小,比如一台计算机的磁盘可以有500G,但是内存可能只有8个G。它只能将数据库的一部分表数据和索引数据缓存起来,并不能将所有数据都加载到缓存里,所以需要合理分配缓存,使尽量多的数据库请求能够在缓冲池当中得到命中,从而最大限度地降低磁盘IO
    innoDB的缓冲池如何保证缓冲命中率的?
    因为我所从事的游戏行业,数据的读取远远大于数据的写入,所以这里先从读缓存介绍起
    1.MySql中的数据并不是你想读哪一条就直接去磁盘里读取单单一条的信息,而是整页读取的,一次读取一页的数据(具体大小可以由参数控制,一般是4K),当某条Sql请求涉及到某一页的某一条数据时,整一个数据页都会从磁盘被加载到缓存里,后续的请求如果存在于这个页内,那么就不需要访问磁盘了
    2.为何是按页读取?因为大部分的MySql读取操作都是遵循“局部性原理”的,也就是当某一条数据被系统请求了,那么与其相邻的数据在不久的未来也有很大的概率会被请求,利用这种“局部性原理”可以十分有效地提升数据库请求的缓存命中率
    3.那么缓存页的置换淘汰规则是怎么样的呢?内存有限,随着Sql请求的增加,缓存池总会被填满,那么新的页想要加载加来,就必然会置换掉已有的列。
        a.通常容易想到的方法就是LRU算法:用队列管理缓存页,若缓存不命中,则从磁盘上加载一个数据页进来,放置在队列头,将队列末尾的数据页淘汰掉,若缓存命中,则将该页数据移动到队列头,不进行淘汰
        b.但是LRU算法也有它的局限性,因为数据库有预读机制,还有一些批量扫描数据的操作,预读操作会将一些数据页加载到内存中,但是并不会使用它,导致没有被使用的数据页平白被放置在了队列头,占用了缓冲池的空间,而批量扫描的操作会大量地将数据页加载进来,如果用单纯的LRU算法,一次大量扫描的操作,就会将缓冲池内的全部数据页都置换一遍
        c.解决预读失效的问题:将缓冲池分成新生代老生代,新生代的末尾连接老生代的头部,被预读的页被读入缓存时,只放在老生代的头部,只有数据真正被读取了,该页才会从老生代的头部被移动到新生代的头部,如果没有被读取,那么因为老生代在新生代的后面,所以会更快地被淘汰出缓存
        d.新生代和老生代的长度占比是可以通过MySql参数调节的
        e.解决数据批量扫描的问题:大量热数据被刷出缓冲,造成缓冲污染。这里可以在上面的新生代与老生代基础上做进一步的细化优化,提出一个老生代窗口期的机制,预读的数据页,即使在老生代中被真正访问,也不会立即插入到新生代的头部,只有在满足“被访问”and“在老生代停留时间大于T”的情况下,才会被放入新生代的头部,于是,在批量扫描的时候,只会有大量的数据页被置入到老生代当中,如果这些数据页只被访问一次,那么它们将会很快被淘汰出缓冲池
        f.总而言之,可以总结为一个策略:提高新生代缓冲池的准入门槛,确保只有真正的“热数据”才会被置入进来,将一些“有可能热的数据”放在老生代里按照某种策略观察,可以理解为,新生代的缓存是关乎数据库性能的极其重要的数据,不能随意被某些操作污染
        g.相关参数:

        innodb_buffer_pool_size:缓冲池大小,在内存足够的情况下,往往会选择调大这个参数,缓冲池越大,数据库访问的缓存命中率也就会越高,数据库性能也会越好
        innodb_old_blocks_pct:老生代占整个缓冲池链表的长度比例,默认是63:37,老生代37
        innodb_old_blocks_time:老生代停留窗口时间,老生代中的数据页只有在被读取,并在老生代中存在超过这个参数指明的时间后,才会被置入到新生代的头部
    

你可能感兴趣的:(mysql)