写在前面

周末,跟阿里的一个朋友(去年晋升为P9了)聊了很久,聊的内容几乎全是技术,当然了,两个技术男聊得最多的话题当然就是技术了。从基础到架构,从算法到AI,无所不谈。中间又穿插着不少天马行空的想象,虽然现在看起来不太实际,但是随着技术的进步,相信五年、十年之后都会实现的。

不知道是谁提起了在高并发环境下如何构建缓存服务,结果一路停不下来了!!

缓存特征

(1)命中率:命中数/(命中数+没有命中数)

(2)最大元素(空间):代表缓存中可以存放的最大元素的数量,一旦缓存中元素的数量超过这个值,或者缓存数据所占的空间超过了最大支持的空间,将会触发缓存清空策略。根据不同的场景,合理设置最大元素(空间)的值,在一定程度上可以提高缓存的命中率,从而更有效的使用缓存。

(3)清空策略:FINO(先进先出)、LFU(最少使用)、LRU(最近最少使用)、过期时间、随机等。

  • FINO(先进先出):最先进入缓存的数据,在缓存空间不够或超出最大元素限制的情况下,会优先被清除掉,以腾出新的空间来接收新的数据。这种策略的算法主要是比较缓存元素的创建时间,在数据实时性较高的场景下,可以选择这种策略,优先保证最新策略可用。
  • LFU(最少使用):无论元素是否过期,根据元素的被使用次数来判断,清除使用次数最少的元素来释放空间。算法主要是比较元素的命中次数,在保证高频数据有效的场景下,可以选择这种策略。
  • LRU(最近最少使用):无论元素是否过期,根据元素最后一次被使用的时间戳,清除最远使用时间戳的元素,释放空间。算法主要是比较元素最近一次被获取的时间,在热点数据场景下,可以选择这种策略。

    过期时间:根据过期时间判断,清理过期时间最长的元素,或者清理最近要过期的元素。

缓存命中率影响因素

(1)业务场景和业务需求

缓存往往适合读多写少的场景。业务需求对实时性的要求,直接会影响到缓存的过期时间和更新策略。实时性要求越低,就越适合缓存。在相同Key和相同请求数的情况下,缓存的时间越长,命中率就会越高。

(2)缓存的设计(粒度和策略)

通常情况下,缓存的粒度越小,命中率越高。缓存的更新和命中策略也会影响缓存的命中率,当数据发生变化时,直接更新缓存的值会比移除缓存或使缓存过期的命中率更高。

(3)缓存容量和基础设施

缓存的容量有限,则容易引起缓存失效和被淘汰(目前多数的缓存框架或中间件都采用了LRU算法)。同时,缓存的技术选型也是至关重要的,比如采用应用内置的本地缓存就比较容易出现单机瓶颈,而采用分布式缓存则毕竟容易扩展。所以需要做好系统容量规划,并考虑是否可扩展。此外,不同的缓存框架或中间件,其效率和稳定性也是存在差异的。

(4)其他因素

当缓存节点发生故障时,需要避免缓存失效并最大程度降低影响,这种特殊情况也是架构师需要考虑的。业内比较典型的做法就是通过一致性Hash算法,或者通过节点冗余的方式。

有些朋友可能会有这样的理解误区:既然业务需求对数据时效性要求很高,而缓存时间又会影响到缓存命中率,那么系统就别使用缓存了。其实这忽略了一个重要因素--并发。通常来讲,在相同缓存时间和key的情况下,并发越高,缓存的收益会越高,即便缓存时间很短。

提高缓存命中率的方法

从架构师的角度,需要应用尽可能的通过缓存直接获取数据,并避免缓存失效。这也是比较考验架构师能力的,需要在业务需求,缓存粒度,缓存策略,技术选型等各个方面去通盘考虑并做权衡。尽可能的聚焦在高频访问且时效性要求不高的热点业务上,通过缓存预加载(预热)、增加存储容量、调整缓存粒度、更新缓存等手段来提高命中率。

对于时效性很高(或缓存空间有限),内容跨度很大(或访问很随机),并且访问量不高的应用来说缓存命中率可能长期很低,可能预热后的缓存还没来得被访问就已经过期了。

缓存的分类和应用场景

(1)本地缓存:编程实现(成员变量、局部变量、静态变量)、Guava Cache

(2)分布式缓存:Memcached、Redis

高并发场景下缓存常见问题

(1)缓存的一致性

更新数据库成功——更新缓存失败

更新缓存成功——更新数据库失败

更新数据库成功——淘汰缓存失败

淘汰缓存成功——更新数据库失败

(2)缓存并发

并发时请求缓存时已过期或者没有命中或者更新的情况下有大量的请求访问数据库。

解决办法:在缓存更新或者过期的情况下,先尝试获取到lock,当更新完成后,尝试释放锁,其他的请求只需要牺牲一定的等待时间

(3)缓存穿透

在高并发的场景下,如果某一个key被高并发的访问没有被命中,出于对容错性的考虑会尝试从后端的数据库获取,从而导致大量的请求访问了数据库,主要是当key对应的数据为空或者为null的情况下,这就导致数据库中并发的执行了很多不必要的查询操作。从而导致了巨大的冲击和压力。

解决方法:

缓存空对象:对查询结果为空的对象也进行缓存,如果是集合可以缓存一个空的集合,而不是null,如果是单个对象可以通过字段标识来区分,需要保证缓存数据的时效性(实现相对简单),适合命中不高但可能会频繁更新的数据。

单独过滤处理:对所有可能对应数据为空的key进行统一的存放,并在请求前做拦截(实现相对复杂),适合命中不高更新不频繁的数据

(4)缓存颠簸问题

缓存的颠簸问题,有些地方可能被称为“缓存抖动”,可以看作是一种比“雪崩”更轻微的故障,但是也会在一段时间内对系统造成冲击和性能影响。一般是由于缓存节点故障导致。业内推荐的做法是通过一致性Hash算法来解决。

(5)缓存雪崩现象

缓存雪崩就是指由于缓存的原因,导致大量请求到达后端数据库,从而导致数据库崩溃,整个系统崩溃,发生灾难。导致这种现象的原因有很多种,上面提到的“缓存并发”,“缓存穿透”,“缓存颠簸”等问题,其实都可能会导致缓存雪崩现象发生。这些问题也可能会被恶意攻击者所利用。还有一种情况,例如某个时间点内,系统预加载的缓存周期性集中失效了,也可能会导致雪崩。为了避免这种周期性失效,可以通过设置不同的过期时间,来错开缓存过期,从而避免缓存集中失效。

从应用架构角度,我们可以通过限流、降级、熔断等手段来降低影响,也可以通过多级缓存来避免这种灾难。

此外,从整个研发体系流程的角度,应该加强压力测试,尽量模拟真实场景,尽早的暴露问题从而防范。

(6)缓存无底洞现象

该问题由 facebook 的工作人员提出的, facebook 在 2010 年左右,memcached 节点就已经达3000 个,缓存数千 G 内容。他们发现了一个问题---memcached 连接频率,效率下降了,于是加 memcached 节点,添加了后,发现因为连接频率导致的问题,仍然存在,并没有好转,称之为”无底洞现象”

重磅福利

关注「 冰河技术 」微信公众号,后台回复 “设计模式” 关键字领取《深入浅出Java 23种设计模式》PDF文档。回复“Java8”关键字领取《Java8新特性教程》PDF文档。两本PDF均是由冰河原创并整理的超硬核教程,面试必备!!

好了,今天就聊到这儿吧!别忘了点个赞,给个在看和转发,让更多的人看到,一起学习,一起进步!!

写在最后

如果你觉得冰河写的还不错,请微信搜索并关注「 冰河技术 」微信公众号,跟冰河学习高并发、分布式、微服务、大数据、互联网和云原生技术,「 冰河技术 」微信公众号更新了大量技术专题,每一篇技术文章干货满满!不少读者已经通过阅读「 冰河技术 」微信公众号文章,吊打面试官,成功跳槽到大厂;也有不少读者实现了技术上的飞跃,成为公司的技术骨干!如果你也想像他们一样提升自己的能力,实现技术能力的飞跃,进大厂,升职加薪,那就关注「 冰河技术 」微信公众号吧,每天更新超硬核技术干货,让你对如何提升技术能力不再迷茫!

【高并发】高并发环境下构建缓存服务需要注意哪些问题?我和阿里P9聊了很久!的更多相关文章

  1. .Net环境下的缓存技术介绍 (转)

    .Net环境下的缓存技术介绍 (转) 摘要:介绍缓存的基本概念和常用的缓存技术,给出了各种技术的实现机制的简单介绍和适用范围说明,以及设计缓存方案应该考虑的问题(共17页) 1         概念 ...

  2. .Net环境下的缓存技术介绍

    .Net环境下的缓存技术介绍 摘要: 介绍缓存的基本概念和常用的缓存技术,给出了各种技术的实现机制的简单介绍和适用范围说明,以及设计缓存方案应该考虑的问题(共17页) 1         概念 1.1 ...

  3. 在Windows环境下构建Lua 入门

    在Windows环境下构建Lua 一:准备软件 1.C-compiler(TDM GCC)  http://tdm-gcc.tdragon.net/download 2.Lua源代码  http:// ...

  4. 在Mac OS环境下安装MySQL服务

    在Mac OS环境下安装MySQL服务 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 我之前介绍过window环境下安装mysql服务,以及在Linux环境下安装mysql服务,今 ...

  5. 在高通平台Android环境下编译内核模块【转】

    本文转载自:http://blog.xeonxu.info/blog/2012/12/04/zai-gao-tong-ping-tai-androidhuan-jing-xia-bian-yi-nei ...

  6. SpringCloud的入门学习之Eureka(高可用注册中心HA)构建Provider服务、Consumer服务

    1.在高可用的Eureka注册中心中构建provider服务. 使用springboot的多环境配置,来搭建Eureka的高可用集群式部署.由于使用的是maven构建的springboot项目,所以首 ...

  7. .Net Core 环境下构建强大且易用的规则引擎

    本文源码: https://github.com/jonechenug/ZHS.Nrules.Sample 1. 引言 1.1 为什么需要规则引擎 在业务的早期时代,也许使用硬编码或者逻辑判断就可以满 ...

  8. ubuntu 14.04LTS 环境下配置NFS服务

    简言之,NFS(Network FileSystem,网络文件系统)用于在不同机器,不同操作系统之间通过网络互相分享各自的文件.NFS设计之初就是为了在不同的系统间使用,所以它的通讯协议设计与主机及操 ...

  9. 【swoole】如何在docker+swoole环境下测试udp服务

    前面几篇文章讲了使用 docker+swoole 环境来测试tcp服务以及如何测试,但是当我开始学习udp服务那块的时候,发现使用原来的方式在 docker+swoole 环境下行不通啦,后来发现如果 ...

随机推荐

  1. 【UVA11383】 Golden Tiger Claw 【二分图KM算法(板子)】

    题目 题目传送门:https://www.luogu.com.cn/problem/UVA11383 分析 最近刚刚学了二分图,然后来了一个这样的题,看完题意之后,稍微想一想就能想出来是一个二分图,然 ...

  2. QtableWidget用法流程

    QtableWidget用法流程 ​ 作者:流火 日期:2020/5/10 QTableWidget的基本构造函数 QTableWidget 是QTableview的子类.主要去呗是QTableVie ...

  3. C#状态机Stateless

    最近在折腾一些控制相关的软件设计,想起来状态机这个东西,对解决一些控制系统状态切换还是挺有用的. 状态机(有限状态自动机)网上有很多介绍.简单理解就是定义一系列状态,通过一系列的事件,可以使得状态可以 ...

  4. 03 . Shell数组和函数

    Shell数组 简介 数组中可以存放多个值.Bash Shell 只支持一维数组(不支持多维数组),初始化时不需要定义数组大小(与 PHP 类似). 与大部分编程语言类似,数组元素的下标由0开始. S ...

  5. (四)ansible 通过堡垒机访问内网服务器

    场景:     在ansible的使用过程中,存在这样的场景,ansible所在的管理节点与被管理的机器需要 通过一个跳板机才能连接,无法直接连接.要解决这个问题,并不需要在 ansible里做什么处 ...

  6. JavaScript图形实例:Hilbert曲线

    德国数学家David Hilbert在1891年构造了一种曲线,首先把一个正方形等分成四个小正方形,依次从西北角的正方形中心出发往南到西南正方形中心,再往东到东南角的正方形中心,再往北到东北角正方形中 ...

  7. @Autowired 引发的一系列思考

    关于Java注解 注解定义 标记注解 - 没有元素 @interface Marker { } 单元素注解 - 只有一个元素 @interface Single { String value() de ...

  8. [Mybatis]Mybatis常用操作

    Mybatis是目前国内比较流行的ORM框架,特点是可以写灵活的SQL语句,非常适合中小企业的面向数据库开发. 本文总结自己开发过程中常用的Mybatis操作. 一.插入操作 主键自增插入单条 < ...

  9. day39 作业

    实现生产消费原理 from multiprocessing import Process,JoinableQueue import time import random def cooker(q): ...

  10. TB6560电机驱动器参数设置

    TB6560电机驱动器参数设置 最近接触了一些步进电机的开发,整理了一些参数设置的经验,希望能帮助到有需要的人儿~ 步进电机主要按一定的给电规律,给对应的绕组响应的电信号,电机将按一定的方向运行,而且 ...