域名申请到网站建设教程,北京vi设计公司哪,上海到北京高铁价格查询,代理网络设置文章目录 前言一、影响因素二、db or redis or local1.db2.redis3. local 三、redisson 和 CaffeineCache 封装3.1 redisson3.1.1 maven3.1.2 封装3.1.3 使用 3.2 CaffeineCache3.1.1 maven3.1.2 封装3.1.3 使用 总结 前言
让我们来聊一下数据缓存#xff0c;它是如何为我们带… 文章目录 前言一、影响因素二、db or redis or local1.db2.redis3. local 三、redisson 和 CaffeineCache 封装3.1 redisson3.1.1 maven3.1.2 封装3.1.3 使用 3.2 CaffeineCache3.1.1 maven3.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 dependencygroupIdorg.redisson/groupIdartifactIdredisson-spring-boot-starter/artifactId/dependency3.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 ListT getCachedList(String key, SupplierListT dataFetcher, long ttl, TimeUnit timeUnit) {if (ObjectUtil.isNotNull(redissonClient)) {// 尝试从缓存中获取数据ListT cachedData redissonClient.getList(key);if (cachedData.size() 0) {// 缓存中有数据直接返回return cachedData;} else {// 缓存中没有数据调用数据提供者接口从数据库中获取ListT data dataFetcher.get();cachedData.clear();cachedData.addAll(data);// 将数据存入缓存并设置存活时间// 获取 bucket 对象为了设置过期时间RBucketListT 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 ListMatMaterialsResp listCache(ListQO qo) {RedisCacheProvider cache new RedisCacheProvider(redissonClient);ListMatMaterialsResp resps cache.getCachedList(testList, () - {// 缓存数据查询}, 20, TimeUnit.SECONDS);return resps;
}3.2 CaffeineCache
也可以使用hashMap
3.1.1 maven dependencygroupIdcom.github.ben-manes.caffeine/groupIdartifactIdcaffeine/artifactIdversion3.0.5/version/dependency3.1.2 封装
CaffeineCacheK, 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 CaffeineCacheK, V {private final CacheK, 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 ListT getCachedList(String key, SupplierListT dataFetcher) {if (ObjectUtil.isNotNull(cache.get(key))) {return (ListT) cache.get(key);} else {ListT data dataFetcher.get();cache.put(key, data);return data;}}public static T ListT getCachedList(String key, FunctionString, ListT dataFetcher) {return (ListT) 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 ListMatMaterialsResp listLocalCache(ListQO qo) {ListMatMaterialsResp resps cache.getCachedList(testList, (s) - {// 缓存数据查询});return resps;
}注意Caffeine 实现的缓存占用 JVM 内存小心 OutOfMemoryError
解决场景: 1.本地缓存适用不限制缓存大小,导致OOM适合缓存小对象 2.本地缓存长时间存在,未及时清除无效缓存,导致内存占用资源浪费 3.防止人员api滥用, 未统一管理随意使用,导致维护性差等等
总结
从前的无脑经验db查询慢redis缓存起来redis真不一定快 一个简单性能测试测试响应时间均为二次查询的大概时间
前置条件 一条数据转换需要200ms共5条数据5个字段项数据量大小463 B
db 1s
redis 468ms
local 131ms去除转换时间直接响应
db 208ms
redis 428ms
local 96ms