缓存详解

1.缓存

缓存,数据交换的缓冲区,针对缓冲对象的不同(不同的硬件)都可以构建缓存。

目的是,把读写速度慢的介质数据保存在读写速度快的介质中,从而提高读写的速度,减少时间消耗。例如:

  • CPU高速缓存:高速缓存的读写速度远高于内存。
    1. CPU读数据时,如在高速缓存中找到所需数据,就不需要读内存
    2. CPU写数据是,先写入到高速缓存中,在回写到内存
  • 磁盘缓存:磁盘缓存其实就把常用的磁盘数据保存在内存中,内存的读写数据也远高于磁盘的
    1. 读数据时从内存中去取
    2. 写数据时,可以先写到内存中,定时或者定量回写到磁盘,或者同步回写。

目的:使用缓存的目的就是为了提升读写性能。在实际的业务场景中就是为了提升读性能,带来更好的性能,更高的并发量

2.缓存算法

  1. LRU(least recentlty used,最近最少使用)算法
  2. LFU(least Frequently used,最不经常使用)
  3. FIFO(first in first out,先进先出)

3.缓存穿透

存储穿透:是指查询一个一定不存在的数据,由于缓存是不命中时被动写,并且处于容错考虑,如果从DB查不到数据就不写入缓存,这将导致这个不存在的数据一直去请求DB,是去缓存的意义。

被动写:当从缓存中查询不到数据的时候,从DB查到数据,然后将数据写入到缓存。

image

如何解决?

  1. 方案一,缓存空对象。

    当从DB查询数据为空的时候,我们仍然将这个结果进行缓存,具体的值需要使用特殊的标识,能和真正的缓存数据区分开。另外需要设置较短的过期时间,不建议超过5分钟。缓存时间太久没意义,浪费缓存内存。

  2. 方案二,BloomFilter布隆过滤器

    在缓存服务的基础上,构建BloomFilter数据结构。在BloomFilter中存储对应的key是否存在,如果存在,再说明该key的值不为空,逻辑如下:

    1. 根据key查询BloomFilter,如果不存在对应的值,直接返回。如果存在,继续向下执行。
    2. 根据key的值,查询缓存的值,如果存在,直接返回,不存在,向下执行。
    3. 查询db的值,如果存在,更新到缓存,直接返回。

    为什么BloomFilter不存储Key是不存在的情况?

    1. BloomFilter存在误判。简单来说,存在的不一定存在,不存在的不一定不存在。一个误判的key就会被误判为不存在。
    2. BloomFilter不允许删除。如果一个key开始不存在,后面又有数据了,这时候会被判断为一致不存在。
    缓存空对象 BloomFilter
    使用场景 1.数据命中率不高;2.保证一致性 1.数据命中不高。2数据相对固定,实时性低
    维护成本 1.代码维护简单;2.需要过多的缓存空间;3.数据不一致 1.代码维护复杂;2.缓存空间占用少

4.缓存雪崩

概念

缓存雪崩,是指缓存由于某些原因无法提供服务(比如缓存挂了),所有的请求到达DB,导致DB负荷增大,最终挂掉的情况。

如何解决

  1. 缓存高可用

    通过搭建缓存的高可用,避免缓存挂掉导致无法提供服务的情况,从而降低出现缓存雪崩的情况。Redis可以通过搭建Redis Sentinel或者Redis Cluster来做缓存高可用

  2. 本地缓存

    使用本地缓存,即使分布式缓存挂了,也可以将DB查询到的数据缓存到本地,避免后续请求全部到达DB。

  3. 请求DB限流

    通过限制DB的每秒请求数,避免DB挂掉,这样做的好处:

    • 有一部分用户任然可以使用该系统。
    • 缓存服务恢复后,立即可以使用。不用再去管理DB服务

    被限流的请求,我们可以通过服务降级,提供一些默认的值,比如空白页面、友情提示等到。我们可以通过Sentinel、Hystrix等来实现。

引入本地缓存的问题

  • 本地缓存实时性如何保证
    • 引入消息队列。在数据更新时,发布数据更新的消息;而进程中有相应的消费者消费该消息,从而更新本地缓存;
    • 设置较短的过期时长,请求从DB拉取数据;
    • 通过手动过期
  • 每个进程可能会在本地缓存相同的数据,导致资源浪费?
    • 需要配置本地的缓存过期策略和缓存数量上限。

5.缓存击穿

概念

缓存击穿,是指在某个热点数据在某个时间点过期的时候,恰好这个时间对这个key有大量的的并发请求过来,这些请求发现缓存过期,都会去请求到DB加载数据并回写到缓存,这个时候过大的并发就有可能瞬间把DB压垮。

如何解决

  1. 方案一,使用互斥锁

    请求发现缓存不存在后,去查询DB前,使用分布式锁,保证只有一个线程拿到锁,然后取请求DB,然后更新写入缓存

    • 1获取分布式锁,直到成功或者超时。如果超时则抛出异常,成功,继续向下执行
    • 2、获取缓存,如果存在值,直接返回,如果不存在,继续。
    • 3、查询DB,更新到缓存,返回值。
  2. 方案二,手动过期

    缓存上不设置过期时间,功能上将过期时间设置在Value中。流程如下

    • 1、获取缓存,通过value中的时间来比较是否过期。如果未过期,直接返回,如果过期,继续向下执行。
    • 2、通过一个后台的异步线程进行缓存的构建,也就是“手动过期”。通过后台的异步线程,保证只有一个线程查询DB。
    • 3、同时,虽然Value还是过期,但还是直接返回。通过这样的方式保证了服务的可用性,但是损失了一定的实效性。

6.缓存与DB一致性保持

产生不一致的原因

  1. 并发场景下,导致老的DB数据写入到缓存中。

    这里指的是,更新DB之前,先删除Cache中的数据。在低并发的情况下,不会出问题,但是在高并发的情况下就会出问题。在删除Cache和更新DB之间,这里恰好有请求进来,这时候使用被动读,因为在DB中的数据还是老数据,这时候又会将老数据写入到缓存中。

  2. 缓存和DB的操作不在同一个事物中,可能DB操作成功,二cache操作失败,这样会导致不一致

解决方案

  1. 将缓存可能出现的并行写,实现串行写

    这里指的缓存并行写。在被动读中,如果缓存不存在,也存在写。

    1. 在写请求前,先淘汰缓存之前,先获取分布式锁。

      写-->获取锁-->delete cache-->update db ---> write cache

    2. 在读请求时,如果缓存不存在,先去获取分布式锁

      读-->读cache,如果null-->获取锁-->查询cache,如果null-->查db-->write cache

  2. 实现缓存的最终一致性。

    1. 先淘汰缓存,在写数据库。

      因为先淘汰缓存,所以数据的最终一致性是可以保证的。为什么尼?先淘汰缓存,即使写数据库发生异常,在下次缓存读取时候,多读取一次数据库。

    2. 先写数据库,在更新缓存

      需要保证写数据库和更新缓存的操作,能够在一个”事务“中,从而实现最终一致性。

      基于定时任务来实现

      • 首先,写入数据库
      • 然后在写入数据库所在的事物中,插入一个记录的任务表,该记录表存储需要存入cache中的key和value
      • 【异步】遍历任务表,写入cache

      基于消息队列来实现

      • 首先,写入数据库
      • 然后发送带我缓存key和value的事物消息。
      • 【异步】消费者消费该消息,写入缓存

      基于数据库的binlog日志

      image
      • 应用直接将数据写入数据库
      • 数据库会更新binlog日志
      • 利用Cannal中间件读取binlog日志
      • Cannal借助于限流主键按频率将数据发送到MQ中
      • 应用监控MQ渠道,将MQ的数据缓存到cache中

7.缓存预热

启动时,先将热点数据缓存到缓存中

如何实现

  1. 数据量不大的时候,项目启动,自动进行初始化
  2. 通过修复脚本,执行脚本
  3. 写管理页面,手动操作

7.缓存淘汰策略

  1. 定时清理
  2. 当用户请求过来的时候,去判断是否过期,过期的话就去底层系统获取数据,进行更新

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,039评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,426评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,417评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,868评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,892评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,692评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,416评论 3 419
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,326评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,782评论 1 316
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,957评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,102评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,790评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,442评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,996评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,113评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,332评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,044评论 2 355

推荐阅读更多精彩内容