学习时间: 4-5小时
学习目标: 掌握Redis高级特性,学会分布式缓存设计,理解缓存一致性策略
详细学习清单
✅ 第一部分:Redis高级数据结构与应用(60分钟)
1. Redis高级数据结构详解
Redis数据结构应用场景
java
// RedisDataStructuresDemo.java
package com.example.demo.redis;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.util.*;
public class RedisDataStructuresDemo {
private JedisPool jedisPool;
public RedisDataStructuresDemo() {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(20);
config.setMaxIdle(10);
config.setMinIdle(5);
config.setTestOnBorrow(true);
config.setTestOnReturn(true);
config.setTestWhileIdle(true);
this.jedisPool = new JedisPool(config, "localhost", 6379);
}
/**
* 1. 字符串操作 - 计数器、缓存
*/
public void stringOperations() {
try (Jedis jedis = jedisPool.getResource()) {
System.out.println("=== 字符串操作 ===");
// 基本操作
jedis.set("user:1001:name", "张三");
jedis.set("user:1001:age", "25");
jedis.setex("user:1001:token", 3600, "abc123token");
// 计数器操作
jedis.set("page:views", "0");
jedis.incr("page:views");
jedis.incrBy("page:views", 10);
jedis.decr("page:views");
// 批量操作
Map<String, String> userInfo = new HashMap<>();
userInfo.put("name", "李四");
userInfo.put("age", "30");
userInfo.put("email", "lisi@example.com");
jedis.hmset("user:1002", userInfo);
System.out.println("页面访问量: " + jedis.get("page:views"));
System.out.println("用户信息: " + jedis.hgetAll("user:1002"));
}
}
/**
* 2. 哈希操作 - 对象存储
*/
public void hashOperations() {
try (Jedis jedis = jedisPool.getResource()) {
System.out.println("\n=== 哈希操作 ===");
// 用户信息存储
jedis.hset("user:1003", "id", "1003");
jedis.hset("user:1003", "name", "王五");
jedis.hset("user:1003", "age", "28");
jedis.hset("user:1003", "city", "北京");
// 获取单个字段
String userName = jedis.hget("user:1003", "name");
System.out.println("用户名: " + userName);
// 获取所有字段
Map<String, String> allFields = jedis.hgetAll("user:1003");
System.out.println("用户完整信息: " + allFields);
// 字段存在性检查
boolean hasEmail = jedis.hexists("user:1003", "email");
System.out.println("是否有邮箱: " + hasEmail);
// 删除字段
jedis.hdel("user:1003", "age");
System.out.println("删除年龄后的信息: " + jedis.hgetAll("user:1003"));
}
}
/**
* 3. 列表操作 - 消息队列、最新列表
*/
public void listOperations() {
try (Jedis jedis = jedisPool.getResource()) {
System.out.println("\n=== 列表操作 ===");
// 最新消息列表
jedis.lpush("messages:user:1001", "消息1", "消息2", "消息3");
jedis.lpush("messages:user:1001", "消息4");
// 获取最新5条消息
List<String> latestMessages = jedis.lrange("messages:user:1001", 0, 4);
System.out.println("最新消息: " + latestMessages);
// 任务队列
jedis.rpush("task:queue", "任务A", "任务B", "任务C");
String task = jedis.lpop("task:queue");
System.out.println("处理任务: " + task);
System.out.println("剩余任务: " + jedis.lrange("task:queue", 0, -1));
// 列表长度
long listLength = jedis.llen("messages:user:1001");
System.out.println("消息列表长度: " + listLength);
}
}
/**
* 4. 集合操作 - 标签、去重
*/
public void setOperations() {
try (Jedis jedis = jedisPool.getResource()) {
System.out.println("\n=== 集合操作 ===");
// 用户标签
jedis.sadd("user:1001:tags", "VIP", "活跃用户", "北京用户");
jedis.sadd("user:1002:tags", "普通用户", "活跃用户", "上海用户");
jedis.sadd("user:1003:tags", "VIP", "新用户", "北京用户");
// 获取用户标签
Set<String> userTags = jedis.smembers("user:1001:tags");
System.out.println("用户1001标签: " + userTags);
// 标签交集 - 共同标签
Set<String> commonTags = jedis.sinter("user:1001:tags", "user:1003:tags");
System.out.println("用户1001和1003共同标签: " + commonTags);
// 标签并集 - 所有标签
Set<String> allTags = jedis.sunion("user:1001:tags", "user:1002:tags", "user:1003:tags");
System.out.println("所有用户标签: " + allTags);
// 标签差集
Set<String> diffTags = jedis.sdiff("user:1001:tags", "user:1002:tags");
System.out.println("用户1001独有标签: " + diffTags);
// 在线用户集合
jedis.sadd("online:users", "1001", "1002", "1003");
boolean isOnline = jedis.sismember("online:users", "1001");
System.out.println("用户1001是否在线: " + isOnline);
}
}
/**
* 5. 有序集合操作 - 排行榜、范围查询
*/
public void sortedSetOperations() {
try (Jedis jedis = jedisPool.getResource()) {
System.out.println("\n=== 有序集合操作 ===");
// 用户积分排行榜
jedis.zadd("user:score:rank", 1000, "user:1001");
jedis.zadd("user:score:rank", 1500, "user:1002");
jedis.zadd("user:score:rank", 800, "user:1003");
jedis.zadd("user:score:rank", 2000, "user:1004");
// 获取排行榜前3名
Set<String> topUsers = jedis.zrevrange("user:score:rank", 0, 2);
System.out.println("积分排行榜前3名: " + topUsers);
// 获取用户排名
Long rank = jedis.zrevrank("user:score:rank", "user:1002");
System.out.println("用户1002排名: " + (rank != null ? rank + 1 : "未上榜"));
// 获取积分范围用户
Set<String> scoreRangeUsers = jedis.zrangeByScore("user:score:rank", 1000, 1500);
System.out.println("积分1000-1500的用户: " + scoreRangeUsers);
// 增加用户积分
jedis.zincrby("user:score:rank", 500, "user:1001");
Double newScore = jedis.zscore("user:score:rank", "user:1001");
System.out.println("用户1001新积分: " + newScore);
// 文章点赞数排行榜
jedis.zadd("article:likes:rank", 50, "article:001");
jedis.zadd("article:likes:rank", 30, "article:002");
jedis.zadd("article:likes:rank", 80, "article:003");
Set<String> popularArticles = jedis.zrevrange("article:likes:rank", 0, 2);
System.out.println("热门文章: " + popularArticles);
}
}
/**
* 6. 位图操作 - 用户签到、统计
*/
public void bitmapOperations() {
try (Jedis jedis = jedisPool.getResource()) {
System.out.println("\n=== 位图操作 ===");
// 用户签到记录(按天)
String today = "2024-01-15";
String yesterday = "2024-01-14";
// 用户1001签到
jedis.setbit("sign:user:1001:" + today, 0, true);
jedis.setbit("sign:user:1001:" + yesterday, 0, true);
// 用户1002签到
jedis.setbit("sign:user:1002:" + today, 0, true);
// 检查用户是否签到
boolean hasSigned = jedis.getbit("sign:user:1001:" + today, 0);
System.out.println("用户1001今天是否签到: " + hasSigned);
// 统计签到用户数
long signCount = jedis.bitcount("sign:user:1001:" + today);
System.out.println("用户1001今日签到次数: " + signCount);
// 统计连续签到天数
int consecutiveDays = 0;
for (int i = 0; i < 7; i++) {
String date = "2024-01-" + (15 - i);
if (jedis.getbit("sign:user:1001:" + date, 0)) {
consecutiveDays++;
} else {
break;
}
}
System.out.println("用户1001连续签到天数: " + consecutiveDays);
}
}
public static void main(String[] args) {
RedisDataStructuresDemo demo = new RedisDataStructuresDemo();
demo.stringOperations();
demo.hashOperations();
demo.listOperations();
demo.setOperations();
demo.sortedSetOperations();
demo.bitmapOperations();
}
}
2. Redis发布订阅模式
Redis发布订阅实现
java
// RedisPubSubDemo.java
package com.example.demo.redis;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPubSub;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class RedisPubSubDemo {
private JedisPool jedisPool;
private ExecutorService executorService;
public RedisPubSubDemo() {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(20);
config.setMaxIdle(10);
config.setMinIdle(5);
this.jedisPool = new JedisPool(config, "localhost", 6379);
this.executorService = Executors.newFixedThreadPool(5);
}
/**
* 消息发布者
*/
public void publishMessages() {
try (Jedis jedis = jedisPool.getResource()) {
System.out.println("=== 消息发布者 ===");
// 发布订单消息
jedis.publish("order:events", "订单创建: ORDER-001");
jedis.publish("order:events", "订单支付: ORDER-001");
jedis.publish("order:events", "订单完成: ORDER-001");
// 发布用户消息
jedis.publish("user:events", "用户注册: USER-1001");
jedis.publish("user:events", "用户登录: USER-1001");
// 发布系统消息
jedis.publish("system:alerts", "系统维护通知");
jedis.publish("system:alerts", "数据库连接异常");
System.out.println("消息发布完成");
}
}
/**
* 订单事件订阅者
*/
public void subscribeOrderEvents() {
executorService.submit(() -> {
try (Jedis jedis = jedisPool.getResource()) {
jedis.subscribe(new JedisPubSub() {
@Override
public void onMessage(String channel, String message) {
System.out.println("订单事件订阅者收到消息: [" + channel + "] " + message);
// 处理订单事件
if (message.contains("订单创建")) {
handleOrderCreated(message);
} else if (message.contains("订单支付")) {
handleOrderPaid(message);
} else if (message.contains("订单完成")) {
handleOrderCompleted(message);
}
}
@Override
public void onSubscribe(String channel, int subscribedChannels) {
System.out.println("订阅订单事件频道: " + channel);
}
@Override
public void onUnsubscribe(String channel, int subscribedChannels) {
System.out.println("取消订阅订单事件频道: " + channel);
}
}, "order:events");
} catch (Exception e) {
e.printStackTrace();
}
});
}
/**
* 用户事件订阅者
*/
public void subscribeUserEvents() {
executorService.submit(() -> {
try (Jedis jedis = jedisPool.getResource()) {
jedis.subscribe(new JedisPubSub() {
@Override
public void onMessage(String channel, String message) {
System.out.println("用户事件订阅者收到消息: [" + channel + "] " + message);
// 处理用户事件
if (message.contains("用户注册")) {
handleUserRegistered(message);
} else if (message.contains("用户登录")) {
handleUserLoggedIn(message);
}
}
@Override
public void onSubscribe(String channel, int subscribedChannels) {
System.out.println("订阅用户事件频道: " + channel);
}
}, "user:events");
} catch (Exception e) {
e.printStackTrace();
}
});
}
/**
* 系统告警订阅者
*/
public void subscribeSystemAlerts() {
executorService.submit(() -> {
try (Jedis jedis = jedisPool.getResource()) {
jedis.subscribe(new JedisPubSub() {
@Override
public void onMessage(String channel, String message) {
System.out.println("系统告警订阅者收到消息: [" + channel + "] " + message);
// 处理系统告警
handleSystemAlert(message);
}
@Override
public void onSubscribe(String channel, int subscribedChannels) {
System.out.println("订阅系统告警频道: " + channel);
}
}, "system:alerts");
} catch (Exception e) {
e.printStackTrace();
}
});
}
// 事件处理方法
private void handleOrderCreated(String message) {
System.out.println("处理订单创建事件: " + message);
}
private void handleOrderPaid(String message) {
System.out.println("处理订单支付事件: " + message);
}
private void handleOrderCompleted(String message) {
System.out.println("处理订单完成事件: " + message);
}
private void handleUserRegistered(String message) {
System.out.println("处理用户注册事件: " + message);
}
private void handleUserLoggedIn(String message) {
System.out.println("处理用户登录事件: " + message);
}
private void handleSystemAlert(String message) {
System.out.println("处理系统告警: " + message);
}
public static void main(String[] args) throws InterruptedException {
RedisPubSubDemo demo = new RedisPubSubDemo();
// 启动订阅者
demo.subscribeOrderEvents();
demo.subscribeUserEvents();
demo.subscribeSystemAlerts();
// 等待订阅者启动
Thread.sleep(2000);
// 发布消息
demo.publishMessages();
// 等待消息处理
Thread.sleep(3000);
demo.executorService.shutdown();
}
}
✅ 第二部分:Spring Boot Redis集成(90分钟)
1. Redis配置与连接池
Redis配置类
java
// RedisConfig.java
package com.example.demo.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import java.time.Duration;
@Configuration
public class RedisConfig {
@Value("${spring.redis.host:localhost}")
private String host;
@Value("${spring.redis.port:6379}")
private int port;
@Value("${spring.redis.password:}")
private String password;
@Value("${spring.redis.database:0}")
private int database;
@Value("${spring.redis.timeout:2000}")
private int timeout;
@Value("${spring.redis.lettuce.pool.max-active:20}")
private int maxActive;
@Value("${spring.redis.lettuce.pool.max-idle:10}")
private int maxIdle;
@Value("${spring.redis.lettuce.pool.min-idle:5}")
private int minIdle;
@Value("${spring.redis.lettuce.pool.max-wait:2000}")
private long maxWait;
/**
* Redis连接工厂配置
*/
@Bean
public RedisConnectionFactory redisConnectionFactory() {
// Redis单机配置
RedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration();
redisConfig.setHostName(host);
redisConfig.setPort(port);
redisConfig.setPassword(password);
redisConfig.setDatabase(database);
// 连接池配置
GenericObjectPoolConfig<Object> poolConfig = new GenericObjectPoolConfig<>();
poolConfig.setMaxTotal(maxActive);
poolConfig.setMaxIdle(maxIdle);
poolConfig.setMinIdle(minIdle);
poolConfig.setMaxWaitMillis(maxWait);
poolConfig.setTestOnBorrow(true);
poolConfig.setTestOnReturn(true);
poolConfig.setTestWhileIdle(true);
// Lettuce客户端配置
LettucePoolingClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder()
.poolConfig(poolConfig)
.commandTimeout(Duration.ofMillis(timeout))
.shutdownTimeout(Duration.ofMillis(100))
.build();
return new LettuceConnectionFactory(redisConfig, clientConfig);
}
/**
* RedisTemplate配置
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
// 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(mapper);
// 使用StringRedisSerializer来序列化和反序列化redis的key值
StringRedisSerializer stringSerializer = new StringRedisSerializer();
// 设置key和value的序列化规则
template.setKeySerializer(stringSerializer);
template.setValueSerializer(serializer);
template.setHashKeySerializer(stringSerializer);
template.setHashValueSerializer(serializer);
// 设置支持事务
template.setEnableTransactionSupport(true);
template.afterPropertiesSet();
return template;
}
/**
* 字符串RedisTemplate
*/
@Bean
public RedisTemplate<String, String> stringRedisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, String> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
StringRedisSerializer stringSerializer = new StringRedisSerializer();
template.setKeySerializer(stringSerializer);
template.setValueSerializer(stringSerializer);
template.setHashKeySerializer(stringSerializer);
template.setHashValueSerializer(stringSerializer);
template.afterPropertiesSet();
return template;
}
}
2. Redis工具类
Redis工具类
java
// RedisUtil.java
package com.example.demo.util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.*;
import java.util.concurrent.TimeUnit;
@Component
public class RedisUtil {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 指定缓存失效时间
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据key获取过期时间
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判断key是否存在
*/
public boolean hasKey(String key) {
try {
return Boolean.TRUE.equals(redisTemplate.hasKey(key));
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除缓存
*/
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
// ============================String=============================
/**
* 普通缓存获取
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通缓存放入并设置时间
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 递增
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 递减
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
// ================================Map=================================
/**
* HashGet
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* 获取hashKey对应的所有键值
*/
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* HashSet
*/
public boolean hmset(String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* HashSet 并设置时间
*/
public boolean hmset(String key, Map<String, Object> map, long time) {
try {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*/
public boolean hset(String key, String item, Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*/
public boolean hset(String key, String item, Object value, long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除hash表中的值
*/
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
}
/**
* 判断hash表中是否有该项的值
*/
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
}
// ============================set=============================
/**
* 根据key获取Set中的所有值
*/
public Set<Object> sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 根据value从一个set中查询,是否存在
*/
public boolean sHasKey(String key, Object value) {
try {
return Boolean.TRUE.equals(redisTemplate.opsForSet().isMember(key, value));
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将数据放入set缓存
*/
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 将set数据放入缓存
*/
public long sSetAndTime(String key, long time, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0) {
expire(key, time);
}
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 获取set缓存的长度
*/
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 移除值为value的
*/
public long setRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
// ===============================list=================================
/**
* 获取list缓存的内容
*/
public List<Object> lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取list缓存的长度
*/
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 通过索引 获取list中的值
*/
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 将list放入缓存
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*/
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*/
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*/
public boolean lSet(String key, List<Object> value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据索引修改list中的某条数据
*/
public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 移除N个值为value
*/
public long lRemove(String key, long count, Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, count, value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
// ================================zset=================================
/**
* 添加元素到有序集合
*/
public boolean zAdd(String key, Object value, double score) {
try {
return Boolean.TRUE.equals(redisTemplate.opsForZSet().add(key, value, score));
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 获取有序集合的成员数
*/
public long zCard(String key) {
try {
return redisTemplate.opsForZSet().zCard(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 获取有序集合中指定成员的分数
*/
public Double zScore(String key, Object value) {
try {
return redisTemplate.opsForZSet().score(key, value);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取有序集合中指定排名范围的成员
*/
public Set<Object> zRange(String key, long start, long end) {
try {
return redisTemplate.opsForZSet().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取有序集合中指定分数范围的成员
*/
public Set<Object> zRangeByScore(String key, double min, double max) {
try {
return redisTemplate.opsForZSet().rangeByScore(key, min, max);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 分布式锁
*/
public boolean lock(String key, String value, long expireTime) {
try {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) " +
"else " +
"return 0 " +
"end";
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptText(script);
redisScript.setResultType(Long.class);
Long result = redisTemplate.execute(redisScript, Collections.singletonList(key), value);
return result != null && result == 1;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 获取分布式锁
*/
public boolean tryLock(String key, String value, long expireTime) {
try {
Boolean result = redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, TimeUnit.SECONDS);
return Boolean.TRUE.equals(result);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}
3. 缓存服务实现
缓存服务类
java
// CacheService.java
package com.example.demo.service;
import com.example.demo.util.RedisUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Slf4j
@Service
public class CacheService {
@Autowired
private RedisUtil redisUtil;
@Autowired
private ObjectMapper objectMapper;
/**
* 缓存用户信息
*/
public void cacheUserInfo(Long userId, Object userInfo) {
String key = "user:info:" + userId;
try {
String json = objectMapper.writeValueAsString(userInfo);
redisUtil.set(key, json, 3600); // 缓存1小时
log.info("缓存用户信息成功: {}", userId);
} catch (JsonProcessingException e) {
log.error("缓存用户信息失败: {}", userId, e);
}
}
/**
* 获取用户信息
*/
public <T> T getUserInfo(Long userId, Class<T> clazz) {
String key = "user:info:" + userId;
Object value = redisUtil.get(key);
if (value != null) {
try {
return objectMapper.readValue(value.toString(), clazz);
} catch (JsonProcessingException e) {
log.error("获取用户信息失败: {}", userId, e);
}
}
return null;
}
/**
* 缓存商品信息
*/
public void cacheProductInfo(Long productId, Object productInfo) {
String key = "product:info:" + productId;
try {
String json = objectMapper.writeValueAsString(productInfo);
redisUtil.set(key, json, 1800); // 缓存30分钟
log.info("缓存商品信息成功: {}", productId);
} catch (JsonProcessingException e) {
log.error("缓存商品信息失败: {}", productId, e);
}
}
/**
* 获取商品信息
*/
public <T> T getProductInfo(Long productId, Class<T> clazz) {
String key = "product:info:" + productId;
Object value = redisUtil.get(key);
if (value != null) {
try {
return objectMapper.readValue(value.toString(), clazz);
} catch (JsonProcessingException e) {
log.error("获取商品信息失败: {}", productId, e);
}
}
return null;
}
/**
* 缓存热点数据
*/
public void cacheHotData(String dataType, String dataKey, Object data) {
String key = "hot:data:" + dataType + ":" + dataKey;
try {
String json = objectMapper.writeValueAsString(data);
redisUtil.set(key, json, 600); // 缓存10分钟
log.info("缓存热点数据成功: {}:{}", dataType, dataKey);
} catch (JsonProcessingException e) {
log.error("缓存热点数据失败: {}:{}", dataType, dataKey, e);
}
}
/**
* 获取热点数据
*/
public <T> T getHotData(String dataType, String dataKey, Class<T> clazz) {
String key = "hot:data:" + dataType + ":" + dataKey;
Object value = redisUtil.get(key);
if (value != null) {
try {
return objectMapper.readValue(value.toString(), clazz);
} catch (JsonProcessingException e) {
log.error("获取热点数据失败: {}:{}", dataType, dataKey, e);
}
}
return null;
}
/**
* 删除缓存
*/
public void deleteCache(String key) {
redisUtil.del(key);
log.info("删除缓存成功: {}", key);
}
/**
* 批量删除缓存
*/
public void deleteCacheByPattern(String pattern) {
// 这里需要实现根据模式删除缓存的逻辑
log.info("批量删除缓存: {}", pattern);
}
/**
* 缓存计数器
*/
public long incrementCounter(String key, long delta) {
return redisUtil.incr(key, delta);
}
/**
* 获取计数器值
*/
public long getCounter(String key) {
Object value = redisUtil.get(key);
return value != null ? Long.parseLong(value.toString()) : 0;
}
/**
* 设置计数器过期时间
*/
public void setCounterExpire(String key, long seconds) {
redisUtil.expire(key, seconds);
}
}
✅ 第三部分:分布式缓存一致性(90分钟)
1. 缓存一致性策略
缓存一致性策略实现
java
// CacheConsistencyStrategy.java
package com.example.demo.cache;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
public class CacheConsistencyStrategy {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 1. Cache Aside模式 - 旁路缓存
* 读:先读缓存,缓存没有则读数据库,然后写入缓存
* 写:先写数据库,然后删除缓存
*/
public class CacheAsideStrategy {
/**
* 读取数据
*/
public <T> T read(String cacheKey, Class<T> clazz, DataLoader<T> dataLoader) {
// 1. 先读缓存
T cachedData = (T) redisTemplate.opsForValue().get(cacheKey);
if (cachedData != null) {
log.info("缓存命中: {}", cacheKey);
return cachedData;
}
// 2. 缓存未命中,读数据库
log.info("缓存未命中,从数据库读取: {}", cacheKey);
T data = dataLoader.load();
// 3. 写入缓存
if (data != null) {
redisTemplate.opsForValue().set(cacheKey, data, 3600, TimeUnit.SECONDS);
log.info("数据写入缓存: {}", cacheKey);
}
return data;
}
/**
* 写入数据
*/
public <T> void write(String cacheKey, T data, DataWriter<T> dataWriter) {
// 1. 先写数据库
dataWriter.write(data);
log.info("数据写入数据库: {}", cacheKey);
// 2. 删除缓存
redisTemplate.delete(cacheKey);
log.info("删除缓存: {}", cacheKey);
}
}
/**
* 2. Write Through模式 - 写穿透
* 写:先写缓存,缓存负责同步写入数据库
* 读:从缓存读取
*/
public class WriteThroughStrategy {
/**
* 写入数据
*/
public <T> void write(String cacheKey, T data, DataWriter<T> dataWriter) {
// 1. 先写缓存
redisTemplate.opsForValue().set(cacheKey, data, 3600, TimeUnit.SECONDS);
log.info("数据写入缓存: {}", cacheKey);
// 2. 缓存负责写入数据库
dataWriter.write(data);
log.info("数据写入数据库: {}", cacheKey);
}
/**
* 读取数据
*/
public <T> T read(String cacheKey, Class<T> clazz) {
T data = (T) redisTemplate.opsForValue().get(cacheKey);
if (data != null) {
log.info("缓存命中: {}", cacheKey);
} else {
log.info("缓存未命中: {}", cacheKey);
}
return data;
}
}
/**
* 3. Write Behind模式 - 写回
* 写:只写缓存,异步批量写入数据库
* 读:从缓存读取
*/
public class WriteBehindStrategy {
/**
* 写入数据
*/
public <T> void write(String cacheKey, T data) {
// 只写缓存,异步写入数据库
redisTemplate.opsForValue().set(cacheKey, data, 3600, TimeUnit.SECONDS);
log.info("数据写入缓存: {}", cacheKey);
// 异步写入数据库(这里简化处理)
asyncWriteToDatabase(cacheKey, data);
}
/**
* 异步写入数据库
*/
private <T> void asyncWriteToDatabase(String cacheKey, T data) {
// 实际项目中应该使用消息队列或线程池异步处理
log.info("异步写入数据库: {}", cacheKey);
}
}
/**
* 4. 双删策略 - 解决缓存不一致问题
*/
public class DoubleDeleteStrategy {
/**
* 更新数据
*/
public <T> void update(String cacheKey, T data, DataWriter<T> dataWriter) {
// 1. 删除缓存
redisTemplate.delete(cacheKey);
log.info("第一次删除缓存: {}", cacheKey);
// 2. 更新数据库
dataWriter.write(data);
log.info("更新数据库: {}", cacheKey);
// 3. 延迟删除缓存
delayDeleteCache(cacheKey);
}
/**
* 延迟删除缓存
*/
private void delayDeleteCache(String cacheKey) {
// 延迟删除,避免并发问题
new Thread(() -> {
try {
Thread.sleep(1000); // 延迟1秒
redisTemplate.delete(cacheKey);
log.info("延迟删除缓存: {}", cacheKey);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}