MySQL的BufferPool和其他组件缓存抗污染方案

MySQL的BufferPool和其他组件缓存抗污染方案 场景1Redis4C-8G缓存经常在业务代码里面看到首次就加载的缓存比如我们往Redis里面缓存数据Java逻辑先判断Redis是否存在不存在就放进缓存里然后下次再次命中直接读缓存了这种情况。问题如果有有跑批处理等相关逻辑走了这套缓存可能造成的结果是大量的首次查询数据写入到Redis而且这类数据很久都没第二次访问导致Redis的空间不足出发Redis的淘汰策略。假如Java业务缓存策略写的是第一次出现就放入缓存便于下次查询Redis淘汰策略是(官网截图)翻译后版以上这些Redis的LRU淘汰策略会将一些之前非常热点的数据淘汰掉假设此时瞬间有一些访问被淘汰掉的热点key的请求Java又要重新加载到Redis。这就是对热点数据的缓存污染。MySQL的BufferPool针对缓存污染的解决方案BufferPool是啥可以理解为MySQL启动后申请的连续内存的缓存空间配置innodb_buffer_pool_size参数可设立大小可配置多个以页作为缓存的基本单位也就是你的select命中某页时它会先拿页标记去找BufferPool是否已经缓存过那个页了如果缓存过则直接响应给客户端没缓存过把那页放进BufferPool然后再响应给客户端所以BufferPool也是首次访问某页的数据才会将页加载到里面假设我有个SQLselect * from xxx limit 10000000;那这会加载一堆页到BufferPool里面假设这张表的列很多那它就会有更多页被加载到缓存把以前的热点页干掉的问题或者加载进来的页不一定被用到MySQL使用了类似于年轻代老年代的分而治之的思想抗污染。BufferPool的young区和old区MySQL缓存设计者将BufferPool分成了young和old两部分表去解决这个问题:使用频率高的缓冲页热数据放进young链表使用频率不是很高的缓冲液冷数据放进old链表首次访问的页放入old区多次访问的页看距离上次的访问时间如果这个间隔没有超过阈值innodb_old_blocks_time参数可配置那么就不会进入young里如果间隔很短在阈值内就会将页从old迁移到young官方文档MySQL MySQL 8.0 参考手册 17.5.1 缓冲池实际上本质就是个升级的过程不过不同组件的实现方式、内存划分、操作数据的基本单位不同下面我们看一下Java咖啡因缓存的抗污染策略。Java进程级缓存-Caffeine的抗污染策略先看下caffeine的内存划分翻译这么看他是用CurrentHashMap实现的也是有两块大空间的Window考察区 Main空间(继续考察区和重点保护区)整个流程穿起来就是新数据 ↓ Window ↓ Window 淘汰 ↓ TinyLFU 准入判断 ↓ ┌─────────────┐ │ Probation │ ← 刚进入 Main继续观察 └──────┬──────┘ │ 再次访问 ↓ ┌─────────────┐ │ Protected │ ← 证明是热点重点保护 └─────────────┘这套设计就是在解决和MySQL等组件的同一个问题既不能让新数据把老热点挤掉也不能让一个偶尔访问的数据长期占着核心缓存。