1️⃣rediscache(分布式缓存)
概念:
rediscache 是基于 redis 的缓存,数据存储在内存中,并且可以被多个应用实例共享,属于分布式缓存。
优点:
- 高性能:redis 在内存中读写,速度非常快(毫秒级甚至微秒级)。
- 分布式共享:多个应用实例可以共享同一份缓存,保证数据一致性。
- 持久化:可以选择持久化策略(rdb/aof),重启后缓存不会完全丢失。
- 功能丰富:支持数据结构(string、hash、list、set、sortedset)、过期策略、订阅/发布等。
- 扩展性强:可以通过 redis cluster 支持水平扩展,适合大规模系统。
缺点(顺便提醒):
- 网络访问会增加延迟,尤其在高并发场景下。
- 内存有限,数据量太大时成本高。
import com.fasterxml.jackson.core.jsonprocessingexception;
import com.fasterxml.jackson.core.type.typereference;
import com.fasterxml.jackson.databind.objectmapper;
import jakarta.annotation.resource;
import lombok.extern.slf4j.slf4j;
import org.jetbrains.annotations.notnull;
import org.jetbrains.annotations.nullable;
import org.jspecify.annotations.nonnull;
import org.springframework.data.redis.core.stringredistemplate;
import org.springframework.stereotype.component;
import java.time.duration;
import java.util.objects;
import java.util.function.function;
import java.util.function.supplier;
/**
* redis缓存工具类
*/
@slf4j
@component
public class rediscachetemplate {
@resource
private stringredistemplate redistemplate;
@resource
private objectmapper objectmapper;
/**
* 指定typereference类型获取数据
*/
public <t> t get(@nonnull string key, @nonnull duration duration, @nonnull typereference<t> clazz, @nonnull supplier<t> supplier) {
return gett(key, duration, supplier, strvalue -> {
try {
return objectmapper.readvalue(strvalue, clazz);
} catch (exception e) {
log.error("redis反序列化失败,key:{}", key, e);
throw new runtimeexception(e);
}
});
}
/**
* class类型获取数据
*/
public <t> t get(@nonnull string key, @nonnull duration duration, @nonnull class<t> clazz, @nonnull supplier<t> supplier) {
return gett(key, duration, supplier, strvalue -> {
try {
return objectmapper.readvalue(strvalue, clazz);
} catch (exception e) {
log.error("redis反序列化失败,key:{}", key, e);
throw new runtimeexception(e);
}
});
}
/**
* 写入数据
*/
public <t> void set(@nonnull string key, @nonnull duration duration, @nonnull t object) {
string rediskey = this.buildkey(key);
try {
string json = objectmapper.writevalueasstring(object);
redistemplate.opsforvalue().set(rediskey, json, duration);
} catch (jsonprocessingexception e) {
log.error("redis序列化失败,key:{}", key, e);
throw new runtimeexception(e);
}
}
/**
* 移除数据
*/
public void del(@nonnull string key) {
string rediskey = this.buildkey(key);
redistemplate.delete(rediskey);
}
/**
* 公共业务代码处理
*/
private <t> @nullable t gett(@notnull string key, @notnull duration duration, @notnull supplier<t> supplier, function<string, t> deserialize) {
string rediskey = this.buildkey(key);
string strvalue = redistemplate.opsforvalue().get(rediskey);
if (objects.isnull(strvalue)) {
t value = supplier.get();
if (objects.nonnull(value)) {
this.set(key, duration, value);
}
return value;
} else {
return deserialize.apply(strvalue);
}
}
/**
* redis的key拼接
*/
private string buildkey(string key) {
return "test:cache:" + key;
}
}
// 使用方式
public static void main(string[] args) {
private static final typereference<list<string>> list_test = new typereference<>() {
};
@resource
private rediscachetemplate rediscachetemplate;
list<string> strings = rediscachetemplate.get(rediskey.test_list, duration.ofminutes(60), list_test, () -> {
system.out.println("test");
return list.of("test");
});
}2️⃣localcache(本地缓存)
概念:
localcache 是应用本地内存缓存,数据只存在于当前应用实例的内存里,常见实现有 guava cache、caffeine。
优点:
- 超低延迟:数据在本地内存,访问速度最快(纳秒到微秒级)。
- 简单易用:不依赖外部组件,集成方便。
- 降低网络压力:读写缓存不需要通过网络访问 redis。
- 可配置丰富:支持过期策略、容量限制、lru/lfu 等缓存淘汰策略。
缺点:
- 数据不共享:多实例部署时,每个实例都有一份独立缓存,可能出现数据不一致。
- 内存受限:缓存太大可能会占用应用内存,影响 jvm 性能。
import com.fasterxml.jackson.core.type.typereference;
import com.fasterxml.jackson.databind.objectmapper;
import com.github.benmanes.caffeine.cache.cache;
import com.github.benmanes.caffeine.cache.caffeine;
import com.github.benmanes.caffeine.cache.expiry;
import jakarta.annotation.resource;
import lombok.extern.slf4j.slf4j;
import org.jetbrains.annotations.notnull;
import org.jetbrains.annotations.nullable;
import org.jspecify.annotations.nonnull;
import org.springframework.stereotype.component;
import java.time.duration;
import java.util.map;
import java.util.function.function;
import java.util.function.supplier;
/**
* caffeine本地缓存工具类
*/
@slf4j
@component
public class localcachetemplate {
/**
* jvm 全局缓存
* 存储 cachevalue 包装对象,支持每条缓存独立 ttl
*/
private static final cache<string, cachevalue<object>> cache = caffeine.newbuilder()
// lru 淘汰:数量满了移除长时间未访问或最早的数据
.maximumsize(30000)
// ttl 过期:按时间清理
.expireafter(new expiry<string, cachevalue<object>>() {
@override
public long expireaftercreate(@notnull string key, @notnull cachevalue<object> value, long currenttime) {
return value.ttlnanos();
}
@override
public long expireafterupdate(@notnull string key, @notnull cachevalue<object> value, long currenttime, long currentduration) {
return value.ttlnanos();
}
@override
public long expireafterread(@notnull string key, @notnull cachevalue<object> value, long currenttime, long currentduration) {
// 读取不改变 ttl
return currentduration;
}
}).build();
@resource
private objectmapper objectmapper;
/**
* 判断key是否存在(非强一致,仅用于弱判断)
*/
public boolean exists(@nonnull string key) {
string rediskey = this.buildkey(key);
return cache.getifpresent(rediskey) != null;
}
/**
* 获取key数据
*/
public <t> t get(@nonnull string key, @nonnull class<t> clazz) {
string rediskey = this.buildkey(key);
cachevalue<object> wrapper = cache.getifpresent(rediskey);
return gett(key, clazz, wrapper);
}
/**
* class类型获取数据,没有就写入
*/
public <t> t get(@nonnull string key, @nonnull duration duration, @nonnull class<t> clazz, @nonnull supplier<t> supplier) {
string rediskey = this.buildkey(key);
cachevalue<object> wrapper = cache.get(rediskey, k -> new cachevalue<>(supplier.get(), duration.tonanos()));
return gett(key, clazz, wrapper);
}
/**
* 指定typereference类型获取数据,没有就写入
*/
public <t> t get(@nonnull string key, @nonnull duration duration, @nonnull typereference<t> clazz, @nonnull supplier<t> supplier) {
string rediskey = this.buildkey(key);
cachevalue<object> wrapper = cache.get(rediskey, k -> new cachevalue<>(supplier.get(), duration.tonanos()));
if (wrapper == null || wrapper.value() == null) {
return null;
}
return getserialize(key, wrapper.value(), strvalue -> objectmapper.convertvalue(strvalue, clazz));
}
/**
* 写入数据
*/
public void set(@nonnull string key, @nonnull duration duration, @nonnull object value) {
string rediskey = this.buildkey(key);
cache.put(rediskey, new cachevalue<>(value, duration.tonanos()));
}
/**
* 移除数据
*/
public void del(@nonnull string key) {
string rediskey = this.buildkey(key);
cache.invalidate(rediskey);
}
/**
* 公共业务代码处理
*/
private <t> @nullable t gett(@notnull string key, @notnull class<t> clazz, cachevalue<object> wrapper) {
if (wrapper == null || wrapper.value() == null) {
return null;
}
object value = wrapper.value();
// 已经是目标类型
if (clazz.isinstance(value)) {
return clazz.cast(value);
}
return getserialize(key, value, strvalue -> objectmapper.convertvalue(strvalue, clazz));
}
/**
* 序列化操作
*/
private <t> @nullable t getserialize(@notnull string key, @notnull object object, function<object, t> deserialize) {
try {
return deserialize.apply(object);
} catch (exception e) {
log.error("本地缓存序列化失败,key:{}", key, e);
throw new runtimeexception(e);
}
}
/**
* redis的key拼接
*/
private string buildkey(string key) {
return "test:cache:" + key;
}
/**
* 存储数据和过期时间
*
* @param value 内容
* @param ttlnanos 过期时间
*/
public record cachevalue<t>(t value, long ttlnanos) {
}
}
// 使用方式
public static void main(string[] args) {
private static final typereference<list<string>> list_test = new typereference<>() {
};
@resource
private localcachetemplate localcachetemplate;
list<string> strings = localcachetemplate.get(rediskey.test_list, duration.ofminutes(60), list_test, () -> {
system.out.println("test");
return list.of("test");
});
}3️⃣复合缓存(composite cache / two-level cache)
概念:
复合缓存结合了 localcache + rediscache,常见模式是:
- 一级缓存(localcache):应用本地内存,快速响应。
- 二级缓存(rediscache):共享分布式缓存,保证跨实例一致性。
优点:
- 速度快:大部分热点数据在本地缓存,访问本地即可,减少网络请求。
- 一致性保证:redis 做二级缓存,确保跨实例数据一致。
- 降低压力:redis 热点数据由本地缓存缓冲,降低 redis 压力。
- 灵活性高:可以对不同数据设置不同的缓存策略(本地缓存过期时间短,redis 过期时间长)。
- 可扩展性强:支持高并发,结合分布式和本地缓存的优势。
缺点:
- 实现复杂,需要处理缓存一致性问题(如本地缓存与 redis 的同步)。
- 本地缓存失效策略设计不当可能导致缓存雪崩或脏数据。
import com.fasterxml.jackson.core.type.typereference;
import jakarta.annotation.resource;
import lombok.extern.slf4j.slf4j;
import org.jspecify.annotations.nonnull;
import org.springframework.stereotype.component;
import java.time.duration;
import java.util.function.supplier;
/**
* 本地缓存和redis缓存实现复合缓存工具类
*/
@slf4j
@component
public class compoundcachetemplate {
// 默认10分钟
public static duration duration = duration.ofminutes(10);
@resource
private rediscachetemplate rediscachetemplate;
@resource
private localcachetemplate localcachetemplate;
/**
* 获取key数据
*/
public <t> t get(@nonnull string key, @nonnull class<t> clazz) {
// 先查本地缓存
t value = localcachetemplate.get(key, clazz);
if (value != null) {
return value;
}
// 再查redis
value = rediscachetemplate.get(key, duration, clazz, () -> null);
if (value != null) {
// 回写本地缓存
localcachetemplate.set(key, duration.ofminutes(10), value);
}
return value;
}
/**
* class类型获取数据,没有就写入
*/
public <t> t get(@nonnull string key, @nonnull duration duration, @nonnull class<t> clazz, @nonnull supplier<t> supplier) {
// 先查本地缓存
t value = localcachetemplate.get(key, clazz);
if (value != null) {
return value;
}
// 查redis,如果没有就调用业务逻辑
value = rediscachetemplate.get(key, duration, clazz, supplier);
if (value != null) {
// 回写本地缓存
localcachetemplate.set(key, duration, value);
}
return value;
}
/**
* 指定typereference类型获取数据,没有就写入
*/
public <t> t get(@nonnull string key, @nonnull duration duration, @nonnull typereference<t> clazz, @nonnull supplier<t> supplier) {
// 先查本地缓存
t value = localcachetemplate.get(key, duration, clazz, () -> null);
if (value != null) {
return value;
}
// 查redis,如果没有就调用业务逻辑
value = rediscachetemplate.get(key, duration, clazz, supplier);
if (value != null) {
// 回写本地缓存
localcachetemplate.set(key, duration, value);
}
return value;
}
/**
* 写入数据
*/
public void set(@nonnull string key, @nonnull duration duration, @nonnull object value) {
// 同步写入本地缓存和redis
localcachetemplate.set(key, duration, value);
rediscachetemplate.set(key, duration, value);
}
/**
* 移除数据
*/
public void del(@nonnull string key) {
// 同步删除本地缓存和redis
localcachetemplate.del(key);
rediscachetemplate.del(key);
}
}
// 使用方式
public static void main(string[] args) {
private static final typereference<list<string>> list_test = new typereference<>() {
};
@resource
private compoundcachetemplate compoundcachetemplate;
list<string> strings = compoundcachetemplate.get(rediskey.test_list, duration.ofminutes(60), list_test, () -> {
system.out.println("test");
return list.of("test");
});
}到此这篇关于java缓存的使用rediscache、localcache、复合缓存的操作的文章就介绍到这了,更多相关java rediscache localcache 复合缓存内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论