zoukankan      html  css  js  c++  java
  • 缓存使用需要考虑的一些细节

    一、数据库与缓存一致性

    使用缓存,可以降低耗时,提供系统吞吐性能。但是,使用缓存,会存在数据一致性的问题。

    1、几种缓存使用模式

    • Cache-Aside Pattern,旁路缓存模式
    • Read-Through/Write-Through(读写穿透)
    • Write- behind (异步缓存写入)

    一般我们使用缓存,都是旁路缓存模式,读请求流程如下:

    • 读的时候,先读缓存,缓存命中的话,直接返回数据;
    • 缓存没有命中的话,就去读数据库,从数据库取出数据,放入缓存后,同时返回响应。

    旁路缓存模式的写流程:

    2、删除缓存呢,还是更新缓存?

    我们在操作缓存的时候,到底应该删除缓存还是更新缓存呢?我们先来看个例子:

    • 线程A先发起一个写操作,第一步先更新数据库;
    • 线程B再发起一个写操作,第二步更新了数据库;
    • 由于网络等原因,线程B先更新了缓存;
    • 线程A更新缓存。

    这时候,缓存保存的是A的数据(老数据),数据库保存的是B的数据(新数据),数据不一致了,脏数据出现啦。如果是删除缓存取代更新缓存则不会出现这个脏数据问题。

    3、先操作数据库还是先操作缓存

    双写的情况下,先操作数据库还是先操作缓存?我们再来看一个例子:假设有A、B两个请求,请求A做更新操作,请求B做查询读取操作。

    • 线程A发起一个写操作,第一步del cache;
    • 此时线程B发起一个读操作,cache miss;
    • 线程B继续读DB,读出来一个老数据;
    • 然后线程B把老数据设置入cache;
    • 线程A写入DB最新的数据;

    酱紫就有问题啦,缓存和数据库的数据不一致了。缓存保存的是老数据,数据库保存的是新数据。因此,Cache-Aside缓存模式,选择了先操作数据库而不是先操作缓存。

    4、如何保证最终一致性

    • 缓存延时双删
    • 删除缓存重试机制
    • 读取biglog异步删除缓存

    二、缓存穿透

    1、原理

    缓存穿透`:指查询一个一定不存在的数据,由于缓存不命中时,需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,进而给数据库带来压力。”

    缓存穿透一般都是这几种情况产生的:业务不合理的设计、业务/运维/开发失误的操作、黑客非法请求攻击。如何避免缓存穿透呢?

    2、解决办法

    一般有三种方法。

    • 如果是非法请求,我们在API入口,对参数进行校验,过滤非法值。

    • 如果查询数据库为空,我们可以给缓存设置个空值,或者默认值。但是如有有写请求进来的话,需要更新缓存哈,以保证缓存一致性,同时,最后给缓存设置适当的过期时间。(业务上比较常用,简单有效)

    • 使用布隆过滤器快速判断数据是否存在。即一个查询请求过来时,先通过布隆过滤器判断值是否存在,存在才继续往下查。


    三、缓存雪崩

    1、原理

    缓存雪崩:指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。”

    2、解决办法

    缓存雪奔一般是由于大量数据同时过期造成的,对于这个原因,可通过均匀设置过期时间解决,即让过期时间相对离散一点。如采用一个较大固定值+一个较小的随机值,5小时+0到1800秒酱紫。

    Redis 故障宕机也可能引起缓存雪奔。这就需要构造Redis高可用集群啦。


    四、缓存机击穿

    1、原理

    缓存击穿:指热点key在某个时间点过期的时候,而恰好在这个时间点对这个Key有大量的并发请求过来,从而大量的请求打到db。”

    缓存击穿看着有点像缓存雪崩,其实它两区别是,缓存雪奔是指数据库压力过大甚至down机,缓存击穿只是大量并发请求到了DB数据库层面。可以认为击穿是缓存雪奔的一个子集吧。有些文章认为它俩区别,是在于击穿针对某一热点key缓存,雪奔则是很多key。

    2、解决方法

    解决方案就有两种:

    • 使用互斥锁方案。缓存失效时,不是立即去加载db数据,而是先使用某些带成功返回的原子操作命令,如(Redis的setnx)去操作,成功的时候,再去加载db数据库数据和设置缓存。否则就去重试获取缓存。
    • “永不过期”。是指没有设置过期时间,但是热点数据快要过期时,异步线程去更新和设置过期时间。

    五、缓存热Key

    1、原理

    在Redis中,我们把访问频率高的key,称为热点key。如果某一热点key的请求到服务器主机时,由于请求量特别大,可能会导致主机资源不足,甚至宕机,从而影响正常的服务。

    2、解决方法

    如何解决热key问题?

    • Redis集群扩容:增加分片副本,均衡读流量;
    • 对热key进行hash散列,比如将一个key备份为key1,key2……keyN,同样的数据N个备份,N个备份分布到不同分片,访问时可随机访问N个备份中的一个,进一步分担读流量;
    • 使用二级缓存,即JVM本地缓存,减少Redis的读请求。

    六、缓存容量内存考虑

    1、评估容量,合理利用

    如果我们使用的是Redis,而Redis的内存是比较昂贵的,我们不要什么数据都往Redis里面塞,一般Redis只缓存查询比较频繁的数据。同时,我们要合理评估Redis的容量,也避免频繁set覆盖,导致设置了过期时间的key失效。

    如果我们使用的是本地缓存,如guava的本地缓存,也要评估下容量。避免容量不够。

    2、Redis的八种内存淘汰机制

    为了避免Redis内存不够用,Redis用8种内存淘汰策略保护自己~

    • volatile-lru:当内存不足以容纳新写入数据时,从设置了过期时间的key中使用LRU(最近最少使用)算法进行淘汰;
    • allkeys-lru:当内存不足以容纳新写入数据时,从所有key中使用LRU(最近最少使用)算法进行淘汰。
    • volatile-lfu:4.0版本新增,当内存不足以容纳新写入数据时,在过期的key中,使用LFU算法进行删除key。
    • allkeys-lfu:4.0版本新增,当内存不足以容纳新写入数据时,从所有key中使用LFU算法进行淘汰;
    • volatile-random:当内存不足以容纳新写入数据时,从设置了过期时间的key中,随机淘汰数据。
    • allkeys-random:当内存不足以容纳新写入数据时,从所有key中随机淘汰数据。
    • volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的key中,根据过期时间进行淘汰,越早过期的优先被淘汰;
    • noeviction:默认策略,当内存不足以容纳新写入数据时,新写入操作会报错。

    3、不同的业务场景,Redis选择适合的数据结构**

    • 排行榜适合用zset
    • 缓存用户信息一般用hash
    • 消息队列,文章列表适用用list
    • 用户标签、社交需求一般用set
    • 计数器、分布式锁等一般用String类型


    转载

    化解日常Bug的50个大法(覆盖数据库、代码层面、缓存)



  • 相关阅读:
    11. MVC 开发模式 -- JSP篇
    10. JSTL格式化标签
    JQUREY 的 表单序列化 和 .$.getScript () 和 $.getJSON() 方法!
    jQery 与 AXAJ -- 书本进阶【主要讲解方法 详解】
    python少儿编程-turtle 基本绘图
    mysql按月进行表分区
    Mysql分区:分区键和唯一索引主键的关系
    Mysql自动按月分区
    MySQL分区表的正确使用方法
    sqoop定时增量导入
  • 原文地址:https://www.cnblogs.com/qdhxhz/p/15452083.html
Copyright © 2011-2022 走看看