一、内存、缓存、寄存器
1. CPU <===> 寄存器 <===> 缓存 <===> 内存
寄存器:cpu中的一部分,cpu直接拿寄存器中的数据,包含:指令寄存器、程序计数器。寄存器从缓存或者内存中获取数据。
缓存:直接从内存拿数据速度很慢,缓存那些经常读取且不经常修改的数据,让数据更接近使用者。缓存命中率越高越好。
一级缓存:内置在cpu内部,与cpu同速运行(但是在同步内存中的数据时,会对这个cache加锁,所以没寄存器快)
二级缓存和三级缓存:只能存储数据,不能存储原始指令。内存变大,速度变慢。
二、缓存算法:缓存放满了之后的淘汰策略
FIFO:先进先出。移除最早放进来的。 缺:被置换的数据往往是被频繁访问的,所以不用这个。
LFU(Least Frequently Used):最近最少使用。移除一段时间内用的最少的. 需要维护访问频率:用小根堆+哈希表可以实现
LRU(Least Recently Used):移除最久未使用的(认为它在将来被访问的概率也很小,提高对热点数据的缓存效率)。 Redis、Memcached等分布式缓存系统有广泛使用?
双向链表+哈希表实现:链表表示热点顺序、哈希表用来存储和查找
继承LinkedHashMap可快速实现。 accessOrder: true基于访问顺序;false基于插入顺序(默认)
三、spring的缓存
1. spring的cache是作用在方法上的:key是方法的入参,value是返回值。
2. Cache接口: ConcurrentMapCache: 使用java.util.concurrent.ConcurrentHashMap实现的Cache;
GuavaCache: 使用google的Guava做数据存储结构;
EhCacheCache:使用Ehcache实现;
3. 用CacheManager接口管理各个缓存
4. @Cacheable注解:给部分高频查询加了缓存(查业务线、话术配置、用uid查客服信息等),如果加在类上表示所有方法都支持缓存。 @Cacheable(cacheManager = String, value = String, key = String)
四、缓存击穿、穿透、雪崩
1、穿透
原因:恶意大量请求一定不存在的数据,导致每次都查db。
解决:缓存一个过期时间短的空值; 布隆过滤器:把所有可能存在的数据存起来,类似一个不怎么精确的set,一定包含了所有的key。
2、击穿
原因:单个key的高并发,在缓存失效的瞬间打爆db。(一个点被高频击破:如电商的爆款)
解决:热点数据设置永不过期(都能缓存击穿了还不设置永久!); 使用互斥锁:setNX设置一个key当互斥锁,如果操作成功则loadDB否则休眠重试。
3、雪崩
原因:多个key高并发且同时怼到DB上,导致数据库异常。
解决:1.缓存同时大面积失效导致雪崩:缓存的失效时间加随机值;高频热点数据如果可以的话不设置过期时间;
2.redis炸了导致雪崩: 事前:对redis做高可用:主从+哨兵、redis cluster // todo
事中:hystrix 限流&降级,保护mysql不挂
事后:redis持久化做数据恢复
昵称多语言,存取redis时,因为key的原因发生了雪崩