关于缓存 db redis local 取舍之道

文章目录

  • 前言
  • 一、影响因素
  • 二、db or redis or local
    • 1.db
    • 2.redis
    • 3. local
  • 三、redisson 和 CaffeineCache 封装
    • 3.1 redisson
      • 3.1.1 maven
      • 3.1.2 封装
      • 3.1.3 使用
    • 3.2 CaffeineCache
      • 3.1.1 maven
      • 3.1.2 封装
      • 3.1.3 使用
  • 总结


前言

让我们来聊一下数据缓存,它是如何为我们带来快速的数据响应的。你知道吗,为了提高数据的读取速度,我们通常会引入数据缓存。但是,你知道吗,不是所有的数据都适合缓存,有些数据更适合直接从数据库查询。现在,我们就来一起讨论一下,什么样的数据适合直接从数据库查询,什么样的数据适合从缓存中读取。这将有助于我们更好地利用缓存,提高系统的性能。让我们开始吧!

一、影响因素

当涉及到数据查询和缓存时,有几个因素可以考虑来确定什么样的数据适合直接从数据库查询,什么样的数据适合从缓存中读取。

  • 访问频率:如果某个数据被频繁访问,且对实时性要求不高,那么将其缓存在内存中会显著提高响应速度。这样的数据可以是经常被查询的热点数据,比如网站的热门文章、商品信息等。

  • 数据更新频率:如果某个数据经常发生更新,那么将其缓存可能导致缓存和数据库中的数据不一致。对于这种情况,最好直接从数据库中查询最新数据。比如用户个人信息、订单状态等经常变动的数据。

  • 数据大小:较大的数据对象,如图片、视频等,由于其体积较大,将其缓存到内存中可能会占用大量资源。这种情况下,可以将这些数据存储在分布式文件系统或云存储中,并通过缓存存储其访问路径或标识符。

  • 数据一致性:一些数据在不同地方的多个副本可能会导致一致性问题。对于需要保持强一致性的数据,建议直接从数据库查询。而对于可以容忍一定程度的数据不一致的场景,可以考虑将数据缓存。

  • 查询复杂度:某些复杂的查询操作可能会消耗大量的计算资源和时间,如果这些查询结果需要频繁访问,可以将其缓存,避免重复计算,提高响应速度。

需要注意的是,数据缓存并非适用于所有情况。缓存的使用需要谨慎,需要权衡数据的实时性、一致性和存储成本等方面的需求。此外,对于缓存数据的更新和失效策略也需要考虑,以确保缓存数据的准确性和及时性。

综上所述,数据适合直接从数据库查询还是缓存读取,取决于数据的访问频率、更新频率、大小、一致性要求和查询复杂度等因素。在实际应用中,需要根据具体情况进行综合考虑和合理选择。

二、db or redis or local

1.db

  • 查询复杂度低
  • 字段少
  • sql执行效率高
  • 实时性高

通常数据库适合查询字典类型数据,如类似 key value 键值对,数据更新频繁,实时性高的数据。
对于sql效率高的查询,redis查询不一定比db查询快。

2.redis

  • 查询复杂度高
  • 字段相对不多
  • 实时性低

Redis适合查询复杂度较高、实时性要求较低的数据。当SQL查询效率较低,或者需要进行字段code和value的转换存储时,Redis可以提供更高效的查询方式。不过,需要注意的是,Redis的主要瓶颈在于数据的序列化和反序列化过程。如果数据量较大,包含大量字段或者数据量巨大,那么Redis的查询速度可能不一定比数据库快,当然此时数据库本身执行效率也低。在这种情况下,我们需要综合考虑数据的复杂度、实时性要求以及数据量的大小,选择最适合的查询方式。有时候,可能需要在数据库和Redis之间进行权衡和折中,以找到最佳的性能和效率平衡点。因此,为了提高查询速度,我们需要根据具体的业务需求和数据特性,选择合适的存储和查询方案。

3. local

  • 查询复杂度高
  • 字段多
  • 实时性低

本地缓存通常是最快的。它可以在内存中直接读取数据,速度非常快。然而,由于受限于内存大小,本地缓存的数据量是有限的。对于那些数据库和Redis难以处理的大型数据,我们可以考虑使用本地缓存。通过将一部分频繁访问的数据存储在本地缓存中,可以大大提高系统的响应速度。这样,我们可以在不牺牲太多内存资源的情况下,快速获取到需要的数据。当然,需要注意的是,由于本地缓存的数据是存储在内存中的,所以在服务器重启或缓存过期时,需要重新从数据库或Redis中加载数据到本地缓存中。因此,在使用本地缓存时,需要权衡数据的大小、更新频率以及内存资源的限制,以获得最佳的性能和可用性。

三、redisson 和 CaffeineCache 封装

提供缓存查询封装,查询不到时直接查数据库后存入缓存。

3.1 redisson

3.1.1 maven

        <dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId></dependency>

3.1.2 封装

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.cuzue.common.core.exception.BusinessException;
import com.cuzue.dao.cache.redis.RedisClient;
import org.redisson.api.RBucket;
import org.redisson.api.RKeys;
import org.redisson.api.RedissonClient;import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;public class RedisCacheProvider {private static RedissonClient redissonClient;public RedisCacheProvider(RedissonClient redissonClient) {this.redissonClient = redissonClient;}/*** 从redissonClient缓存中取数据,如果没有,查数据后存入** @param key         redis key* @param dataFetcher 获取数据* @param ttl         缓存时间* @param timeUnit    缓存时间单位* @param <T>* @return 数据*/public <T> List<T> getCachedList(String key, Supplier<List<T>> dataFetcher, long ttl, TimeUnit timeUnit) {if (ObjectUtil.isNotNull(redissonClient)) {// 尝试从缓存中获取数据List<T> cachedData = redissonClient.getList(key);if (cachedData.size() > 0) {// 缓存中有数据,直接返回return cachedData;} else {// 缓存中没有数据,调用数据提供者接口从数据库中获取List<T> data = dataFetcher.get();cachedData.clear();cachedData.addAll(data);// 将数据存入缓存,并设置存活时间// 获取 bucket 对象,为了设置过期时间RBucket<List<T>> bucket = redissonClient.getBucket(key);// 为整个列表设置过期时间bucket.expire(ttl, timeUnit);// 返回新获取的数据return data;}} else {throw new BusinessException("redissonClient has not initialized");}}/*** 删除缓存** @param key redis key*/public void deleteCachedList(String systemName, String key) {if (ObjectUtil.isNotNull(redissonClient)) {RKeys keys = redissonClient.getKeys();keys.deleteByPattern(key);} else {throw new BusinessException("redis client has not initialized");}}
}

3.1.3 使用

启动类添加:@Import({RedissonConfig.class})
直接引用:


@Resource
private RedissonClient redissonClient;//缓存数据获取
public List<MatMaterialsResp> listCache(ListQO qo) {RedisCacheProvider cache = new RedisCacheProvider(redissonClient);List<MatMaterialsResp> resps = cache.getCachedList("testList", () -> {// 缓存数据查询}, 20, TimeUnit.SECONDS);return resps;
}

3.2 CaffeineCache

也可以使用hashMap

3.1.1 maven

       <dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId><version>3.0.5</version></dependency>

3.1.2 封装

CaffeineCache<K, V>

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.Weigher;import java.util.concurrent.TimeUnit;
import java.util.function.Function;public class CaffeineCache<K, V> {private final Cache<K, V> cache;/*** 不过期缓存** @param maxSize 缓存条目数量 注意对象大小不要超过jvm内存*/public CaffeineCache(long maxSize) {this.cache = Caffeine.newBuilder().maximumSize(maxSize).build();}/*** 初始化Caffeine** @param maxSize* @param expireAfterWriteDuration* @param unit*/public CaffeineCache(long maxSize, long expireAfterWriteDuration, TimeUnit unit) {this.cache = Caffeine.newBuilder().maximumSize(maxSize).expireAfterWrite(expireAfterWriteDuration, unit).build();}/*** 初始化Caffeine 带权重** @param maxSize* @param weigher                  权重* @param expireAfterWriteDuration* @param unit*/public CaffeineCache(long maxSize, Weigher weigher, long expireAfterWriteDuration, TimeUnit unit) {this.cache = Caffeine.newBuilder().maximumSize(maxSize).weigher(weigher).expireAfterWrite(expireAfterWriteDuration, unit).build();}public V get(K key) {return cache.getIfPresent(key);}public void put(K key, V value) {cache.put(key, value);}public void remove(K key) {cache.invalidate(key);}public void clear() {cache.invalidateAll();}// 如果你需要一个加载功能(当缓存miss时自动加载值),你可以使用这个方法public V get(K key, Function<? super K, ? extends V> mappingFunction) {return cache.get(key, mappingFunction);}// 添加获取缓存统计信息的方法public String stats() {return cache.stats().toString();}
}

LocalCacheProvider

import cn.hutool.core.util.ObjectUtil;
import com.cuzue.dao.cache.localcache.CaffeineCache;import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Supplier;/*** 本地缓存*/
public class LocalCacheProvider {private static CaffeineCache cache;/*** 无过期时间* @param maxSize 缓存最大条数*/public LocalCacheProvider(long maxSize) {cache = new CaffeineCache(maxSize);}/*** 带过期时间* @param maxSize 缓存最大条数* @param ttl 过期时间* @param timeUnit 时间单位*/public LocalCacheProvider(long maxSize, long ttl, TimeUnit timeUnit) {cache = new CaffeineCache(maxSize, ttl, timeUnit);}public static <T> List<T> getCachedList(String key, Supplier<List<T>> dataFetcher) {if (ObjectUtil.isNotNull(cache.get(key))) {return (List<T>) cache.get(key);} else {List<T> data = dataFetcher.get();cache.put(key, data);return data;}}public static <T> List<T> getCachedList(String key, Function<String, List<T>> dataFetcher) {return (List<T>) cache.get(key, dataFetcher);}/*** 删除缓存** @param key redis key*/public void deleteCachedList(String key) {cache.remove(key);}
}

3.1.3 使用

//初始化caffeine对象
LocalCacheProvider cache = new LocalCacheProvider(5000, 20, TimeUnit.SECONDS);//缓存数据获取
public List<MatMaterialsResp> listLocalCache(ListQO qo) {List<MatMaterialsResp> resps = cache.getCachedList("testList", (s) -> {// 缓存数据查询});return resps;
}

注意:Caffeine 实现的缓存占用 JVM 内存,小心 OutOfMemoryError

解决场景:
1.本地缓存适用不限制缓存大小,导致OOM,适合缓存小对象
2.本地缓存长时间存在,未及时清除无效缓存,导致内存占用资源浪费
3.防止人员api滥用, 未统一管理随意使用,导致维护性差等等

总结

从前的无脑经验,db查询慢,redis缓存起来,redis真不一定快!
一个简单性能测试:(测试响应时间均为二次查询的大概时间)

  1. 前置条件: 一条数据转换需要200ms,共5条数据,5个字段项,数据量大小463 B
db > 1s
redis > 468ms
local > 131ms
  1. 去除转换时间,直接响应
db > 208ms
redis > 428ms
local > 96ms

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/640994.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C/C++】C/C++编程——为什么学习 C++?

当提到C的时候&#xff0c;很多人会觉得语法复杂、学习曲线陡峭&#xff0c;并且好像与C语言还有点"纠缠不清"。尽管如此&#xff0c;C仍然是当今世界上最受欢迎和最有影响力的编程语言之一。特别是在当今快速发展的人工智能&#xff08;AI&#xff09;领域&#xff…

vue3前端开发,如何引入element-plus前端框架及配置参数

vue3前端开发,如何引入element-plus前端框架及配置参数&#xff01;这是一个简单的教程&#xff0c;帮助大家快速在自己的项目中引入element-plus框架。 主要是介绍的引入流程和参数的配置情况。 如图&#xff0c;这个就是elment-plus前端框架里面的一个主按钮展示。表示我们配…

NodeJs 第二十章 代理

在计算机网络中&#xff0c;代理是一种中间服务&#xff0c;能够代理用户与网络资源之间的通信。代理服务器可以缓存网页内容、过滤网络流量或隐藏用户的真实IP地址等功能。 在日常开发中&#xff0c;我们接触最多的是客户端发送ajax到服务端。但是服务端并不是 node &#xf…

pod 报错Failed to connect to github.com port 443

pod 报错Failed to connect to github.com port 443 1、排查代理问题1.1、查找网络代理1.2、修改 Git 的代理 2、排查DNS解析问题2.1、查找 ip地址2.2、修改 host 文件 1、排查代理问题 1.1、查找网络代理 打开 设置 --> 网络与Internet --> 查找代理 1.2、修改 Git …

k8s中服务器容器tcp连接数量优化

netty的http1服务器在运行一段时间后会无法提供服务&#xff0c;返回客户端socket hang up 使用apipost测试抓包显示三次握手后被reset 修改net/core/somaxconn 登录容器&#xff0c;cat /proc/sys/net/core/somaxconn显示128&#xff0c;对于一个服务器来说&#xff0c;这个…

PLAN方法:解决 GAN 生成医学图像 Latent 空间中的隐私保护方法

PLAN方法&#xff1a;解决 GAN 生成医学图像 Latent 空间中的隐私保护方法 PLAN 原理StyleGAN 生成视网膜图k-SALSA 生成视网膜图PLAN方法 生成视网膜图 总结 PLAN 原理 论文&#xff1a;https://arxiv.org/abs/2307.02984 代码&#xff1a;https://github.com/perceivelab/P…

vulhub之redis篇

CVE-2022-0543 | redis的远程代码执行漏洞 简介 CVE-2022-0543 该 Redis 沙盒逃逸漏洞影响 Debian 系的 Linux 发行版本,并非 Redis 本身漏洞, 漏洞形成原因在于系统补丁加载了一些redis源码注释了的代码 原理分析 redis一直有一个攻击面,就是在用户连接redis后,可以通过ev…

企业微信开发:本地运行一个页面应用

问题 在开发环境本地运行一个页面应用&#xff0c;将网页URL配置到企业微信的应用主页网址中&#xff0c;此时应用在企业微信中能够正常打开网页吗&#xff1f; 结论是&#xff1a;能够正常访问页面。 能够访问的前提 能够访问的前提条件&#xff0c;企业微信客户端所在的网…

界面控件DevExpress ASP.NET Data Grid组件 - 可快速处理各类型数据!(一)

由DevExpress开发的快速且功能完整的ASP.NET Web Forms的Data Grid组件&#xff0c;从全面的数据塑造和数据过滤选项到十多个集成数据编辑器&#xff0c;该套件提供了帮助用户构建极佳数据所需的一些&#xff0c;没有限制&#xff01; P.S&#xff1a;DevExpress ASP.NET Web …

k8s--helm

什么是helm&#xff1f;在没有这个helm之前&#xff0c;deployment service ingress helm的作用 通过打包的方式&#xff0c;把deployment service ingress等打包在一块&#xff0c;一键式的部署服务&#xff0c;类似yum安装 官方提供的一个类似与安装仓库额功能&#xff0c;…

C语言——结构体讲解

目录 一、结构体类型的声明 二、结构体变量的定义和初始化 三、结构体的重命名 四、结构体的自引用 五、结构体内存对齐 六、结构体传参 七、结构体实现位段 7.1 什么是位段 7.2 位段的声明和使用 7.3 位段的空间大小计算 7.4 位段的内存分配 7.5 位段的跨平…

计算机网络-物理层基本概念(接口特性 相关概念)

文章目录 总览物理层接口特性星火模型给出的相关概念解释&#xff08;仅供参考&#xff09; 总览 求极限传输速率&#xff1a;奈氏准则&#xff0c;香农定理&#xff08;背景环境不一样&#xff09; 编码&#xff1a;数据变成数字信号 调制&#xff1a;数字信号变成模拟信号 信…

Elasticsearch:2023 年 Lucene 领域发生了什么?

作者&#xff1a;来自 Elastic Adrien Grand 2023 年刚刚结束&#xff0c;又是 Apache Lucene 开发活跃的一年。 让我们花点时间回顾一下去年的亮点。 社区 2023 年&#xff0c;有&#xff1a; 5 个次要版本&#xff08;9.5、9.6、9.7、9.8 和 9.9&#xff09;&#xff0c;1 …

51单片机LED点阵屏

LED点阵屏 LED点阵屏是一种由许多小型LED灯组成的矩阵式显示屏。这些LED灯可以是单色、双色或全彩的&#xff0c;它们排列成行和列的网格&#xff0c;可以根据需要点亮来显示图像、文字或动画等内容。LED点阵屏广泛应用于户外广告牌、室内显示、交通信号灯、电子价格标签和其他…

【设计模式】字节三面:请举例阐释访问者模式

今天我们要一起探讨的主题是一种设计模式——访问者模式(Visitor Pattern)。我将从最基础的概念、应用场景&#xff0c;再到实例代码的展示&#xff0c;全方位的为大家剖析访问者模式。而且&#xff0c;我保证&#xff0c;你即使是编程新手&#xff0c;也能理解并开始应用这个设…

如何在 Ubuntu 22.04 上安装 Linux、Apache、MySQL、PHP (LAMP) 堆栈

前些天发现了一个人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;最重要的屌图甚多&#xff0c;忍不住分享一下给大家。点击跳转到网站。 如何在 Ubuntu 22.04 上安装 Linux、Apache、MySQL、PHP (LAMP) 堆栈 介绍 “LAMP”堆栈是一组开源软件&#…

uniapp 在static/index.html中添加全局样式

前言 略 在static/index.html中添加全局样式 <style>div {background-color: #ccc;} </style>static/index.html源码&#xff1a; <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"utf-8"><meta http-…

HCIA——21C/S、P2P、peer的选择

学习目标&#xff1a; 计算机网络 1.掌握计算机网络的基本概念、基本原理和基本方法。 2.掌握计算机网络的体系结构和典型网络协议&#xff0c;了解典型网络设备的组成和特点&#xff0c;理解典型网络设备的工作原理。 3.能够运用计算机网络的基本概念、基本原理和基本方法进行…

安全通信网络

1.网络架构 1&#xff09;应保证网络设备的业务处理能力满足业务高峰期需要。 设备CPU和内存使用率的峰值不大于设备处理能力的70%。 在有监控环境的条件下&#xff0c;应通过监控平台查看主要设备在业务高峰期的资源&#xff08;CPU、内存等&#xff09;使用 情况&#xff…

【Emotion】 自动驾驶最近面试总结与反思

outline 写在前面面试问题回顾和答案展望 写在前面 最近由于公司部门即将撤销&#xff0c;开始了新一轮准备。 发现现在整体行情不太乐观&#xff0c;很看过去的尤其是量产的经验 同时本次面试我coding环节答得不好&#xff0c;&#xff08;其实也是半年前大家问的比较简单…