前言:
此时咱们对“redis的使用和配置”可能比较注意,你们都想要剖析一些“redis的使用和配置”的相关知识。那么小编同时在网络上汇集了一些对于“redis的使用和配置””的相关文章,希望大家能喜欢,大家快快来学习一下吧!1. 简介
当我们对redis的基本知识有一定的了解后,我们再通过实战的角度学习一下在SpringBoot环境下,如何优雅的使用redis。
我们通过使用SpringBoot内置的Redis注解(文章最后有解释)来操作User相关的信息,
再通过Redis工具类的方式操作Role相关信息来全面的学习Redis的使用。
嫌篇幅太长的 可以直接跳到2.6查看具体逻辑即可。
2. 开撸2.1 项目结构结构说明:
├── src│ └── main│ ├── java│ │ └── com│ │ └── ldx│ │ └── redis│ │ ├── RedisApplication.java # 启动类│ │ ├── config│ │ │ └── RedisConfig.java # redis 配置类│ │ ├── constant│ │ │ └── CacheConstant.java # 缓存key常量类│ │ ├── controller│ │ │ ├── RoleController.java # 角色管理控制器│ │ │ └── UserController.java # 用户管理控制器│ │ ├── entity│ │ │ ├── SysRole.java # 角色entity│ │ │ └── SysUser.java # 用户entity│ │ ├── mapper│ │ │ ├── SysRoleMapper.java # 角色持久层│ │ │ └── SysUserMapper.java # 用户持久层│ │ ├── service│ │ │ ├── SysRoleService.java # 角色接口层│ │ │ ├── SysUserService.java # 用户接口层│ │ │ └── impl│ │ │ ├── SysRoleServiceImpl.java # 角色接口实现层│ │ │ └── SysUserServiceImpl.java # 用户接口实现层│ │ └── util│ │ └── RedisUtil.java # redis 工具类│ └── resources│ └── application.yaml # 系统配置文件└── pom.xml # 依赖管理2.2 导入依赖
<?xml version="1.0" encoding="UTF-8"?><project xmlns="; xmlns:xsi="; xsi:schemaLocation=" ;> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.3</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.ldx</groupId> <artifactId>redis</artifactId> <version>0.0.1-SNAPSHOT</version> <name>redis</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <!--spring-web--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- lettuce pool --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <!-- mybatis-plus --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.2</version> </dependency> <!-- mysql驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- lombok 工具包 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build></project>2.3 项目基本配置2.3.1 application.yaml
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username: root password: 123456 type: com.zaxxer.hikari.HikariDataSource # redis 配置 redis: # 地址 host: localhost # 端口,默认为6379 port: 6379 # 密码 password: # 连接超时时间 timeout: 10s lettuce: pool: # 连接池中的最小空闲连接 min-idle: 0 # 连接池中的最大空闲连接 max-idle: 8 # 连接池的最大数据库连接数 max-active: 8 # #连接池最大阻塞等待时间(使用负值表示没有限制) max-wait: -1msmybatis-plus: # 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置 mapper-locations: classpath*:mapper/*.xml # 设置别名包扫描路径,通过该属性可以给包中的类注册别名 type-aliases-package: com.ldx.redis.entity configuration: # 控制台sql打印 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl# 日志配置logging: level: com.ldx.redis.service.impl: debug org.springframework: warn2.3.2 启动类
@EnableCaching :激活缓存支持
@MapperScan : 扫描mapper接口层
import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cache.annotation.EnableCaching;/** * 启动类 * @author ludangxin * @date 2021/8/11 */@EnableCaching@MapperScan(basePackages = "com.ldx.redis.mapper")@SpringBootApplicationpublic class RedisApplication { public static void main(String[] args) { SpringApplication.run(RedisApplication.class, args); }}2.4 redis配置2.4.1 RedisConfig
我们除了在application.yaml中加入redis的基本配置外,一般还需要配置redis key和value的序列化方式,如下:
注解:其默认的序列化方式为 JdkSerializationRedisSerializer ,这种方式跨语言和可读性都不太好,我们将其切换为 Jackson2JsonRedisSerializer 。可以使用 entryTtl() 为对应的模块设置过期时长。
redisTemplate:参考 redisTemplate() 。
import com.fasterxml.jackson.annotation.JsonAutoDetect;import com.fasterxml.jackson.annotation.PropertyAccessor;import com.fasterxml.jackson.databind.ObjectMapper;import com.ldx.redis.constant.CacheConstant;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.cache.RedisCacheConfiguration;import org.springframework.data.redis.cache.RedisCacheManager;import org.springframework.data.redis.cache.RedisCacheWriter;import org.springframework.data.redis.connection.RedisConnectionFactory;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;import org.springframework.data.redis.serializer.RedisSerializationContext;import org.springframework.data.redis.serializer.StringRedisSerializer;import java.time.Duration;import java.util.HashMap;import java.util.Map;/** * redis配置类 * @author ludangxin * @date 2021/8/11 */@Configurationpublic class RedisConfig { @Bean public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) { //设置不同cacheName的过期时间 Map<String, RedisCacheConfiguration> configurations = new HashMap<>(16); // 序列化方式 Jackson2JsonRedisSerializer<Object> jsonRedisSerializer = getJsonRedisSerializer(); RedisSerializationContext.SerializationPair<Object> serializationPair = RedisSerializationContext.SerializationPair.fromSerializer(jsonRedisSerializer); // 默认的缓存时间 Duration defaultTtl = Duration.ofSeconds(20L); // 用户模块的缓存时间 Duration userTtl = Duration.ofSeconds(50L); // 默认的缓存配置 RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() //.entryTtl(defaultTtl) .serializeValuesWith(serializationPair); // 自定义用户模块的缓存配置 自定义的配置可以覆盖默认配置(当前的模块) configurations.put(CacheConstant.USER_CACHE_NAME, RedisCacheConfiguration.defaultCacheConfig() //.entryTtl(userTtl) .serializeValuesWith(serializationPair) ); return RedisCacheManager.builder(RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory)) .cacheDefaults(redisCacheConfiguration) .withInitialCacheConfigurations(configurations) // 事物支持 .transactionAware() .build(); } @Bean public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(factory); Jackson2JsonRedisSerializer<Object> jsonRedisSerializer = getJsonRedisSerializer(); StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); // key采用String的序列化方式 template.setKeySerializer(stringRedisSerializer); // hash的key也采用String的序列化方式 template.setHashKeySerializer(stringRedisSerializer); // value序列化方式采用jackson template.setValueSerializer(jsonRedisSerializer); // hash的value序列化方式采用jackson template.setHashValueSerializer(jsonRedisSerializer); // 支持事物 //template.setEnableTransactionSupport(true); template.afterPropertiesSet(); return template; } /** * 设置jackson的序列化方式 */ private Jackson2JsonRedisSerializer<Object> getJsonRedisSerializer() { Jackson2JsonRedisSerializer<Object> redisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); redisSerializer.setObjectMapper(om); return redisSerializer; }}2.4.1 CacheConstant
我们为了防止redis中key的重复,尽量会给不同的数据主体加上不同的前缀,这样我们在查看和统计的时候也方便操作。
/** * 缓存key 常量类 * @author ludangxin * @date 2021/8/11 */public interface CacheConstant { /** * 用户cache name */ String USER_CACHE_NAME = "user_cache"; /** * 用户信息缓存key前缀 */ String USER_CACHE_KEY_PREFIX = "user_"; /** * 角色cache name */ String ROLE_CACHE_NAME = "role_cache"; /** * 角色信息缓存key前缀 */ String ROLE_CACHE_KEY_PREFIX = "role_"; /** * 获取角色cache key * @param suffix 后缀 * @return key */ static String getRoleCacheKey(String suffix) { return ROLE_CACHE_NAME + "::" + ROLE_CACHE_KEY_PREFIX + suffix; }}2.4.2 RedisUtil
import lombok.RequiredArgsConstructor;import org.springframework.data.redis.core.*;import org.springframework.stereotype.Component;import java.util.*;import java.util.concurrent.TimeUnit;/** * spring redis 工具类 * @author ludangxin **/@Component@RequiredArgsConstructor@SuppressWarnings(value = { "unchecked", "rawtypes" })public class RedisUtil { public final RedisTemplate redisTemplate; /** * 缓存基本的对象,Integer、String、实体类等 * @param key 缓存的键值 * @param value 缓存的值 * @return 缓存的对象 */ public <T> ValueOperations<String, T> setCacheObject(String key, T value) { ValueOperations<String, T> operation = redisTemplate.opsForValue(); operation.set(key, value); return operation; } /** * 缓存基本的对象,Integer、String、实体类等 * @param key 缓存的键值 * @param value 缓存的值 * @param timeout 时间 * @param timeUnit 时间颗粒度 * @return 缓存的对象 */ public <T> ValueOperations<String, T> setCacheObject(String key, T value, Integer timeout, TimeUnit timeUnit) { ValueOperations<String, T> operation = redisTemplate.opsForValue(); operation.set(key, value, timeout, timeUnit); return operation; } /** * 获得缓存的基本对象。 * @param key 缓存键值 * @return 缓存键值对应的数据 */ public <T> T getCacheObject(String key) { ValueOperations<String, T> operation = redisTemplate.opsForValue(); return operation.get(key); } /** * 删除单个对象 * @param key */ public void deleteObject(String key) { redisTemplate.delete(key); } /** * 删除集合对象 * @param collection */ public void deleteObject(Collection collection) { redisTemplate.delete(collection); } /** * 缓存List数据 * @param key 缓存的键值 * @param dataList 待缓存的List数据 * @return 缓存的对象 */ public <T> ListOperations<String, T> setCacheList(String key, List<T> dataList) { ListOperations listOperation = redisTemplate.opsForList(); if (null != dataList) { int size = dataList.size(); for (int i = 0; i < size; i++) { listOperation.leftPush(key, dataList.get(i)); } } return listOperation; } /** * 获得缓存的list对象 * @param key 缓存的键值 * @return 缓存键值对应的数据 */ public <T> List<T> getCacheList(String key) { List<T> dataList = new ArrayList<T>(); ListOperations<String, T> listOperation = redisTemplate.opsForList(); Long size = listOperation.size(key); for (int i = 0; i < size; i++) { dataList.add(listOperation.index(key, i)); } return dataList; } /** * 缓存Set * @param key 缓存键值 * @param dataSet 缓存的数据 * @return 缓存数据的对象 */ public <T> BoundSetOperations<String, T> setCacheSet(String key, Set<T> dataSet) { BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key); Iterator<T> it = dataSet.iterator(); while (it.hasNext()) { setOperation.add(it.next()); } return setOperation; } /** * 获得缓存的set * @param key * @return */ public <T> Set<T> getCacheSet(String key) { Set<T> dataSet = new HashSet<T>(); BoundSetOperations<String, T> operation = redisTemplate.boundSetOps(key); dataSet = operation.members(); return dataSet; } /** * 缓存Map * @param key * @param dataMap * @return */ public <T> HashOperations<String, String, T> setCacheMap(String key, Map<String, T> dataMap) { HashOperations hashOperations = redisTemplate.opsForHash(); if (null != dataMap) { for (Map.Entry<String, T> entry : dataMap.entrySet()) { hashOperations.put(key, entry.getKey(), entry.getValue()); } } return hashOperations; } /** * 获得缓存的Map * @param key * @return */ public <T> Map<String, T> getCacheMap(String key) { Map<String, T> map = redisTemplate.opsForHash().entries(key); return map; } /** * 获得缓存的基本对象列表 * @param pattern 字符串前缀 * @return 对象列表 */ public Collection<String> keys(String pattern) { return redisTemplate.keys(pattern); }}2.5 controller2.5.1 UserController
import com.ldx.redis.entity.SysUser;import com.ldx.redis.service.SysUserService;import lombok.RequiredArgsConstructor;import org.springframework.web.bind.annotation.*;import java.util.List;/** * 用户管理 * @author ludangxin * @date 2021/8/11 */@RestController@RequestMapping("user")@RequiredArgsConstructorpublic class UserController { private final SysUserService userService; @GetMapping public List<SysUser> queryAll() { return userService.queryAll(); } @GetMapping("{userId}") public SysUser getUserInfo(@PathVariable Long userId) { return userService.getUserInfo(userId); } @PostMapping public String add(@RequestBody SysUser user) { userService.add(user); return "新增成功~"; } @PutMapping("{userId}") public String update(@PathVariable Long userId, @RequestBody SysUser user) { userService.update(userId, user); return "更新成功~"; } @DeleteMapping("{userId}") public String del(@PathVariable Long userId) { userService.delete(userId); return "删除成功~"; }}2.5.2 RoleController
import com.ldx.redis.entity.SysRole;import com.ldx.redis.service.SysRoleService;import lombok.RequiredArgsConstructor;import org.springframework.web.bind.annotation.*;import java.util.List;/** * 角色管理 * @author ludangxin * @date 2021/8/12 */@RestController@RequestMapping("role")@RequiredArgsConstructorpublic class RoleController { private final SysRoleService roleService; @GetMapping public List<SysRole> queryAll() { return roleService.queryAll(); } @GetMapping("{roleId}") public SysRole getUserInfo(@PathVariable Long roleId) { return roleService.getRoleInfo(roleId); } @PostMapping public String add(@RequestBody SysRole role) { roleService.add(role); return "新增成功~"; } @PutMapping("{roleId}") public String update(@PathVariable Long roleId, @RequestBody SysRole role) { roleService.update(roleId, role); return "更新成功~"; } @DeleteMapping("{roleId}") public String del(@PathVariable Long roleId) { roleService.delete(roleId); return "删除成功~"; }}2.6 service.impl2.6.1 UserServiceImpl
优雅的使用redis注解实现对数据的缓存
@Cacheable:unless :当 unless 成立时则不缓存。这里判断 size 主要是不想将空值存入redis。
CacheConstant.USER_CACHE_KEY_PREFIX + "' + #userId" :其key = 指定前缀 + 当前方法实参(userId)。
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;import com.ldx.redis.constant.CacheConstant;import com.ldx.redis.entity.SysUser;import com.ldx.redis.mapper.SysUserMapper;import com.ldx.redis.service.SysUserService;import lombok.RequiredArgsConstructor;import lombok.extern.slf4j.Slf4j;import org.springframework.cache.annotation.CacheConfig;import org.springframework.cache.annotation.CacheEvict;import org.springframework.cache.annotation.Cacheable;import org.springframework.cache.annotation.Caching;import org.springframework.stereotype.Service;import java.util.List;/** * 用户管理实现 * @author ludangxin * @date 2021/8/11 */@Slf4j@Service@RequiredArgsConstructor@CacheConfig(cacheNames = CacheConstant.USER_CACHE_NAME)public class SysUserServiceImpl implements SysUserService { private final SysUserMapper userMapper; @Override @Cacheable(key = "'" + CacheConstant.USER_CACHE_KEY_PREFIX + "all'", unless = "#result.size() == 0") public List<SysUser> queryAll() { log.debug("查询全部用户信息~"); LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>(); return userMapper.selectList(queryWrapper); } @Override @Cacheable(key = "'" + CacheConstant.USER_CACHE_KEY_PREFIX + "' + #userId", unless = "#result == null") public SysUser getUserInfo(Long userId) { log.debug("查询用户:{} 详情", userId); return userMapper.selectById(userId); } @Override @CacheEvict(key = "'" + CacheConstant.USER_CACHE_KEY_PREFIX + "all'") public void add(SysUser user) { log.debug("新增用户:{}", user.getNickName()); userMapper.insert(user); } @Override @Caching(evict = {@CacheEvict(key = "'" + CacheConstant.USER_CACHE_KEY_PREFIX + "all'"), @CacheEvict(key = "'" + CacheConstant.USER_CACHE_KEY_PREFIX + "' + #userId") }) public void update(Long userId, SysUser user) { log.debug("更新用户:{}", user.getNickName()); user.setId(userId); userMapper.updateById(user); } @Override @Caching(evict = {@CacheEvict(key = "'" + CacheConstant.USER_CACHE_KEY_PREFIX + "all'"), @CacheEvict(key = "'" + CacheConstant.USER_CACHE_KEY_PREFIX + "' + #userId") }) public void delete(Long userId) { log.debug("删除用户:{}", userId); userMapper.deleteById(userId); }}2.6.2 SysRoleServiceImpl
使用redis工具类实现对数据的缓存。
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;import com.ldx.redis.constant.CacheConstant;import com.ldx.redis.entity.SysRole;import com.ldx.redis.mapper.SysRoleMapper;import com.ldx.redis.service.SysRoleService;import com.ldx.redis.util.RedisUtil;import lombok.RequiredArgsConstructor;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Service;import org.springframework.util.CollectionUtils;import java.util.Collections;import java.util.List;import java.util.Objects;/** * 角色管理 * @author ludangxin * @date 2021/8/11 */@Slf4j@Service@RequiredArgsConstructorpublic class SysRoleServiceImpl implements SysRoleService { private final SysRoleMapper roleMapper; private final RedisUtil redisUtil; String allKey = CacheConstant.getRoleCacheKey("all"); @Override public List<SysRole> queryAll() { List<SysRole> roles = redisUtil.getCacheList(allKey); if(!CollectionUtils.isEmpty(roles)) { return roles; } log.debug("查询全部角色信息~"); LambdaQueryWrapper<SysRole> queryWrapper = new LambdaQueryWrapper<>(); List<SysRole> sysRoles = roleMapper.selectList(queryWrapper); if(CollectionUtils.isEmpty(sysRoles)) { return Collections.emptyList(); } redisUtil.setCacheList(allKey, sysRoles); return sysRoles; } @Override public SysRole getRoleInfo(Long roleId) { String roleCacheKey = CacheConstant.getRoleCacheKey(String.valueOf(roleId)); SysRole role = redisUtil.getCacheObject(roleCacheKey); if(Objects.nonNull(role)) { return role; } log.debug("查询角色:{} 详情", roleId); SysRole sysRole = roleMapper.selectById(roleId); if(Objects.isNull(sysRole)) { return null; } redisUtil.setCacheObject(roleCacheKey, sysRole); return sysRole; } @Override public void add(SysRole role) { log.debug("新增角色:{}", role.getName()); roleMapper.insert(role); redisUtil.deleteObject(allKey); } @Override public void update(Long roleId, SysRole role) { log.debug("更新角色:{}", role.getName()); String roleCacheKey = CacheConstant.getRoleCacheKey(String.valueOf(roleId)); role.setId(roleId); roleMapper.updateById(role); // 更新缓存 redisUtil.setCacheObject(roleCacheKey,role); // 清除缓存 redisUtil.deleteObject(allKey); } @Override public void delete(Long roleId) { log.debug("删除角色:{}", roleId); roleMapper.deleteById(roleId); // 清除缓存 redisUtil.deleteObject(CacheConstant.getRoleCacheKey(String.valueOf(roleId))); redisUtil.deleteObject(allKey); }}2.7 启动测试
这里只测试了user模块(都测试并且贴图会显得篇幅太长且繁琐),role模块本人测试后结果正确。
查询列表:
调用接口返回全部数据并缓存完成,再次调用无查询日志输出,符合预期。
接口调用:
查看缓存:
查看用户详情:
接口调用返回用户详情信息并缓存完成,再次调用无查询日志输出,符合预期。
接口调用:
查看缓存:
更新数据:
接口调用返回更新成功,并且查看全部的缓存被清除。符合预期。
接口调用:
查看缓存:
3. 内置缓存注解3.1 @CacheConfig
@Cacheable()里面都有一个value=“xxx”的属性,这显然如果方法多了,写起来也是挺累的,如果可以一次性声明完 那就省事了, 所以,有了@CacheConfig这个配置, @CacheConfig is a class-level annotation that allows to share the cache names ,如果你在你的方法写别的名字,那么依然以方法的名字为准。
3.2 @Cacheable
@Cacheable(value="myCache"),这个注释的意思是,当调用这个方法的时候,会从一个名叫myCache 的缓存中查询,如果没有,则执行实际的方法(即查询数据库),并将执行的结果存入缓存中,否则返回缓存中的对象。
3.3 @CachePut
@CachePut 的作用 主要针对方法配置,能够根据方法的请求参数对其 结果 进行缓存,和 @Cacheable 不同的是,它每次都会触发真实方法的调用。
3.4 @CacheEvict
@CachEvict 的作用 主要针对方法配置,能够根据一定的条件对缓存进行清空。
// 清空当前cache name下的所有key@CachEvict(allEntries = true)3.5 @Caching
@Caching可以使注解组合使用,比如根据id查询用户信息,查询完的结果为{key = id,value = userInfo},但我们现在为了方遍,想用用户的手机号,邮箱等缓存对应用户的信息,这时候我们就要使用@Caching。例:
@Caching(put = {@CachePut(value = "user", key = "#user.id"),@CachePut(value = "user", key = "#user.username"),@CachePut(value = "user", key = "#user.email")})public User getUserInfo(User user){ ...return user;}
标签: #redis的使用和配置