关于缓存一致性与innodb bufferpool的一点思考
首先缓存是什么?
为了避免查询速度太慢,我们会将一些热点数据放在我们的缓存里面,这样查询数据的时候直接从缓存里面拿数据,是很快的。
但是会存在缓存一致性的问题,和缓存不够的问题。
一致性问题:如果我们一条更新语句下来了应该怎么办,是先更新mysql还是先更新缓存?更新过程中如果宕机或者多线程问题是不是会出现缓存不一致或者数据丢失的问题?缓存满了怎么办?
网上有很多的资料都会有相应的方案,这里就不拿网上的资料来说明了。今天我们才用类比的思想来看看缓存的一致性问题。
innodb 的bufferpool的缓存解决方案。
首先,对于mysql如果每次查询数据库都从磁盘中进行查询,那么多次io将是一个混灭性的灾难。所以在innodb里面会设计一个缓存池叫做buffer pool。
当我们的查询数据对应的页不在bufferpool当中时,会去磁盘中查到对应的数据并放在bufferpool中,这一点和我们理解的缓存是一样的。
那么现在我们来看看对应的增删改操作。
对于新增,传统的解决方案是直接插入到存储中,而对于mysql,如果插入的数据是非聚簇索引非非唯一索引的话,会先插入到另外一个区域,叫做insertbuffer,且此时不会将数据插入到磁盘中,而是当下一次页查询到bufferpool中时,insertBuffer中的数据在合并到bufferpool当中。insertBuffer提高了数据的插入的效率,而且插入时数据不会插到磁盘,所以磁盘一直都是老数据。只有当触发一定的策略让bufferpool中的脏数据写到磁盘,磁盘的数据才可能进行更新。
那么插入的过程中会出现问题吗?当我们成功将数据插入到插入缓冲区时,如果这个时候突然宕机,那么缓存当中的数据就不存在了,那怎么办?磁盘上的数据还是老数据。
innodb也考虑到了这一点,索引在进行一些更新操作的时候,会将对应的更新的物理日志保存到我们的redolog,而且这个redolog是日志顺序写的方式,效率相对于我们的随机写性能是要高很多的。当我们突然宕机,再次启动时,redolog会恢复我们的bufferpool,从而让我们的新数据得到恢复。【但是这对于我们讨论的缓存一致性有点冲突。因为在实际场景我们的缓存宕机和数据库宕机是分开的,所以当缓存宕机时,数据库没有宕机,那么数据库的数据最好是最新的数据可以使用。但是我们讨论的bufferpool的缓存思想,如果将bufferpool类比为缓存,磁盘类比为数据库,那么其实因为脏页的双新机制,我们可能无法保证数据是缓存和磁盘数据是一致的,所以如果我们的数据库宕机还没事,但是如果我们的缓存宕机了,那么我们的磁盘上都是老数据,此时我们肯定还不能做redolog的恢复,此时一定会造成数据库老数据的不一致问题。】
那么如果是删除和更新呢?对应的bufferpool当中有没有对应的删除更新的缓冲buffer呢?其实是有的,通过查询官网,我查询到了change buffer,这个change buffer都包含了这个增删改的修改操作缓存,所以我们所有的修改操作都是满足以上的原理的。
但是对于mysql似乎没有我们传统意义上的过期时间的概念。所以我们还需要一套过期时间的解决方案。
首先我们缓存和磁盘的数据可能是不一致的,所以如果bufferpool当中的某一个页过期了,那么磁盘的老数据就会暴露出来。此刻,能不能在一个缓存过期的时候,将其写会到磁盘?那么怎么检测一个缓存是否过期,其实也是一个问题。
从上面的策略简单来看,似乎,如果缓存不过期,实现还是挺简单的,但是一旦涉及到数据过期,那么需要相应的机制去监测这个过期,并将其写到磁盘。
从上面的思考来看,其实bufferpool的思想似乎不适合来做缓存的一致性架构,在innodb中,这个bufferpool仿佛是和磁盘一体的,如果bufferpool不可用了,那么实际上我们的整个innodb都是不可用的,但是对于我们的缓存一致性来说,缓存和数据库是分开的,那么这样的话才会涉及到数据库的一致性问题。所以这一点类比是失效的。