自学内容网 自学内容网

多级缓存架构设计

欢迎关注公众号 【11来了】 ,持续 中间件源码、系统设计、面试进阶相关内容

在我后台回复 「资料」 可领取编程高频电子书
在我后台回复「面试」可领取硬核面试笔记

文章导读地址:点击查看文章导读!

感谢你的关注!

多级缓存架构设计

对于并发量高的数据一般会采用多级缓存来提升访问效率,越前置的缓存容量越小、访问越快、抗并发能力越强,接下来会介绍如何组成多级缓存架构,看完之后将会知道:

多级缓存架构包括哪些?

数据一致性如何解决?

请求流经多级缓存流程?

复杂的缓存重建如何解决?

多级缓存架构介绍

首先介绍一下多级缓存架构,通用的多级缓存常常包括:本地内存、Redis、DB 三个部分,本地内存作为 Tomcat 服务的一部分,可以承受的请求数量有限,因此对于更高流量的要求,需要再次对多级缓存进行升级改造,如下:

  • 应用 Nginx 本地内存
  • Redis 缓存
  • JVM 内存
  • DB

这里 Nginx 分为两层:

  • 接入层 Nginx:处理入口流量,用作流量分发
  • 应用层 Nginx:接近业务层,处理业务逻辑,用作热点缓存的读取

Nginx 作为高性能的 Web 服务器,经常被作为反向代理服务器用作负载均衡,这里多增加了一层应用层 Nginx,用来处理 热点缓存数据 ,相比于 Tomcat 集群的本地内存来说,可承接的流量更高

image-20241013131536201

多级缓存请求流程

用户请求进入到接入层 Nginx 之后,会经过负载均衡算法进行分发:

  • 用户请求被 负载均衡 到各个应用层 Nginx 上
  • 在应用层 Nginx 上,读取 本地缓存降低对热点数据后端服务的冲击
  • 如果 Nginx 本地缓存未命中,则读取 Redis 缓存 ,作为第二层缓存, 减少对 Tomcat 集群的压力
  • 如果 Redis 缓存未命中,则读取 Tomcat 集群的本地内存,作为第三层缓存, 防止缓存雪崩/穿透之后对 DB 的冲击
  • 如果仍然未命中,则读取 DB,并回写多级缓存

接下来逐个介绍流程中相关的一些问题,如负载均衡算法的选择、应用层 Nginx 读取本地缓存的实现方式、缓存更新的脏写问题等等

负载均衡算法的选择

只要是路由到多个节点,都需要负载均衡算法进行节点选择,负载均衡算法有很多,常用的有:轮询和一致性哈希

轮询负载均衡

轮询的优势在于:

  • 对请求的负载更加均衡

缺点在于:

  • 相同的请求会被转发到不同的节点,因此随着节点的增多,缓存命中率不断降低
一致性哈希

一致性哈希的优势在于:

  • 相同的请求会被路由到同一台机器上
  • 如果出现节点宕机,只会少部分缓存数据失效

缺点在于:

  • 相同的请求路由到同一台机器上,会导致大量请求集中在某台机器上
负载均衡算法选择
  • 当负载较低,此时我们更追求缓存的命中率,因此可以使用一致性哈希
  • 当负载较高,肯定是热点数据的访问,让热点数据在多节点都存储,此时更加追求请求的平均分散

应用层 Nginx 本地缓存实现

在应用层 Nginx 本地缓存可以使用 Lua Shared Dict 来实现,Lua Shared Dict 是 OpenResty 提供的功能

OpenResty 是一个强大的 Web 平台,将 Nginx 和 Lua 语言集成起来,可以通过 Lua 开发相关业务逻辑,进行热点数据的查询、获取等操作

通过 Lua 可以拿到 Nginx 本地缓存数据,并且也可以请求 Redis 获取缓存数据

热点数据实现

应用层 Nginx 会进行第一层的数据处理,并且在这里会存储热点数据,因此需要在这一层对热点数据进行统计

应用层 Nginx 会将请求上报到 热点发现系统 ,热点发现系统可以进行热点数据的统计,并且将热点数据进行推送(推送到应用层 Nginx 本地缓存)

数据缓存优化

对于不同类型的数据也需要进行对应的缓存优化,如复杂数据的缓存重建、大 Value 问题、热点缓存等

数据缓存过期时间

对于缓存数据的加载有两种:

  • 设置过期时间:适合热点、易更新数据,如库存数据缓存几秒,可以短时间内不一致
  • 不设置过期时间:适合非热点、长期访问数据,如用户信息、店铺信息、类别、订单等信息

对于 缓存数据淘汰 来说,设置过期时间的数据到时间后就会自动删除,而不设置过期时间的数据,我们需要控制缓存的大小,当缓存空间满了之后,通过淘汰策略进行数据的删除

对于 淘汰策略 来说,有多种算法可供选择:

  • LRU(Least Recently Used,最近最少使用) 根据访问 时间 淘汰最久未被访问的数据;但是对于大批量数据访问来说,会导致缓存命中率下降

  • LFU(Least Frequently Used,最近最不常用) 根据访问 频率 淘汰最不常访问的数据;如果访问内容发生较大变化,会导致缓存命中率下降

  • ARC(Adaptive Replacement Cache,自适应缓存替换)算法,结合了 LRU、LFU 两者的优势,既能根据 时间 又能根据 频率 进行数据的淘汰

对于缓存的加载可以使用 缓存旁路模式 ,先写数据库,再写缓存;对于更新来说,先更新数据库,再删除缓存

对于 查询频率较高的数据 ,如商品数据,当数据修改时不能先删除缓存,如果先删除缓存,会有大量请求出现缓存 miss;而对于 查询频率较低的数据 ,如用户数据,可以先删除缓存

增量化缓存重建

对于复杂数据来说,缓存重建的成本较高,可以通过两个步骤减少缓存重建成本:

  • 对复杂数据进行维度划分
  • 根据增量数据的变更,只进行对应维度的缓存重建

比如对于商品来说,有多个维度:基础信息、图片、规则、介绍等等,维度化之后的缓存冲减成本大大降低

缓存的更新

如果多个节点同时操作一份缓存数据,那么可能发生缓存数据的覆盖,导致缓存出现脏数据,常用的解决如:

  • CAS 乐观锁:通过版本比较来完成数据更新
  • 将操作同一份数据的更新请求顺序化,消费端单线程处理保证更新不乱序
  • 单节点更新缓存数据:如使用 canal 订阅 MySQL 的 binlog,之后去处理对应 binlog 完成缓存数据的更新,避免多个节点更新同一份缓存

原文地址:https://blog.csdn.net/qq_45260619/article/details/142919698

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!