分布式缓存

1.缓存分类

1.1 前端缓存:页面和浏览器缓存,App缓存

  • 页面缓存属于客户端缓存一种,第一次访问时,页面缓存将浏览器渲染的页面存储在本地。用于数据更新比较少的数据,大部分浏览器自身会实现缓存功能,对于页面中的视频图片,浏览器都会进行缓存,App内各级缓存更加复杂。
  • HTML5支持本地储存,包括localStorage,sessionStorge。localStorage没有时间限制,sessionStorge与session的有效期内相关。支持离线缓存,Application Cache 技术 实现应用离线的缓存,Application Cache基于mainfest文件实现缓存机制,浏览器通过这个文件上的清单解析资源。

1.2 网络传输缓存

大多数业务通过HTTP实现,工作在TCP协议之上,多次握手后,浏览器和服务器建立TCP连接,然后进行数据传输,传输过程中涉及多层缓存。

  • CDN(Content Delivery Network)内容分发网络
    实现关键包括内容存储和内容分发:内容存储,对数据的缓存功能。内容分发,CDN节点支持的负载均衡。前端请求经过DNS后会被指向网络中最近的CDN节点,该节点从真正的应用服务器获取资源返回给前端,同时将静态信息缓存,在新的请求过来后,可以只请求CDN节点数据,同时CDN节点也可以和服务器之间同步更新数据
  • 负载均衡缓存
    负载均衡服务器实现请求路由的负载均衡功能。
    也可以实现部分数据的缓存,比如配置信息等很少修改的数据。
    用户请求在达到应用服务器前,会先访问Nginx负载均衡器,有缓存信息直接返回给用户,没有缓存信息,Nginx会从应用服务器获取信息

1.3 服务器缓存

是缓存的重点,是业务平时最多的缓存

  • 本地缓存:应用内缓存,Guava实现的各级缓存,或者Java语言中使用各类Map结构实现的数据存储。随着服务重启后失效,作用时间短,应用比较灵活
  • 外部缓存: redis,memchaed等NoSQL存储的分布式缓存,在系统设计中对整体性能提升最大的缓存。外部缓存使用不当导致缓存穿透,雪崩等业务问题

1.4 数据库缓存

Mybatis为每个SqlSession创建LocalCache ,其可以实现查询请求的缓存,如果查询语句命中了缓存,返回给用户,否则查询数据库,并且写入LocalCache,返回给用户。
数据库执行查询语句时,M有SQL保存一个KV形式的缓存在内存中,Key是查询语句,Value是结果集,如果缓存被命中,会直接返回给客户端,否则通过数据库引擎进行查询,并且吧结果缓存起来,当数据有修改时,需要删除缓存

2.缓存问题

2.1 缓存穿透

  • 不合理的缓存失效策略:导致大量缓存在同一时刻发生穿透,请求直接到持久层
  • 外部用户的恶意攻击:用户利用不存在的key,来构造大量不存在的数据请求数据,海量请求全部穿过缓存,到数据库

2.1.1 解决方法

  • 设置合理缓存失效策略
  • 通过缓存空数据方式避免:针对数据库不存在数据,查询为空时,添加一个null值到缓存中
  • 布隆过滤器:Bitmap

2.2 缓存击穿

前端请求大量访问某个热点key,而他在某个时刻失效
缓存穿透的特殊场景

2.3 缓存雪崩

大量缓存数据在同一时刻失效,请求全部转发到数据库,导致崩溃。

  • 缓存服务不稳定,redis宕机
  • 缓存失效导致,整体数据存储链路,服务调用链路,最终导致微服务整体对外服务出现问题

2.3.1 解决

  • 明确缓存集群的容量峰值
  • 做好缓存集群的高可用:通过RedisCluster,Proxy等不同的缓存集群,实现redis高可用

3. 缓存稳定性

  • 缓存应用:防止海量业务请求击垮数据库,保护正常服务运行
  • 考虑缓存的稳定性:缓存数据,缓存容器(缓存服务器本身的稳定性)
  • 缓存命中率:落到缓存上的请求占整体请求的占比,一般缓存命中率要求到90%以上,大促等场景99%以上。
    在应用缓存时,要对缓存服务压测,明确缓存最大水位,当前系统容量超过阈值,通过其他高可用手段进行调整,如服务限流,请求降级,使用消息队列等

4.数据不一致问题

处理少数配置信息类缓存,比如黑白名单,页面展示配置,大部分缓存作为前端请求和持久化层的中间层,承担前端的海量请求。
最理想的情况:缓冲和数据库同时更新成功,由于缓存和数据库分开的,会出现缓存更新失败或数据库更新失败情况

  • 场景:某次更新将A价格从1000更新为1200,但是数据库更新成功,缓存失败,用户查看时,看到的是1000,下单是1200

4.1 更新缓存方式

通过更新缓存和更新数据库俩个操作来实现最终一致性
请求先从缓存查,没有命中从数据库查,查询成功后,返回结果同时更新缓存。

  • 先数据库再缓存:在写操作时,先更新数据库,成功后,更新缓存会失败
  • 先删除缓存,再更新数据库: A线程更新操作,将缓存删除,B线程查询直接查数据库,A线程再更新数据库为新的值
  • 先更新数据库,再删除缓存:经典缓存+数据库读写模式,Cache Aside方案。读的时候,先读缓存,没有再读数据库,取出来数据放入缓存,并返回响应。更新时,先更新数据库,成功后再删除缓存。
    考虑该情况:A删除缓存,更新主库。B读取缓存,没有命中,从从库查询并设置缓存。主库从库同步。
    存在删除缓存失败,多次重试解决
  • 更好的方案:更新时删除,等下次查询时再填充缓存

4.2 删除而不是更新缓存

删除数据比更新数据更轻量级,出问题概率更小

4.3 多级缓存

通过事务性消息队列加监听方式失效对应缓存
较难保证数据一致性,通常用于一致性不敏感业务。
针对性的设计:

  • 通过给数据添加版本号,或通过时间戳+业务主键方式,控制缓存数据版本实现最终一致性
  • Binlog异步更新缓存

5.缓存失效策略

删除访问频率不高的缓存,为新缓存腾空间

5.1 页面置换算法

页面的调度算法,操作系统中文件读取会先分配一定的页面空间Page,查询空间是否有该页面的缓存,有直接拿到,否则查询页面空间没有满,就缓存页面,满了就删除部分页面

  • FIFO 先进先出
  • LRU 最近最少使用
  • LFU 最不经常使用

5.2 内存淘汰策略

当redis节点分配的内存使用到达最大值以后,redis启动内存淘汰策略

  • noeviction 默认,对于写请求拒绝服务,直接返回错误,保证数据不丢失
  • allkeys-lru,对所有key使用LRU算法进行缓存淘汰
  • volatile-lru 对设置了过期时间的key 使用LRU算法淘汰
  • allkeys-random ,对所有key随机淘汰数据
  • volatile-random,对设置了过期时间的key随机淘汰数据
  • volatile-ttl,对设置了过期时间的key,根据过期时间进行淘汰,越早过期的越优先被淘汰

5.3 缓存过期策略

内存淘汰是缓存服务层面的操作
过期策略是具体缓存数据何时失效,缓存key过期了该如何处理

  • 定时过期:为每个设置过期时间的key都创建定时器,到期就会清除,但需要耗费大量CPU去处理过期数据,影响缓存服务性能
  • 惰性过期:只有当访问一个key时,才判断是否过期,并且进行删除,极端情况出现大量过期key无法被删除
  • 定时过期: 添加一个即将过期的缓存字典,每隔一段时间,会扫描一定数量的key,并清除其中过期的key

5.4 实现LRU缓存

重写LinkedHashMap,在原生的removeEldestEntry中默认返回false

6.集群

redis集群方案:Cluster,twemproxy,哨兵,Codis
更新缓存,放置那台机器,获取从那台获取

  • 数据同步方案下,所有节点之间数据一样的,不同节点互为副本,但会造成服务器资源浪费,更新缓存时,考虑不同节点的一致性
  • 数据不同步方案:每个缓存节点储存数据不同,在缓存读写使用一定的策略进行分发,如果需要冗余数据,通过缓存集群主从同步实现

6.1 哈希取模路由

通过对hash/节点数量得到服务器下标
适合对固定数量缓存集群进行路由,但是对横向扩展不好

6.2 一致性哈希

在负载均衡策略下,可以应用一致性哈希,减少节点拓展时数据失效或者迁移的情况。
一致性哈希是特殊的哈希算法。在使用一致性哈希算法后,哈希表槽位数的改变平均只需要对K/n个关键字重新映射,其中K是关键字的数量,n是槽位数量。然而在传统的哈希表中,添加或删除一个槽位几乎需要对所有关键字重新映射。

6.3 Hash环

获取所有服务器节点hash值,然后获取key的hash,与节点的hash进行对比,找出顺时针最近的节点进行存储和读取。
设有四台服务器,A:100 B:200 C:300 D:400
key哈希操作200,260.500 分别放在B C A服务器
D服务下线,原先路由到D的数据顺时针迁移到A

6.3.1 缓存雪崩

10台服务器组成哈希环,顺序5台宕机,请求都转移到一台服务上,被打爆后继续转移

  • 添加虚拟节点,对服务器节点进行哈希操作,在整个哈希环上均匀添加诺干个节点,a1 a2属于A b1 b2 属于B 哈希时平均各个节点数据

6.4 一致性哈希算法

TreeMap基于红黑树实现,元素默认按照Keys的自然排序排列,对外开发tailMap(K formKey)方法,可以返回formKey顺序的下一个节点

7.Redis主从复制

任何节点都可以成为主节点,通过Slaveof命令开启复制,主从复制可以作为数据备份,从节点可以拓展主节点的读请求支持能力,实现读写分离

7.1 Mysql故障转移机制

同步方式有半同步,全同步,关于GTID的复制方式
MySQL缺少一个选举决策的节点,一般人工预选主流程
当主节点发生故障宕机,需要运维工程师从从节点服务器列表中选择一个晋升为主节点,需要更新上游客户端配置
自动故障转移:自动实现Failover

7.2 Redis Sentinel 哨兵

是一个独立运行的过程,假如主节点宕机,实现功能

  • 不定期监控Redis服务运行状态
  • Redis节点宕机,通知上游客户端进行调整
  • Master节点不可用时,可以选择一个Slave节点
    作为新的Master机器,并且更新集群中的数据同步关系

7.3 Redis Sentine 方案

包含多个Sentinel节点,以及多个数据节点的分布式架构
Sentinel节点之间相互监控,当发现某个Redis数据节点不可达时,Sentinel会对这个节点做下线处理
如果是Master节点,会通过投票选择是否下线Master节点,完成故障发现和故障转移

posted @ 2023-10-28 20:48  lwx_R  阅读(2)  评论(0编辑  收藏  举报