缓存总结
缓存是互联网架构中不可或缺的一环,其重要性不言而喻,咱不说每种缓存的具体实现和原理,只对“如何用”做了一些简单的概况和总结。以redis为例,因具有多样的数据类型支持、高效性以及可持久化被广泛应用。
什么是缓存(cache)?
缓存:存储在计算机上的一个原始数据复制集,以便于访问。多适用于读大于写的场景。
cache VS buffer:
Buffer(缓冲区)是系统两端处理速度平衡时使用的。它的引入是为了减小短期内突发I/O的影响,起到流量整形的作用,
比如地铁限流用的栅栏,java中的BlockingQueue,一般以FIFO的形式存在。也是缓存的一种
Cache(缓存)则是系统两端处理速度不匹配时的一种折衷策略。比如java中的LinkedHashMap,redis实现等,可以多种策略LRU、FIFO、LFU等。
缓存无处不在:
cpu缓存、操作系统缓存、数据库缓存、jvm编译缓存、CDN缓存、代理与反向代理缓存、应用程序缓存、分布式对象缓存等。
缓存的数据结构:hash表,底层是数组来实现。
关键指标:缓存命中率——是否有效依赖于多少次重用同一个缓存响应业务请求,这个度量指标称之为缓存命中率,比如十次查询九次能得到正确结果,命中率为90%
影响缓冲命中率的主要指标:
缓存键集合大小
缓存可使用内存空间
缓存对象生存时间(TTL)
缓存显著提升性能,原因:
数据通常来自内存,比读磁盘访问速度更快
缓存的是最终结果形态,不需要中间计算,减少cpu消耗
降低数据库、磁盘、网络的负载,IO设备能获得更好的响应性能
由于缓存能显著提升性能,所以经常使用,我们在后端业务中经常用到的有分布式缓存:Memcached 和Redis
redis VS Memcached:
redis支持复杂的数据结构
redis支持多路复用异步I/O高性能
redis支持主从复制高可用
redis原生集群与share noth集群模式
为了让数据能找到访问节点,实现的算法不一样:
memcached,主要是用基于虚拟节点的一致性hash算法,维护key和服务器节点的关系,主要是构造一个hash环的策略。
Redis集群,主要是预先分配哈的16384个slot,然后跟进CRC16(key)mod16384的值,决定key放到哪个slot上,cluster负责维护slot与服务器的映射关系。
使用过程中要注意存在:缓存雪崩、缓存穿透、缓存越热等问题,以下为常见的做法供参考:
缓存穿透
最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。另外也有一个更为简单粗暴的方法(我们采用的就是这种),如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。
缓存击穿
业界比较常用的做法,是使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。
缓存雪崩解
考虑用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。还有一个简单方案就时讲缓存失效时间分散开,比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件
二、对redis的简单封装和对springCache的扩展
解决的问题
1、Spring Cache默认是不支持在@Cacheable上添加过期时间。
2、不同集群方式的配置不同,每换一个方式就改一次配置或代码。
3、操作API风格不统一,redis的客户端不统一。
4、分布式锁没有统一的实现,用if else代码编写,不安全。
主要类图
结果
经过扩展后,不需要开发关注太多细节,拿来即用即可,现支持:
1、对外提供统一的配置方式和操作API,由于集群方式不同配置也有差异
2、适用于不同的集群方式(sharding、主从、哨兵、集群等)。
3、对spring注解扩展,缓存失效时间支持在方法的注解上指定。
4、多客户端的支持(jedis、redisson和spring的redisTemplate)。
5、分布式锁的支持,并提供注解式实现。
6、预防缓存穿透
其它使用小技巧
1、服务之间 不要通过缓存传递数据
2、若缓存挂掉,可能导致雪崩,要做高可用或水平切分。
3、不做缓存服务底层的数据,容易出现数据不一致,以及反向依赖。或在删除、更新数据时去删除缓存。
4、不同服务,缓存的实例要做垂直切分,互不干扰。
5、若对数据有强一致性要求,不能放缓存。
欢迎讨论与分享。
评论