当你听到三级缓存的时候,你在想什么?你了解过的有哪些三级缓存?CPU三级缓存?Spring三级缓存?应用架构(JVM、分布式缓存、db)三级缓存?今天爬完香山,趁自己还不困的时候,把三级缓存的一些重点絮叨絮叨。
CPU三级缓存
离 CPU 核心越近,缓存的读写速度就越快。但 CPU 的空间很狭小,离 CPU 越近缓存大小受到的限制也越大。所以,综合硬件布局、性能等因素,CPU 缓存通常分为大小不等的三级缓存。三级缓存要比一、二级缓存大许多倍,这是因为当下的 CPU 都是多核心的,每个核心都有自己的一、二级缓存,但三级缓存却是一颗 CPU 上所有核心共享的。
缓存一致性:在多核CPU时代,CPU有“缓存一致性”原则,也就是说每个处理器(核)都会通过嗅探在总线上传播的数据来检查自己的缓存值是不是过期了。如果过期了,则失效。比如声明volitate,当变量被修改,则会立即要求写入系统内存。
Spring三级缓存
Spring三级缓存机制包括以下三个缓存:1. singletonObjects:用于存储完全创建好的单例bean实例。2. earlySingletonObjects:用于存储早期创建但未完成初始化的单例bean实例。3. singletonFactories:用于存储创建单例bean实例的工厂对象。
当Spring发现两个或更多个bean之间存在循环依赖关系时,它会将其中一个bean创建的过程中尚未完成的实例放入earlySingletonObjects缓存中,然后将创建该bean的工厂对象放入singletonFactories缓存中。接着,Spring会暂停当前bean的创建过程,去创建它所依赖的bean。当依赖的bean创建完成后,Spring会将其放入singletonObjects缓存中,并使用它来完成当前bean的创建过程。在创建当前bean的过程中,如果发现它还依赖其他的bean,Spring会重复上述过程,直到所有bean的创建过程都完成为止。
需要注意的是,当使用构造函数注入方式时,循环依赖是无法解决的。因为在创建bean时,必须先创建它所依赖的bean实例,而构造函数注入方式需要在创建bean实例时就将依赖的bean实例传入构造函数中。如果依赖的bean实例尚未创建完成,就无法将其传入构造函数中,从而导致循环依赖无法解决。此时,可以考虑使用setter注入方式来解决循环依赖问题。
应用架构三级缓存
当我们说应用架构三级缓存的时候,一般说JVM级别的、分布式缓存级别的、数据库级别的。
JVM级别的话,一般常见本地缓存框架有Guava Cache和Caffeine Cache。据说Caffeine Cache青出于蓝,可以Caffeine为学习基础。
分布式缓存级别的话,我们一般用的Redis。三高架构一般会选择Redis集群方式,一般会有两种集群方案的实现:分片集群社区方案 -> Twemproxy、Codis(Redis 节点之间无通信,需要部署哨兵,可横向扩容)。分片集群官方方案 -> Redis Cluster (Redis 节点之间 Gossip 协议,无需部署哨兵,可横向扩容) 。Proxy + Redis Cluster不侵入业务侧。
数据库缓存的话拿MySQL举例子,MySQL底层按页存储,一般会按页缓存。如果语句不在查询缓存中,就会继续后面的执行阶段。执行完成后,执行结果会被存入查询缓存中。你可以看到,如果查询命中缓存,MySQL 不需要执行后面的复杂操作,就可以直接返回结果,这个效率会很高。
但是大多数情况下建议你不要使用查询缓存,为什么呢?查询缓存的失效非常频繁,只要有对一个表的更新,这个表上所有的查询缓存都会被清空。因此很可能你费劲地把结果存起来,还没使用呢,就被一个更新全清空了。对于更新压力大的数据库来说,查询缓存的命中率会非常低,因为查询缓存往往弊大于利。mysql8直接去掉了查询缓存。
怎么保证三级缓存的一致性?1.设置:ttl过期时间和maxIdleTime空闲时间;JVM级别的ttl小于分布式缓存的。2.延时双删(event订阅延迟),Redis多实例一致,Redis发布订阅模式。
你还了解那些三级缓存?欢迎留言沟通,希望能抛砖引玉。
精进自省:不装不端、不怨不怼、不破不立、真实诚恳、心存光明。