目录
4. 使用?Spring Data Redis 中操作 Redis
SpringDataRedis 快速入门
/**
* Redis 操作测试
* @author 乐小鑫
* @version 1.0
*/
@SpringBootTest
public class RedisTest {
@Resource
private RedisTemplate redisTemplate;
@Test
void test() {
ValueOperations valueOperations = redisTemplate.opsForValue();
// 增
valueOperations.set("ghostString", "dog");
valueOperations.set("ghostInt", 1);
valueOperations.set("ghostDouble", 2.0);
User user = new User();
user.setId(1L);
user.setUsername("ghost");
valueOperations.set("ghostUser", user);
// 查
Object ghost = valueOperations.get("ghostString");
Assertions.assertTrue("dog".equals((String) ghost));
ghost = valueOperations.get("ghostInt");
Assertions.assertTrue(1 == (Integer) ghost);
ghost = valueOperations.get("ghostDouble");
Assertions.assertTrue(2.0 == (Double) ghost);
System.out.println(valueOperations.get("ghostUser"));
valueOperations.set("ghostString", "dog");
redisTemplate.delete("ghostString");
}
}
/**
* RedisTemplate 配置
* @author 乐小鑫
* @version 1.0
*/
@Configuration
public class RedisTemplateConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
redisTemplate.setKeySerializer(RedisSerializer.string());
return redisTemplate;
}
}
直接使用单层的 key 作为键可能会对其他业务产生影响,例如以 username 为 key,其他业务可能也用 username 为 key,用的是同一台 Redis 服务器时会影响其他业务的数据。
设计缓存首先的原则是不要和其他 key 发生冲突
/**
* 用户推荐
* @param request
* @return 用户列表
*/
@GetMapping("/recommend")
public BaseResponse<Page<User>> recommendUsers(long pageSize, long pageNum, HttpServletRequest request) {
log.info("推荐用户列表");
ValueOperations valueOperations = redisTemplate.opsForValue();
User loginUser = userService.getLoginUser(request);
String key = String.format("langhua:user:recommend:%s", loginUser.getId());
Page<User> userPage = (Page<User>) valueOperations.get(key);
// 查缓存,有直接返回缓存数据
if (userPage != null) {
return ResultUtils.success(userPage);
}
// 没有缓存数据,才查询数据库
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
userPage = userService.page(new Page<>((pageNum - 1) * pageSize, pageSize), queryWrapper);// 查询所有用户
// 将查询出来的数据写入缓存
try {
valueOperations.set(key,userPage);
} catch (Exception e) {
log.error("redis key set error", e);
}
return ResultUtils.success(userPage);
}
场景分析:当缓存里没有数据时,第一个用户进来需要查询数据库才能看到响应数据,页面响应时间较久,对有些用户不友好
解决方案:在所有用户进入之前预先缓存好数据,程序员自己加载缓存,而不是等到用户进入再触发
注意?在分析一个技术的优缺点时,要从整个项目从 0 到 1 的整个软件生命周期去考虑(需求分析开始到项目部署上线和维护)
使用定时任务,每天刷新所有用户的推荐列表(缓存预热)
- 缓存预热的意义(新增数据少、总数据量大)
- 缓存占用空间不能太大,需要给其他缓存预留空间
- 缓存数据的周期(根据业务需求来选择)
/**
* 缓存预热定时任务
* @author 乐小鑫
* @version 1.0
*/
@Component
@Slf4j
public class PreCacheUser {
@Resource
private RedisTemplate redisTemplate;
@Resource
private UserService userService;
List<Long> mainUserList = Arrays.asList(3L);// 重要用户列表,为该列表的用户开启缓存预热
@Scheduled(cron = "0 5 21 ? * * ")
public void doPreCacheUser() {
// 查出用户存到 Redis 中
for (Long userId : mainUserList) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
Page<User> userPage = userService.page(new Page<>(1, 20), queryWrapper);// 查询所有用户
String key = String.format("langhua:user:recommend:%s", userId);
ValueOperations valueOperations = redisTemplate.opsForValue();
// 将查询出来的数据写入缓存
try {
valueOperations.set(key,userPage,30000, TimeUnit.MILLISECONDS);
} catch (Exception e) {
log.error("redis key set error", e);
}
}
}
}