缓存架构笔记

多级缓存系统

->缓存概述:
  缓存是分布式系统中的重要组件;
  主要解决:
    高并发(大数据场景中的热点数据访问的性能问题)
  ->缓存的原理:
    将数据缓存到写入/读取速度更快的存储(设备);
    将数据缓存到离应用最近的位置;
    将数据缓存到离用户最近的位置。
  ->缓存的分类:(从部署角度有以下几个方面的缓存应用)
    CDN缓存;
    反向代理缓存;
    分布式Cache;
    本地应用缓存。
  ->缓存媒介:
    常用中间件:Nginx, Redis, Memcache等
    缓存的内容:文件、数据、对象;
    缓存的介质:CPU、内存(本地,分布式)、磁盘(本地,分布式)
  ->缓存设计:
    缓存什么:(哪些数据需要缓存)
      热点数据;
      静态资源。
    缓存的位置:
      CDN、反向代理、分布式缓存服务器、本机(内存、硬盘)
    如何进行缓存:
      过期策略;
      固定时间:比如指定缓存的时间是30分钟;
      相对时间:比如最近10分钟内没有访问的数据;
      同步机制;
      实时写入;(推)
      异步刷新。(推拉)
->CDN缓存:(主要解决将数据缓存到离用户最近的位置的问题,一般缓存静态资源文件:页面、脚本、图片、视频、文件等)
  ->CDN原理:
    ...
->反向代理缓存:(指在网站服务器机房部署代理服务器,实现负载均衡、数据缓存、安全控制等功能)
  ->缓存原理:(反向代理位于应用服务器机房,处理所有对WEB服务器的请求。)
    若用户请求的页面在代理服务器上有缓冲的话,代理服务器直接将缓冲内容发送给用户;
    若没有缓冲则先向WEB服务器发出请求,取回数据,本地缓冲后再发送给用户。
    (通过降低向WEB服务器的请求数,从而降低了WEB服务器的负载)
    反向代理一般缓存静态资源,动态资源转发到应用服务器处理。常用的缓存应用服务器有Nginx、Varnish、Squid
  ->代理缓存比较:
    Varnish, Squid, Nginx
->分布式缓存:(CDN, 反向代理缓存,主要解决静态文件,或用户请求资源的缓存,数据源一般为静态文件或动态生成的文件,有缓存头标识)
  分布式缓存,主要指缓存用户经常访问数据的缓存,数据源为数据库。一般起到热点数据访问和减轻数据库压力的作用。
  常用的中间件有Memcache, Redis。
  ->Memcache:(高性能,分布式内存对象缓存系统,通过在内存里维护一个统一的巨大的hash表,可以存储各种格式的数据,总之就是将数据调用到内存中,然后从内存中读取,从而大大提高读取速度。)
    Memcache特性:
      ->使用物理内存作为缓存区,可独立运行在服务器上;
      ->使用key-value的方式来存储数据,即一种单索引的结构化数据组织形式,可使数据项查询的时间复杂度为O(1)
      ->协议简单:基于文本行的协议,直接通过telnet在memcached服务器上可进行存取数据操作
      ->基于Libevent高性能通信:
      ->内置的内存管理方式:所有数据都保存在内存中,存取比硬盘快,当内存满后,通过LRU自动删除不使用的缓存(但没有考虑数据的容灾问题,重启服务,所有数据会丢失)
      ->分布式:服务器不具有分布式功能,分布式部署取决于Memcache客户端;
      ->缓存策略:memcached的缓存策略是LRU(最近最少使用)到期失效策略;
    Memcache集群:(虽然成为"分布式"缓存服务器,但服务器端并没有"分布式"功能。memcached的分布式,是由客户端程序实现的,当向memcached集群存入/取出key value时,memcached客户端程序根据一定的算法计算存入哪台服务器,然后再把key value值存到此服务器中)
    分布式算法(Consistent Hashing):
      根据余数来计算分布;
      根据散列算法来计算分布。
  ->Redis:(基于内存的,多数据结构存储系统,可以用作数据库、缓存和消息中间件。支持多种类型的数据结构:....)
    内置了复制(replication),LUA脚本, LRU驱动事件,事务和不同级别的磁盘持久化,并通过了Redis哨兵和自动分区提供高可用性。
    Redis常用数据类型:
      String:(常用命令:set, get, decr, incr, mget)
        实现方式:String在Redis内部存储默认就是一个字符串,被redisObject所引用,当遇到incr, decr等操作时会转成数值类型进行计算,此时redisObject的encoding字段为int。
      Hash:(常用命令:hget, hset, hgetall)
        实现方式:Redis Hash对应的value, 内部实际就是一个HashMap
      List:(常用命令:lpush, rpush, lpop, rpop, lrange)
        实现方式:Redis list的实现为一个双向链表,可以支持反向查找和遍历,方便操作。
      Set:(常用命令:sadd, spop, smembers, sunion)
        实现方式:set的内部实现是一个value永远为null的HashMap,实际就是通过计算hash的方式来快速排重的,这也是set能提供判断一个成员是否在集合内的原因。
      Sorted set: (常用命令:zadd, zrange, zrem, zcard)
        实现方式:Redis sorted set的内部使用HashMap和跳跃表(SkipList)来保证数据的存储和有序,HashMap里放的是成员到score的映射,而跳跃表里存放的是所有的成员,排序依据是HashMap里存的score,使用跳跃表的结构可以获得比较高的查找效率,并且实现简单
    Redis集群:
      ->通过keepalived实现的高可用方案:(切换流程)
        当Master挂了后,VIP漂移到Slave;Slave上的keepalived通知redis执行:slaveof no one,开始提供业务/;
        当Master起来后,VIP地址不变,Master的keepalived通知redis执行slaveof slave IP host,开始作为从同步数据;
        依次类推。
      ->使用Twemproxy实现集群方案:
        ...
      使用keepalived实现高可用主备方案,解决proxy单点问题。(优点)
        对于客户端而言,redis集群是透明的,客户端简单,便于动态扩容;
        Proxy为单点,处理一致性hash时,集群节点可用性检测不存在脑裂问题;
        高性能,CPU密集型,而redis节点集群多CPU资源冗余,可部署在redis节点集群上,不需要额外设备。
  ->Memcache与Redis的比较:
    数据结构:Memcache只支持key value存储方式,Redis支持更多的数据类型
    多线程:Memcache支持多线程,Redis支持单线程;CPU利用方面,Memcache优于Redis;
    持久化:Memcache不支持持久化,Redis支持持久化;
    内存利用率:Memcache高,Redis低(采用压缩的情况下比Memcache高)
    过期策略:Memcache过期后,不删除缓存,会导致下次取数据的问题,Redis有专门线程,清除缓存数据
->本地缓存:(指应用内部的缓存,标准的分布式系统,一般由多级缓存构成。)
  ->硬盘缓存:
  ->内存缓存
->缓存架构示例:
  多级缓存
->缓存常见问题:
  ->数据一致性:
    ...
  ->缓存高可用:(一般通过分布式和复制实现)
    分布式实现数据的海量缓存;(一致性Hash算法)
    复制实现缓存数据节点的高可用。 (异步复制)
  ->缓存雪崩:(指当大量缓存失效时,导致大量的请求访问数据库,导致数据库服务器无法抗住请求或挂掉的情况)
    合理规划缓存的失效时间;
    合理评估数据库的负载压力;
    对数据库进行过载保护或应用层限流;
    多级缓存设计,缓存高可用。
  ->缓存穿透:
    ...

透明多级缓存解决方案(TMC)设计思路

  ->多级缓存解决方案需要解决的痛点:
    热点探测:如何快速且准确的发现热点访问key;
    数据一致性:前置在应用层的本地缓存,如何保障与分布式缓存系统的数据一致性;
    效果验证:如何让应用层查看本地缓存命中率、热点key等数据,验证多级缓存效果;
    透明接入:整体解决方案如何减少对应用系统的入侵,做到快速平滑接入。
  ->TMC本地缓存:
    如何透明:
      TMC对原生的jedis包的JedisPool和Jedis类做了改造,在JedisPool初始化过程中集成TMC"热点发现"+"本地缓存"功能Hermes-SDK包的初始化逻辑,使Jedis客户端与缓存服务端代理层交互时先与Hermes-SDK交互,从而完成"热点探测"+"本地缓存"功能的透明接入。
    整体结构:
      Jedis-Client: Java应用与缓存服务端交互的直接入口,接口定义与原生Jedis-Client无异。
      Hermes-SDK: 自研"热点发现+本地缓存"功能的SDK封装,Jedis-Client通过与它交互来集成相应能力。
        SDK接口:
          获取key值:byte[] get(String key, Callable)
          失效key值:invalid(String key)
        热点模块:
          热点维护:(key列表&过期时间)
          本地缓存维护
        通信模块:key访问上报、key失效上报、热点发现监听、热点失效监听
      Hermes服务端集群:接收Hermes-SDK上报的缓存访问数据,进行热点探测,将热点key推送给Hermes-SDK做本地缓存。
?为什么不是在Hermes-SDK进行通信缓存数据
      缓存集群:由代理层和存储层组成,为应用客户端提供统一的分布式缓存服务入口。
      基础组件:etcd集群,Apollo配置中心,为TMC提供"集群推送"和"统一配置"能力;
      
      基本流程:
        key值获取
        key值过期
        热点发现
        配置读取
      稳定性:
        数据上报异步化
        通信模块线程隔离
        缓存管控
      一致性:(缓存一致性)
        Hermes-SDK的热点模块仅缓存热点key数据,绝大多数非热点key数据由缓存集群存储
        热点key变更导致value失效时,Hermes-SDK同步失效本地缓存,保证本地强一致
        热点key变更导致value失效时,Hermes-SDK通过etcd集群广播事件,异步失效业务应用集群中其他节点的本地缓存,保证集群最终一致
  ->TMC热点发现:(整体流程分为以下四个步骤)
    数据收集:(Hermes-SDK通过本地rsyslog将key访问事件以协议格式放入kafka,Hermes服务端集群的每个节点消费kafka消息,实时获取key访问事件)
      访问事件协议格式如下:
        appName: 集群节点所属业务应用
        uniqueKey: 业务应用key访问事件的key
        sendTime: 业务应用key访问事件的发生时间
        weight: 业务应用key访问事件的访问权值
      Hermes服务端集群节点将收集到的key访问事件存储在本地内存中,内存数据结构为Map<String, Map<String, LongAdder>>,对应业务含义映射为Map<appName, Map<uniqueKey, 热度>>
    热度滑窗:
      通过appName和uniqueKey分别对事件的热度进行映射,从而达到分级缓存的效果
      时间滑窗:(Hermes服务端集群节点,对每个APP的每个key,维护一个时间轮:)
        时间轮中共10个时间片,每个时间片记录当前key对应3秒时间周期的总访问次数;
        时间轮10个时间片的记录累加即表示当前key从当前时间向前30秒时间窗口内的总访问次数。
      映射任务:(Hermes服务端集群节点,对每个APP每3秒生成一个映射任务,交由节点内"缓存映射线程池"执行)
        对当前APP,从Map<appName, Map<uniqueKey, 热度>>中取出appName对应的Map Map<uniqueKey, 热度>>;
        遍历Map<uniqueKey, 热度>>中的key,对每个key取出其热度存入其时间轮对应的时间片中。
    热度汇聚:(完成热度滑窗之后,映射任务继续对当前APP进行热度汇聚工作)
      遍历APP的key,将每个key的时间轮热度进行汇总(即30秒时间窗口内总热度)得到探测时刻滑窗总热度;
      将<key, 滑窗总热度>以排序集合的方式存入Redis存储服务中,即热度汇聚结果。
    热点探测:

Redis设计简介

  你会怎样设计一个缓存:(就用Map来实现一个缓存)
String value = map.get("somKey");
if (null == value) {
  value = queryValueFromDB("someKey");
}
  HashMap、TreeMap都是线程不安全的,就用HashTable、ConcurrentHashMap好了:不管用什么Map,其背后都是key-value的Hash表结构,目的就是为了实现O(1)复杂度的查找算法
  ->C/S架构:
    ->Redis客户端:
      Redis的命令行、各种语言的Redis API
    ->Redis服务端:
      Hash表所在的是Redis服务端(Server)
  ->Redis的Server是单线程服务器(不必考虑线程安全问题,简化开发,提高性能;减少线程切换损耗的时间)
  ->集群:
    一台Redis内存有限(当很多台客户端使用同一台Redis服务器时,其内存毕竟是有限的,放不了那么多数据)
    客户端变多了,Redis吞吐量变低
    ->通过集群来解决(一台Redis不够,就再多加几台):
      客户端的请求会通过负载均衡算法(通常是一致性Hash),分散到各个Redis服务器上
      扩大缓存容量
      提升吞吐量
  ->主从复制:
    数据可用性差:若其中一台Redis挂了,则上面的全部的缓存数据都会丢失,导致原来可以从缓存中获取的请求,都去访问数据库了,数据库陡增。
    数据查询缓慢:监测发现,每天有一段时间,Redis 1的访问量非常高,而且大多数请求都是去查一个相同的缓存数据,导致Redis 1非常忙碌,吞吐量不足以支撑这个高的查询负载。
    ->解决可用性问题:给每一台Redis都加上一台Slave,通过Master-Slave模式,则实现了两个特性:
      数据高可用:Master负责接收客户端的写入请求,将数据写到Master后,同步给Slave,实现数据备份。一旦Master挂了,可以将Slave提拔为Master。
      提高查询效率:一旦Master发现自己忙不过来了,可以把一些查询请求,转发给Slave去处理,也就是Master负责读写或者只负责写,Slave负责读。
      ->Master/Slave chains架构
  ->Redis使用:
    GET: GET var
    SET: SET var *
    INCR: 对操作的变量做+=1操作,且是原子性操作
    Redis可以设置key的有效时间(通过EXPIRE和TTL命令来完成),如下:
      SET resource:lock "Redis Demo"
      EXPIRE resource:lock 20   //这个key将在120秒后被删除
      TTL resource:lock    //通过TTL命令来测试一个key还能存在多长时间,执行命令后会返回该key剩余的存在时间,-2表示这个key已经不存在了,-1表示

一致性hash简介

——>一致性Hash:(https://zhuanlan.zhihu.com/p/34985026)
  为什么要使用一致性Hash?
    一个常见的场景:当一条数据进入Redis缓存集群时,如果是以随机的方式进入的话,那取数据的时候就会有一个问题:要遍历Redis集群中的服务器才能找到取的数据。所以,这个时候就需要数据的存储具有一定的规律,这样在取数据的时候就能够按照某种规律快速取出数据。
  其中Hash策略是规律性存储数据的一种方案;
  使用Hash的问题:
    使用Hash虽然提升了性能(不需要对整个Redis服务器进行遍历),但是考虑这样一个场景:当服务器数量变动的时候,所有缓存的位置都要发生改变。(即当服务器数量发生改变时,所有缓存在一定时间内是失效的,当应用无法从缓存中获取数据时,则会向后端数据库请求数据——>缓存雪崩)
  一致性Hash算法:(很明显它就是用来解决上面所描述的问题的)
    2的32次方个点所组成的圆环称为Hash环;
    将各个服务器使用Hash进行一个哈希,具体可以选择服务器的IP或主机名作为关键字进行哈希,这样每台机器就能确定其在哈希环上的位置;
    接下来使用如下算法定位数据访问到相应服务器:
      将数据key使用相同的函数Hash计算出哈希值;
      确定此数据在环上的位置;
      从此位置沿顺时针“行走”,第一台服务器就是其应该定位到的服务器
    无论是增加机器数量还是减少机器数量,一致性Hash算法对于节点的增减都只需重定位环空间中的一小部分数据,具有较好的容错性和可扩展性。
  Hash环的数据倾斜问题:
    考虑这样一个场景:一致性Hash算法在服务节点太少时,容易因为节点分布不均匀而造成数据倾斜(被缓存的数据大部分集中缓存在某一台服务器上)问题;
    解决办法:一致性Hash算法引入了虚拟节点机制,即对每一个服务节点计算多个哈希,每个计算结果位置都放置一个此服务节点,称为虚拟节点。

缓存架构设计方案

SDKManagerServer
本地缓存路由策略本地缓存
路由策略获取心跳管理心跳上报
API封装指标管理指标上报
指标上报工单管理限流控制
限流控制
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值