浅谈缓存系统的三个问题

一.无处不在的缓存
缓存在计算机系统是无处不在,在CPU层面有L1-L3的Cache,在Linux中有TLB加速虚拟地址和物理地址的转换,在应用层有Redis等内存数据库缓存、在浏览器有本地缓存、手机有本地文件缓存等等。
可见,缓存在计算机系统中有非常重要的地位,主要作用就是提高响应速度、减少磁盘读取等,本文主要讨论在高并发系统中的缓存系统。一句话概括缓存系统在高并发系统中的地位的话,就是:

如果高并发系统是烤羊肉串,那么缓存系统就是那一撮孜然......

二.高并发系统中的缓存

  • 缓存系统的作用

缓存系统在高并发系统的作用巨大,没有缓存系统很难支撑C50K(或许这个值已经非常乐观了)以上的场景。
基于机械磁盘或SSD的数据库系统,读写的速度远慢于内存,因此单纯磁盘介质的数据库无法支撑高并发,你可以认为缓存就是为了保护磁盘数据库、是磁盘数据库的屏障。

  • 缓存系统和数据库系统的访问

以读多写少的场景为例(实际场景也是读多写少),看看请求是如何得到响应的,简单流程:请求到达之后,业务线程首先访问缓存,如果缓存命中则返回,如果未命中则继续请求磁盘数据库系统,并将结果回写到缓存系统且增加老化时间,这样在超时时间内再有相同请求到达时则可以命中缓存。以上是高并发系统中缓存和磁盘数据库系统、客户端请求之间的交互过程,后续的问题分析,也是基于此过程展开的。

三.缓存系统的三大问题
网络上对于缓存三大问题的文章很多,提到的三个问题主要是:

  1. 缓存雪崩 Cache Avalanche
  2. 缓存穿透 Cache Penetration
  3. 缓存击穿 Hotspot Invalid

对于上面的三个名词我一直分不清楚,脑海中并没有清晰的区别,于是想到去谷歌看看歪果仁是怎么说的,然而英文表述就是上面的英文,基本上和汉语翻译是一样的,所以只能强记了。

A.缓存雪崩问题

所谓雪崩就是原来有所支撑的冰雪,某一瞬间失去依托,瞬间涌下来。这个场景让我想起了2011年上映的柯南剧场版《沉默的十五分钟》,柯南在北泽村水库为了拯救村庄制造的雪崩:

注:图为柯南被雪崩所埋,黄金抢救期时毛利兰在寻找他的镜头

可见雪崩确实很可怕,回到高并发系统,如果缓存系统故障,大量的请求无法从缓存完成数据请求,因此就全量汹涌冲向磁盘数据库系统,导致数据库被打死,整个系统彻底崩溃。

缓存雪崩解决方案

从原因来看主要是缓存系统不够高可用,因此提高缓存系统的稳定性和可用性十分必要,对于使用Redis作为缓存的系统而言需要使用Sentinel哨兵机制、集群化、持久化等来提高缓存系统的HA。

另一方面除了保证缓存系统的HA之外,服务本身也需要支持降级,可以使用奈飞的Hystrix来实现服务的熔断、降级、限流来降低出现雪崩时的故障程度。说白了就是别让服务彻底死掉就行,就像大雪封高速肯定彻底不能通行了,堵车就是慢一些至少可以走,如果还不清楚,回想一下每年你回乡的车票是怎么从12306抢回来的。

B.缓存穿透问题

穿透形象一点就是:请求过来了 转了一圈 一无所获 就像穿过透明地带一样。

在高并发系统中缓存穿透,其实是这样的如果一个req需要请求的key在缓存中没有,这时业务线程就会访问磁盘数据库系统,然而磁盘数据库也没有这个key,无奈业务线程只能返回null,白白处理一圈。
小概率事件在高并发系统几乎要成为必然,也就是如果某时段有大量恶意的不存在的key的爆破请求,那么服务将一直处理这些根本不存在的请求,导致正常请求无法被处理,从而出现问题。
举个栗子:

拉面馆的服务员和厨师不允许拒绝已经进来的消费者,但是拉面馆的经营范围有限,此时恶意消费者点了一只5斤的澳洲龙虾,经过服务员和厨师都无法响应这个需求,因此被最终被拒绝,此时轮流来了1000个这样的恶意消费者,拉面馆基本要歇菜了。

缓存穿透解决方案

高并发系统也是如此,有效甄别是否存在这个key再决定是否读取很重要,常见的做法有:

a.把不存在的key写一下null,这样再来就相当于命中了,其实这种方法局限性很大,今天是5斤龙虾,明天改成6斤的螃蟹,缓存系统和数据库中存储大量无用key本身是无意义的,所以一般不建议。
 b.另外一种思路,转换为查找问题,类似于在海量数据中查找某个key是否存在,考虑空间复杂度和时间复杂度,一般选用布隆过滤器来实现。

缓布隆过滤器简介

布隆过滤器是个好东西,在1970年由布隆提出的,它实际上是一个很长的二进制向量和一系列随机映射函数。

布隆可以实现的系统包括:垃圾邮件识别、搜索蜘蛛爬虫url去重等,主要借助K个哈希函数和一个超大的bit数组来降低哈希冲突本身带来的误判,从而提高识别准确性。
布隆过滤器也存在一定的误判,假如判断存在可能不一定存在,但是假如判断不存在就一定不存在,因此刚好用在解决缓存穿透的key查找场景,事实上很多系统都是基于布隆过滤器来解决缓存穿透问题的。

C.缓存击穿问题

缓存击穿是这样一种情况:由于缓存系统中的热点数据都有过期时间,如果没有过期时间就造成了主存和缓存的数据不一致,因此过期时间一般都不会太长,设想某时刻一批热点数据同时在缓存系统中过期失效,那么这部分数据就都将请求磁盘数据库系统。
从描述上来看有点像微小规模的雪崩,但是对数据库的压力就很小了,只不过会影响并发性能,然而在多线程场景中缓存击穿却是经常发生的,相反缓存穿透和雪崩频率不如缓存击穿,因此研究击穿的现实意义更大一些

缓存击穿解决方案

可以采用的方案大概有几种:

a. 在设置热点数据过期时间时尽量分散,比如设置100ms的基础值,在此基础上正负浮动10ms,从而降低相同时刻出现CacheMiss的key的数量。
b. 另外一种做法是多线程加锁,其中第一个线程发现CacheMiss之后进行加锁,再从数据库获取内容之后写到缓存中,其他线程获取锁失败则阻塞数ms之后再进行缓存读取,这样可以降低访问数据数据库的线程数,需要注意在单机和集群需要使用不同的锁,集群环境使用分布式锁来实现,但是由于锁的存在也会影响并发效率。
c.还有一种办法是使用类似于Redis的SETNX命令,这种貌似存在问题,存在一个gap间隙请求处于无法从缓存获取数据也无法从主存获取数据的未决状态。
d. 最后一种方法是在业务层对使用的热点数据查看是否即将过期,如果即将过期则去数据库获取最新数据进行更新并延长该热点key在缓存系统中的时间,从而避免后面的过期CacheMiss,相当于把事情提前解决了。

缓存击穿的解决方法都有一定的权衡,实际中根据自己的需求来解决,不过缓存击穿的影响一般来说并不会太大,或许在你的服务跑了很久之后你才意识到会有缓存击穿问题。

原文地址:https://www.cnblogs.com/backnullptr/p/11993876.html

时间: 2024-10-15 22:07:26

浅谈缓存系统的三个问题的相关文章

浅谈缓存写法(三):内存缓存该如何设计

分析设计 假设有个项目有比较高的并发量,要用到多级缓存,如下: 在实际设计一个内存缓存前,需要考虑的问题: 1:内存与Redis的数据置换,尽可能在内存中提高数据命中率,减少下一级的压力. 2:内存容量的限制,需要控制缓存数量. 3:热点数据更新不同,需要可配置单个key过期时间. 4:良好的缓存过期删除策略. 5:缓存数据结构的复杂度尽可能的低. 关于置换及命中率:采用LRU算法,因为它实现简单,缓存key命中率也很好. LRU即是:把最近最少访问的数据给淘汰掉,经常被访问到即是热点数据. 关

浅谈单例的三种实现--C#

传统的double check : public sealed class Singleton { private static Singleton instance = null; private static readonly object padlock = new object(); Singleton() { } public static Singleton Instance { get { if (instance == null) { lock (padlock) { if (i

浅谈Linux系统中的7种运行级别及其原理

浅谈Linux系统中的7种运行级别 Linux系统7个运行级别(runlevel): 运行级别0: 系统停机状态,系统默认运行级别不能设为0,否则不能正常启动. 运行级别1: 单用户工作状态,root权限,用于系统维护,禁止远程登陆. 运行级别2: 多用户状态(没有NFS). 运行级别3: 完全的多用户状态(有NFS),登陆后进入控制台命令行模式. 运行级别4: 系统未使用,保留. 运行级别5: X11控制台,登陆后进入图形GUI模式. 运行级别6: 系统正常关闭并重启,默认运行级别不能设为6,

【ASP.NET 系列】浅谈缓存技术在ASP.NET中的运用

本篇文章虽不谈架构,但是Cache又是架构中不可或缺的部分,因此,在讲解Cache的同时,将会提及到部分架构知识,关于架构部分,读者可以不用理解,或者直接跳过涉及架构部分的内容, 你只需关心Cache即可,具体的架构,会在后续文章中与大家分享,如果你感兴趣,只需关注即可. 一   为什么要在ASP.NET 项目中引入缓存 1. 我们先来考虑一个问题,通常,面临高并发问题时,我们应该怎么处理? 下图为常规的处理思路和方法 2.为什么引入Cache呢? 我们知道,造成高并发的根本原因是大量读写的问题

浅谈Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service Manager接口之路

文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6627260 在前面一篇文章浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路中,介绍了Service Manager是如何成为Binder机制的守护进程的.既然作为守护进程,Service Manager的职责当然就是为Server和Client服务了.那么,Server和Client如何获得S

浅谈缓存写法(一):缓存的雪崩和穿透

基本写法 为了方便演示,这里使用Runtime.Cache做缓存容器,并定义个简单操作类.如下: <pre class="brush:csharp;gutter:true;" style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word;"> public class CacheHelper { public static object Get(str

&lt;转&gt;浅谈缓存击穿、缓存并发和缓存失效

原文地址:缓存穿透.缓存并发.缓存失效之思路变迁 我们在用缓存的时候,不管是Redis或者Memcached,基本上会通用遇到以下三个问题: 缓存穿透 缓存并发 缓存失效 一.缓存穿透 注:上面三个图会有什么问题呢? 我们在项目中使用缓存通常都是先检查缓存中是否存在,如果存在直接返回缓存内容,如果不存在就直接查询数据库然后再缓存查询结果返回. 这个时候如果我们查询的某一个数据在缓存中一直不存在,就会造成每一次请求都查询DB,这样缓存就失去了意义,在流量大时,可能DB就挂掉了. 那这种问题有什么好

浅谈win7系统压缩包出错处理及提高启动速度的方法

有时候,客户需从网上下载一些软件举办安装时,网上很多软件都是压缩包名目标,必要解压后才气安装,然则有些客户会碰着压缩文件打不开的妨碍,呈现压缩错误或无法成立文件夹的妨碍,怎样办呢?Win7纯净版系统性能稳定,功能强大,占用资源也比较多,当客户电脑配置较低的时候,启动速度就会偏慢,其实是大概通过设置的方案提高一下WIN7系统启动速度, 一,原因阐发: 第一.电脑中病毒,全部exe文件受传染导致的打不开. 第二.你的压缩软件不支撑所压缩的算法,一样平常来说,此刻WinRAR软件行使的压缩算法都比较先

浅谈Android系统的图标设计规范

http://homepage.yesky.com/89/11620089.shtml 目前移动平台的竞争日益激烈,友好的用户界面可以帮助提高用户体验满意度,图标Icon是用户界面中一个重要的组成部分,今天我们来研究和学习一下Android系统的图标设计规范.在探讨Android的icon设计之前,我们有必要先了解Android的界面是如何适配多样化屏幕的. 适配性 由于同一个UI元素(如100 x100像素的图片)在高精度的屏幕上要比低精度的屏幕上看起来要小,为了让这两个屏幕上的图片看起来效果