1.缓存是计算机上的原始数据的复制集,缓存的使用与应用场景密切相关,在不同的场景上会有不同的意义
2.缓存的目的是想通过提高服务的性能从而提高应用的用户体验
3.系统性能的指标一般包括:响应时间、延迟时间、吞吐量、并发用户数量和资源利用率等几个方面
(1)吞吐量:系统在单位时间内处理的请求的数量
4.缓存的原理与基本概念:
(1)缓存:用于协调两者数据传输速度差异的结构,均可称之为Cache
<1>是用来协调CPU与主存之间存取速度的差异而设置的
<2>CPU的工作速度高,但内存的工作速度相对较低,为了解决这个问题,通常使用缓存
<3>缓存的存取速度介于CPU和主存之间
<4>缓存核心就是用空间换时间
<5>常见的应用包括CPU缓存、操作系统缓存、本地缓存、分布式缓存、HTTP缓存、数据库缓存等
<6>实现的难点就在于清空策略的实现,比较合理的思路就是定时回收与即时判断数据是否过期相结合
(2)缓存相关概念
<1>命中率:指请求次数与正确返回结果次数的比例,其影响因素包括数据实时性
1.提高缓存命中率,可以对缓存分层,分为全局缓存,二级缓存
2.他们是存在继承关系的,全局缓存可以有二级缓存来组成
<2>最大元素:缓存中可以存放的元素的最大数量
<3>清空策略:FIFO,最先进入缓存的数据在空间不够时会被优先清理;
LFU一直以来最少被使用的元素会被清理,可以给缓存元素设置一个计数器实现;
LRU最近最少使用的缓存元素会被清理,可以通过一个时间戳来讲最近未使用数据清除
<4>预热策略:全量预热,一开始就加载全部数据,适用于不怎么变化的数据(地区数据);
增量预热,查询不到时从数据源取出放入缓存
(3)缓存相关问题
<1>缓存穿透:
1.一般的缓存系统,都是按照key去缓存查询,如果不存在对应的value,就应该去后端系统查找(比如DB)
2.若key对应的value是一定不存在的,该key并发请求量很大,会直接去后端查找。这就叫做缓存穿透
3.解决方法包括:将查询结果为空的情况也进行缓存,
缓存时间设置短一点,并在该key对应的数据insert之后清理缓存,
对一定不存在的key进行过滤。
<2>缓存雪崩:
1.当缓存服务器重启或者大量缓存集中在某一个时间段失效的现象叫做缓存雪崩,
2.缓存雪崩后,这时会直接去后端查询,给后端系统(比如DB)带来很大压力
3.解决方案包括:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量,
不同的key设置不同的过期时间,让缓存失效的时间点尽量均匀,
做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期
<3>缓存一致性:
1.通过锁解决一致性问题
2.采用延时双删策略(双删指的是对缓存进行2次删除操作),具体的步骤就是:先删除缓存
再写数据库
休眠500毫秒
再次删除缓存
3.设置缓存过期时间
4.结合双删策略+缓存超时设置:这样最差的情况是在超时时间内数据存在不一致,而且又增加了写请求的耗时
5.异步更新缓存(基于订阅binlog的同步机制)
5.1.MySQL binlog增量订阅消费+消息队列+增量数据更新到缓存(主要指:Redis)
5.2.实现步骤如下:读数据-热数据基本都在缓存
写数据-增删改都是操作MySQL
更新缓存数据:MySQ的数据操作binlog,来更新到缓存
5.3.缓存(Redis)更新:数据操作主要分为两大块:全量(将全部数据一次写入到缓存(redis))
增量(实时更新)
5.4.增量,指的是mysql的update、insert、delate变更数据
5.5.读取binlog后分析 ,利用消息队列,推送更新各台的缓存(redis)数据
5.5.1.MySQL中产生了新的写入、更新、删除等操作
5.5.2.就可以把binlog相关的消息推送至缓存(Redis)
5.5.3.缓存(Redis)再根据binlog中的记录,对缓存(Redis)进行更新
5.5.4.这种机制,很类似MySQL的主从备份机制,因为MySQL的主备也是通过binlog来实现的数据一致性
5.6.消息推送工具可以采用第三方:kafka、rabbitMQ等来实现推送更新缓存(Redis)
(4)分布式缓存系统需要注意缓存一致性、缓存穿透和雪崩、缓存数据的清理等问题
(5)为了保证系统的HA,缓存系统可以组合使用两套存储系统(memcache,redis)
5.java缓存种类
在java应用中通常由两类缓存:进程内缓存;进程外缓存
(1)进程内缓存(本地缓存):使用java应用虚拟机内存的缓存
<1>在一个JVM中,快速且可用性高
<2>会存在多态负载均衡主机数据不一致的问题
<3>合最常用且不易变的数据
(2)进程外缓存:现在常用的各种分布式缓存
<1>扩展性强,而且相关的方案多,比如Redis Ehcache 等
<2>通常来说,从数据库读取一条数据需要10ms,从分布式缓存读取则只需要0.5ms左右
<3>本地缓存则只需要10μs