Redis最全详解(三)——SpringBoot整合2种方式
SpringBoot整合Redis
常用 api 介绍
有两个 redis 模板:RedisTemplate 和 StringRedisTemplate。我们不使用 RedisTemplate,RedisTemplate 提供给我们操作对象,操作对象的时候,我们通常是以 json 格式存储,但在存储的时候,会使用 Redis 默认的内部序列化器;导致我们存进里面的是乱码之类的东西。当然了,我们可以自己定义序列化,但是比较麻烦,所以使用 StringRedisTemplate 模板。StringRedisTemplate 主要给我们提供字符串操作,我们可以将实体类等转成 json 字符串即可,在取出来后,也可以转成相应的对象,这需要导入阿里 fastjson依赖来操作 。
StringRedisTemplate
没有序列化
依赖
pom.xml
<fastjson.version>1.2.51fastjson.version>
<dependency><groupId>org.springframework.bootgroupId><artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency><groupId>com.alibabagroupId><artifactId>fastjsonartifactId><version>${fastjson.version}version>
dependency>
导入阿里巴巴的 fastjson 是为了在后面我们要存一个实体,为了方便把实体转换成 json 字符串存进去。
配置
yml
spring:#redis配置redis:host: 47.113.80.250port: 6379password: 123456timeout: 30000jedis:pool:max-active: 8max-wait: -1max-idle: 500min-idle: 0lettuce:shutdown-timeout: 0spring:#redis相关配置redis:# 指定库,默认0库,全部16个库database: 5# 配置redis的主机地址,需要修改成自己的host: 47.113.80.250port: 6379password: 123456timeout: 5000jedis:pool:# 连接池中的最大空闲连接,默认值也是8。max-idle: 500# 连接池中的最小空闲连接,默认值也是0。min-idle: 50# 如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)max-active: 1000# 等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionExceptionmax-wait: 2000
redis:string 类型
新建一个 RedisService,注入 StringRedisTemplate,使用 stringRedisTemplate.opsForValue() 可以获取 ValueOperations 对象,通过该对象即可读写 redis 数据库了。如下:
public class RedisService {@Resourceprivate StringRedisTemplate stringRedisTemplate;/*** set redis: string类型* @param key key* @param value value*/public void setString(String key, String value){ValueOperations<String, String> valueOperations = stringRedisTemplate.opsForValue();valueOperations.set(key, value);}/*** get redis: string类型* @param key key* @return*/public String getString(String key){return stringRedisTemplate.opsForValue().get(key);}
}
该对象操作的是 string,我们也可以存实体类,只需要将实体类转换成 json 字符串即可。下面来测试一下:
@RunWith(SpringRunner.class)
@SpringBootTest
public class Course14ApplicationTests {private static final Logger logger = LoggerFactory.getLogger(Course14ApplicationTests.class);@Resourceprivate RedisService redisService;@Testpublic void contextLoads() {//测试redis的string类型redisService.setString("weichat","程序员私房菜");logger.info("我的微信公众号为:{}", redisService.getString("weichat"));// 如果是个实体,我们可以使用json工具转成json字符串,User user = new User("CSDN", "123456");redisService.setString("userInfo", JSON.toJSONString(user));logger.info("用户信息:{}", redisService.getString("userInfo"));}
}
先启动 redis,然后运行这个测试用例,观察控制台打印的日志如下:
我的微信公众号为:程序员私房菜
用户信息:{"password":"123456","username":"CSDN"}
redis:hash 类型
hash 类型其实原理和 string 一样的,但是有两个 key,使用 stringRedisTemplate.opsForHash() 可以获取 HashOperations 对象。比如我们要存储订单信息,所有订单信息都放在 order 下,针对不同用户的订单实体,可以通过用户的 id 来区分,这就相当于两个 key 了。
@Service
public class RedisService {@Resourceprivate StringRedisTemplate stringRedisTemplate;/*** set redis: hash类型* @param key key* @param filedKey filedkey* @param value value*/public void setHash(String key, String filedKey, String value){HashOperations<String, Object, Object> hashOperations = stringRedisTemplate.opsForHash();hashOperations.put(key,filedKey, value);}/*** get redis: hash类型* @param key key* @param filedkey filedkey* @return*/public String getHash(String key, String filedkey){return (String) stringRedisTemplate.opsForHash().get(key, filedkey);}
}
可以看出,hash 和 string 没啥两样,只不过多了个参数,Spring Boot 中操作 redis 非常简单方便。来测试一下:
@SpringBootTest
public class Course14ApplicationTests {private static final Logger logger = LoggerFactory.getLogger(Course14ApplicationTests.class);@Resourceprivate RedisService redisService;@Testpublic void contextLoads() {//测试redis的hash类型redisService.setHash("user", "name", JSON.toJSONString(user));logger.info("用户姓名:{}", redisService.getHash("user","name"));}
}
redis:list 类型
使用 stringRedisTemplate.opsForList() 可以获取 ListOperations redis 列表对象,该列表是个简单的字符串列表,可以支持从左侧添加,也可以支持从右侧添加,一个列表最多包含 2 ^ 32 -1 个元素。
@Service
public class RedisService {@Resourceprivate StringRedisTemplate stringRedisTemplate;/*** set redis:list类型* @param key key* @param value value* @return*/public long setList(String key, String value){ListOperations<String, String> listOperations = stringRedisTemplate.opsForList();return listOperations.leftPush(key, value);}/*** get redis:list类型* @param key key* @param start start* @param end end* @return*/public List<String> getList(String key, long start, long end){return stringRedisTemplate.opsForList().range(key, start, end);}
}
可以看出,这些 api 都是一样的形式,方便记忆也方便使用。具体的 api 细节我就不展开了,大家可以自己看 api 文档。其实,这些 api 根据参数和返回值也能知道它们是做什么用的。来测试一下:
@RunWith(SpringRunner.class)
@SpringBootTest
public class Course14ApplicationTests {private static final Logger logger = LoggerFactory.getLogger(Course14ApplicationTests.class);@Resourceprivate RedisService redisService;@Testpublic void contextLoads() {//测试redis的list类型redisService.setList("list", "football");redisService.setList("list", "basketball");List<String> valList = redisService.getList("list",0,-1);for(String value :valList){logger.info("list中有:{}", value);}}
}
使用:
//StringRedisTemplate
@Resource
private StringRedisTemplate stringRedisTemplate;//缓存3分钟
if (StringUtils.isEmpty(stringRedisTemplate.boundValueOps(s).get())){stringRedisTemplate.opsForValue().set(key, value, 3*60, TimeUnit.SECONDS);//秒单位
}
工具类
public class RedisService {@Resourceprivate StringRedisTemplate stringRedisTemplate;/*** set redis: string类型* @param key key* @param value value*/public void setString(String key, String value){ValueOperations<String, String> valueOperations = stringRedisTemplate.opsForValue();valueOperations.set(key, value);}/*** get redis: string类型* @param key key* @return*/public String getString(String key){return stringRedisTemplate.opsForValue().get(key);}/*** set redis: hash类型* @param key key* @param filedKey filedkey* @param value value*/public void setHash(String key, String filedKey, String value){HashOperations<String, Object, Object> hashOperations = stringRedisTemplate.opsForHash();hashOperations.put(key,filedKey, value);}/*** get redis: hash类型* @param key key* @param filedkey filedkey* @return*/public String getHash(String key, String filedkey){return (String) stringRedisTemplate.opsForHash().get(key, filedkey);}/*** set redis:list类型* @param key key* @param value value* @return*/public long setList(String key, String value){ListOperations<String, String> listOperations = stringRedisTemplate.opsForList();return listOperations.leftPush(key, value);}/*** get redis:list类型* @param key key* @param start start* @param end end* @return*/public List<String> getList(String key, long start, long end){return stringRedisTemplate.opsForList().range(key, start, end);}}
总结
本节主要介绍了 redis 的使用场景、安装过程,以及 Spring Boot 中集成 redis 的详细步骤。在实际项目中,通常都用 redis 作为缓存,在查询数据库的时候,会先从 redis 中查找,如果有信息,则从 redis 中取;如果没有,则从数据库中查,并且同步到 redis 中,下次 redis 中就有了。更新和删除也是如此,都需要同步到 redis。redis 在高并发场景下运用的很多。
RestTemplate
SpringBoot开发基本就是使用这个。SpringData是对数据库访问的技术进一步封装。默认lettuce连接池。
依赖
pom.xml
<dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency> <groupId>org.apache.commonsgroupId> <artifactId>commons-pool2artifactId>
dependency>
配置
yml
spring:redis:port: 6379password: 123456host: 192.168.169.135database: 1 #指定库lettuce:pool:max-active: 8 # 连接池最大连接数(使用负值表示没有限制)max-idle: 8 # 连接池中的最大空闲连接min-idle: 0 # 连接池中的最小空闲连接max-wait: 1000 # 连接池最大阻塞等待时间(使用负值表示没有限制)shutdown-timeout: 100 # 关闭超时时间spring:#redis相关配置redis:# 指定库,默认0库,全部16个库database: 5# 配置redis的主机地址,需要修改成自己的host: 47.113.80.250port: 6379password: 123456timeout: 5000jedis:pool:# 连接池中的最大空闲连接,默认值也是8。max-idle: 500# 连接池中的最小空闲连接,默认值也是0。min-idle: 50# 如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)max-active: 1000# 等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionExceptionmax-wait: 2000
RedisConfig类
编写缓存配置类RedisConfifig用于调优缓存默认配置,RedisTemplate
大家可以看到在redisTemplate()这个方法中用JacksonJsonRedisSerializer更换掉了Redis默认的序列化方
式:JdkSerializationRedisSerializer
spring-data-redis中序列化类有以下几个:
GenericToStringSerializer:可以将任何对象泛化为字符创并序列化 Jackson2JsonRedisSerializer:序列化
Object对象为json字符创(与JacksonJsonRedisSerializer相同) JdkSerializationRedisSerializer:序列化java
对象 StringRedisSerializer:简单的字符串序列化
JdkSerializationRedisSerializer序列化被序列化对象必须实现Serializable接口,被序列化除属性内容还有其他
内容,长度长且不易阅读,默认就是采用这种序列化方式
存储内容如下:
"\xac\xed\x00\x05sr\x00!com.oreilly.springdata.redis.User\xb1\x1c
\n\xcd\xed%\xd8\x02\x00\x02I\x00\x03ageL\x00\buserNamet\x00\x12Ljava/lang/String;xp\x00\x00\x00\
x14t\x00\x05user1"
JacksonJsonRedisSerializer序列化,被序列化对象不需要实现Serializable接口,被序列化的结果清晰,容易阅读,而且存储字节少,速度快
存储内容如下:
“{“userName”:“guoweixin”,“age”:20}”
StringRedisSerializer序列化
一般如果key、value都是string字符串的话,就是用这个就可以了 ,在如下配置。
package com.config;import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.KeyGenerator;
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.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;import javax.annotation.Resource;
import java.lang.reflect.Method;/*** @ClassName RedisConfig* @Description TODO* @Author wzq* @Version 1.0*/
@Configuration
public class RedisConfig extends CachingConfigurerSupport {/*** 自定义缓存key的生成策略。默认的生成策略是看不懂的(乱码内容)* 通过Spring的依赖注入特性进行自定义的* 配置注入并且此类是一个配置类可以更多程度的自定义配置* @return*/@Bean@Overridepublic KeyGenerator keyGenerator() {return new KeyGenerator() {@Overridepublic Object generate(Object target, Method method, Object... params) {StringBuilder sb = new StringBuilder();sb.append(target.getClass().getName());sb.append(method.getName());for (Object obj : params) {sb.append(obj.toString());}return sb.toString();}};}@Resourceprivate LettuceConnectionFactory factory;/*** 缓存配置管理器 */@Beanpublic CacheManager cacheManager() {//以锁写入的方式创建RedisCacheWriter对象RedisCacheWriter writer = RedisCacheWriter.lockingRedisCacheWriter(factory);//创建默认缓存配置对象RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();return new RedisCacheManager(writer, config);}@Beanpublic RedisTemplate<String,Object> redisTemplate(){RedisTemplate<String,Object> template = new RedisTemplate <>();template.setConnectionFactory(factory);Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();// 在使用注解@Bean返回RedisTemplate的时候,同时配置hashKey与hashValue的序列化方式。// key采用String的序列化方式template.setKeySerializer(stringRedisSerializer);// value序列化方式采用jacksontemplate.setValueSerializer(jackson2JsonRedisSerializer);// hash的key也采用String的序列化方式template.setHashKeySerializer(stringRedisSerializer);// hash的value序列化方式采用jacksontemplate.setHashValueSerializer(jackson2JsonRedisSerializer);template.afterPropertiesSet();return template;}}
用法
用作缓存都是先查redis,没有在查mysql,从mysql查的要存入redis。
string
opsForValue()
redisTemplate.hasKey(key);(String) redisTemplate.opsForValue().get(key);redisTemplate.opsForValue().set(key, val);//设置过期时间秒,可以设置天、分、时
redisTemplate.opsForValue().set(key, value, expireTime, TimeUnit.SECONDS);
@Service
@Log
public class RedisServiceImpl {@Resourceprivate RedisTemplate<String,Object> redisTemplate;@Resource(name="redisTemplate") //引用redisTemplate资源private ValueOperations<String,String> string;//下面的redisTemplate.opsForValue()可以直接string代替/*** 普通缓存放入* @param key* @return*/public String getString(String key) {if (redisTemplate.hasKey(key)) {log.info("Redis中查询");return (String) redisTemplate.opsForValue().get(key);} else {String val = "guoweixin";redisTemplate.opsForValue().set(key, val);log.info("数据库中查询的");return val;}}/** 普通缓存放入* @param key 键* @param value 值* @param expireTime 超时时间(秒)* @return true成功 false失败*/public Boolean set(String key, Object value, int expireTime) {try {//设置秒,可以设置天、分、时redisTemplate.opsForValue().set(key, value, expireTime, TimeUnit.SECONDS);return true;} catch (Exception e) {e.printStackTrace();return false;}}
}
hash
hash默认格式user:id。使用::::::::
User user=new User();
redisTemplate.opsForHash().put("user",id,user); //添加hash,key是user:id,值是user中各字段redisTemplate.hasKey("user:id") //指是否有"user:id"这个key
redisTemplate.opsForHash().hasKey("user",id) //指user中是否有id这个key(User)redisTemplate.opsForHash().get("user",id); //指获取user中是id这个域的值
@Resourceprivate RedisTemplate<String,Object> redisTemplate; @Resource(name="redisTemplate") private HashOperations<String,String,User> hash;//下面的redisTemplate.opsForHash()可以直接hash代替/***判断key是否存在,如果存在 在Redis中查询* 如果不存在,在MYSQL中查询,并将结果得到,添加到Redis Hash中* @param id* @return* */public User selectUserById1(String id){if(redisTemplate.opsForHash().hasKey("user",id)){log.info("Redis中查询对象");return (User) redisTemplate.opsForHash().get("user",id);}else{User user=new User();user.setId(id);user.setName("guoweixin");user.setAge(22);log.info("mysql中查询对象");redisTemplate.opsForHash().put("user",id,user);return user;}}
工具类:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;@Service("hashCacheService")
public class HashCacheServiceImpl{private final static Logger log = LoggerFactory.getLogger(HashCacheServiceImpl.class);@Resourceprivate RedisTemplate<String, Object> redisTemplate;/*** 获取MAP中的某个值*@param key 键*@param item 项*@return 值*/public Object hget(String key, String item) {return redisTemplate.opsForHash().get(key, item);}/*** 获取hashKey对应的所有键值* @param key 键* @return 对应的多个键值*/public Map<Object, Object> hmget(String key) {return redisTemplate.opsForHash().entries(key);}/*** 以map集合的形式添加键值对*@param key 键*@param map 对应多个键值*@return true 成功 false 失败*/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 并设置时间*@param key 键*@param map 对应多个键值*@param time 时间(秒)*@return true成功 false失败*/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表中放入数据,如果不存在将创建*@param key 键*@param item 项*@param value 值*@return true 成功 false失败*/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表中放入数据,如果不存在将创建*@param key 键*@param item 项*@param value 值* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间*@return true 成功 false失败*/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表中的值*@param key 键 不能为null*@param item 项 可以使多个 不能为null*/public void hdel(String key, Object... item) {redisTemplate.opsForHash().delete(key, item);}/*** 判断hash表中是否有该项的值*@param key 键 不能为null*@param item 项 不能为null*@return true 存在 false不存在*/public boolean hHasKey(String key, String item) {return redisTemplate.opsForHash().hasKey(key, item);}/*** hash递增 如果不存在,就会创建一个 并把新增后的值返回*@param key 键*@param item 项* @param by 要增加几(大于0)*@return*/public long hincr(String key, String item, long by) {return redisTemplate.opsForHash().increment(key, item, by);}/***hash递减*@param key 键*@param item 项* @param by 要减少记(小于0)*@return*/public long hdecr(String key, String item, long by) {return redisTemplate.opsForHash().increment(key, item, -by);}/*** 获取指定变量中的hashMap值。*@param key*@return 返回LIST对象*/public List<Object> values(String key) {return redisTemplate.opsForHash().values(key);}/*** 获取变量中的键。*@param key*@return 返回SET集合*/public Set<Object> keys(String key) {return redisTemplate.opsForHash().keys(key);}/*** 获取变量的长度。*@param key 键*@return 返回长度*/public long size(String key) {return redisTemplate.opsForHash().size(key);}/*** 以集合的方式获取变量中的值。*@param key*@param list*@return 返回LIST集合值*/public List multiGet(String key, List list) {return redisTemplate.opsForHash().multiGet(key,list);}/*** 如果变量值存在,在变量中可以添加不存在的的键值对* 如果变量不存在,则新增一个变量,同时将键值对添加到该变量。*@param key*@param hashKey*@param value*/public void putIfAbsent(String key, String hashKey, Object value) {redisTemplate.opsForHash().putIfAbsent(key,hashKey,value);}/*** 匹配获取键值对,ScanOptions.NONE为获取全部键对,ScanOptions.scanOptions().match("map1").build()* 匹配获取键位map1的键值对,不能模糊匹配。*@param key*@param options*@return*/public Cursor<Map.Entry<Object, Object>> scan(String key, ScanOptions options) {return redisTemplate.opsForHash().scan(key,options);}/*** 删除变量中的键值对,可以传入多个参数,删除多个键值对。*@param key 键*@param hashKeys MAP中的KEY*/public void delete(String key, String... hashKeys) {redisTemplate.opsForHash().delete(key,hashKeys);}public void expire(String key, long seconds) {redisTemplate.expire(key, seconds, TimeUnit.SECONDS);}/*** 删除* @param keys*/public void del(String... keys) {if (keys != null && keys.length > 0) { if (keys.length == 1) {redisTemplate.delete(keys[0]);} else {redisTemplate.delete(CollectionUtils.arrayToList(keys));}}}public Long getExpire(String key) {return redisTemplate.getExpire(key);}
}
list
opsForList()
工具类:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.TimeUnit;/***@ClassName ListCacheServiceImpl*@Description TODO*@Author wzq*@Version 1.0*/
@Service("listCacheService")
public class ListCacheServiceImpl{private final static Logger log = LoggerFactory.getLogger(ListCacheServiceImpl.class);@Resourceprivate RedisTemplate<String, Object> redisTemplate;/*** 将list放入缓存** @param key 键* @param value 值* @return true 成功 false 失败*/public boolean lpushAll(String key, List<Object> value) {try {redisTemplate.opsForList().leftPushAll(key, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 将list放入缓存** @param key 键* @param value 值* @param time 时间(秒)* @return true 成功 false 失败*/public boolean lpushAll(String key, List<Object> value, long time) {try {redisTemplate.opsForList().leftPushAll(key, value);if (time > 0) {expire(key, time);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 将list放入缓存** @param key 键* @param value 值* @return true 成功 false 失败*/public boolean rpushAll(String key, List<Object> value) {try {redisTemplate.opsForList().rightPushAll(key, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 将list放入缓存** @param key 键* @param value 值* @param time 时间(秒)* @return true 成功 false 失败*/public boolean rpushAll(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;}}/*** 在变量左边添加元素值。** @param key 键* @param object 值* @return true 成功 false 失败*/public boolean lpush(String key, Object object) {try {redisTemplate.opsForList().leftPush(key, object);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 把最后一个参数值放到指定集合的第一个出现中间参数的前面,如果中间参数值存在的话。** @param key 键* @param pivot 中间参数* @param object 要放的值* @return 成功 true 失败 false*/public Boolean lpush(String key, Object pivot, Object object) {try {redisTemplate.opsForList().leftPush(key, pivot, object);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 集合中第一次出现第二个参数变量元素的右边添加第三个参数变量的元素值。** @param key 键* @param pivot 中间参数* @param object 要放的值* @return 成功 true 失败 false*/public Boolean rpush(String key, Object pivot, Object object) {try {redisTemplate.opsForList().rightPush(key, pivot, object);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 向集合最右边添加元素。** @param key 键* @param object 值* @return 成功 true 失败 false*/public Boolean rpush(String key, Object object) {try {redisTemplate.opsForList().rightPush(key, object);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 在变量左边添加元素值。** @param key 键* @param expireTime 超时时间* @param objects 值* @return 成功 true 失败 false*/public Boolean lpush(String key, int expireTime, Object... objects) {try {redisTemplate.opsForList().leftPush(key, objects);if (expireTime > 0) {expire(key, expireTime);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 在变量右边添加元素值。** @param key 键* @param expireTime 超时时间* @param objects 值* @return 成功 true 失败 false*/public Boolean rpush(String key, int expireTime, Object... objects) {try {redisTemplate.opsForList().rightPush(key, objects);if (expireTime > 0) {expire(key, expireTime);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 如果存在集合则向左边添加元素,不存在不加** @param key 键* @param object 值* @return 成功 true 失败 false*/public boolean lPushIfPresent(String key, Object object) {try {redisTemplate.opsForList().leftPushIfPresent(key, object);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 如果存在集合则向右边添加元素,不存在不加** @param key 键* @param object 返回* @return 成功 true 失败 false*/public boolean rPushIfPresent(String key, Object object) {try {redisTemplate.opsForList().rightPushIfPresent(key, object);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 移除集合中的左边第一个元素** @param key 键* @return 返回右边的第一个元素*/public Object lpop(String key) {return redisTemplate.opsForList().leftPop(key);}/*** 移除集合中右边的元素。一般用在队列取值** @param key 键* @return 返回右边的元素*/public Object rpop(String key) {return redisTemplate.opsForList().rightPop(key);}/*** 移除集合中左边的元素在等待的时间里,如果超过等待的时间仍没有元素则退出。一般用在队列取值** @param key 键* @param time 时间* @return 左边的元素*/public Object lpop(String key, long time) {return redisTemplate.opsForList().leftPop(key, time, TimeUnit.MILLISECONDS);}/*** 移除集合中右边的元素在等待的时间里,如果超过等待的时间仍没有元素则退出。一般用在队列取值** @param key 键* @param time 时间* @return 返回右边元素*/public Object rpop(String key, long time) {return redisTemplate.opsForList().rightPop(key, time, TimeUnit.MILLISECONDS);}/*** 获取指定区间的值。** @param key 键* @param start 开始位置* @param end 结束位置,为-1指结尾的位置, start 0,end -1取所有* @return*/public List<Object> lrange(String key, long start, long end) {return redisTemplate.opsForList().range(key, start, end);}/*** 获取集合长度** @param key 键* @return 返回长度*/public Long llen(String key) {return redisTemplate.opsForList().size(key);}/*** 在集合的指定位置插入元素,如果指定位置已有元素,则覆盖,没有则新增,超过集合下标+n则会报错。** @param key 键* @param index 位置* @param value 值*/public void set(String key, Long index, Object value) {redisTemplate.opsForList().set(key, index, value);}/*** 获取集合指定位置的值** @param key 键* @param index 位置* @return 返回值*/public Object lindex(String key, Long index) {return redisTemplate.opsForList().index(key, index);}/*** 从存储在键中的列表中删除等于值的元素的第一个计数事件。count> 0:* 删除等于从左到右移动的值的第一个元素;count< 0:删除等于从右到左移动的值的第一个元素;count = 0:删除等于value的所有元素。** @param key 键* @param count* @param object* @return*/public Long remove(String key, long count, Object object) {return redisTemplate.opsForList().remove(key, count, object);}/*** // 截取集合元素长度,保留长度内的数据。** @param key 键* @param start 开始位置* @param end 结束位置*/public void trim(String key, long start, long end) {redisTemplate.opsForList().trim(key, start, end);}/*** 除集合中右边的元素,同时在左边加入一个元素。** @param key 键* @param str 加入的元素* @return 返回右边的元素*/public Object rightPopAndLeftPush(String key, String str) {return redisTemplate.opsForList().rightPopAndLeftPush(key, str);}/*** 移除集合中右边的元素在等待的时间里,同时在左边添加元素,如果超过等待的时间仍没有元素则退出。** @param key 键* @param str 左边增中的值* @param timeout 超时时间* @return 返回移除右边的元素*/public Object rightPopAndLeftPush(String key, String str, long timeout) {return redisTemplate.opsForList().rightPopAndLeftPush(key, str, timeout, TimeUnit.MILLISECONDS);}/*** 删除* @param keys*/public void del(String... keys) {if (keys != null && keys.length > 0) {if (keys.length == 1) {redisTemplate.delete(keys[0]);} else {redisTemplate.delete(CollectionUtils.arrayToList(keys));}}}/*** 设置过期时间* @param key 键* @param seconds 超时时间*/public void expire(String key, long seconds) {redisTemplate.expire(key, seconds, TimeUnit.SECONDS);}
}
项目常应用于:1、对数据量大的集合数据删减 2、任务队列
1、对数据量大的集合数据删减 列表数据显示、关注列表、粉丝列表、留言评价等…分页、热点新闻(Top5)等 利用
LRANGE还可以很方便的实现分页的功能,在博客系统中,每片博文的评论也可以存入一个单独的list中。
2、任务队列 (list通常用来实现一个消息队列,而且可以确保先后顺序,不必像MySQL那样还需要通过ORDER BY来 进行排序)
案例1:
比如:获取最新5条首页新闻,获取最新的评论列表,获取最后登录10个用户,获取最近7天的活跃用户数等或做为队列来使用。
需求: 获取最新5条首页新闻。
案例2:任务队列
1:用户系统登录注册短信实名认证等
2:订单系统的下单流程等
set
opsForSet()
工具类:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;import javax.annotation.Resource;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;/***@ClassName SetcacheServiceImpl*@Description TODO*@Author wzq*@Version 1.0*/
@Service("setCacheService")
public class SetCacheServiceImpl{private final static Logger log = LoggerFactory.getLogger(SetCacheServiceImpl.class);@Resourceprivate RedisTemplate<String, Object> redisTemplate;/***向变量中批量添加值。*@param key 键*@param objects 值*@return true成功 false失败*/public boolean add(String key, Object...objects){try {redisTemplate.opsForSet().add(key,objects);return true;} catch (Exception e) {e.printStackTrace();return false;}}/***向变量中批量添加值。*@param key 键*@param expireTime 值*@param values 值*@return true成功 false失败*/public Boolean add(String key, long expireTime, Object... values) {try {redisTemplate.opsForSet().add(key,values);if (expireTime > 0)expire(key, expireTime);return true;} catch (Exception e) {e.printStackTrace();return false;}}/***members(K key)获取变量中的值。*@param key 键*@return 返回Set对象*/public Set<Object> members(String key) {return redisTemplate.opsForSet().members(key);}/*** 获取变量中值的长度。*@param key 键*@return 返回SET的长度*/public Long size(String key) {return redisTemplate.opsForSet().size(key);}/*** 检查给定的元素是否在变量中。*@param key 键* @param o 要检查的变量*@return true存在 false不存在*/public Boolean isMember(String key, Object o) {return redisTemplate.opsForSet().isMember(key,o);}/*** 转移变量的元素值到目的变量。*@param key 键*@param value 要转移的元素*@param destValue 目标键*@return true 成功 false 失败*/public Boolean move(String key, Object value, String destValue) {return redisTemplate.opsForSet().move(key,value,destValue);}/*** 弹出变量中的元素。* @param key 键* @return 返回弹出的元素*/public Object pop(String key) {return redisTemplate.opsForSet().pop(key);}/*** 批量移除变量中的元素。* @param key 键* @param values 要移除的元素* @return 返回移除元素个数*/public Long remove(String key, Object... values) {return redisTemplate.opsForSet().remove(key,values);}/*** 匹配获取键值对*@param key 键*@param options 选项*@return 返回键值对*/public Cursor<Object> scan(String key, ScanOptions options) {return redisTemplate.opsForSet().scan(key,options);}/*** 通过集合求差值。*@return 返回差差值* @param key 键* @param list LIST中的对象是要比较缓存的KEY*/public Set difference(String key, List list) {return redisTemplate.opsForSet().difference(key,list);}public Set<Object> difference(String key, String otherKeys) {return redisTemplate.opsForSet().difference(key,otherKeys);}/*** 将求出来的差值元素保存。*@param key 键*@param otherKey 要比较的缓存键*@param destKey 要保存差值的缓存键*/public void differenceAndStore(String key, String otherKey, String destKey) {redisTemplate.opsForSet().differenceAndStore(key,otherKey,destKey);}/*** 将求出来的差值元素保存。*@param key 键*@param otherKeys 要比较的多个缓存键*@param destKey 要保存差值的缓存键*/public void differenceAndStore(String key, List otherKeys, String destKey) {redisTemplate.opsForSet().differenceAndStore(key,otherKeys,destKey);}/*** 获取去重的随机元素。*@param key 键*@param count 数量* @return 返回随机元素*/public Set<Object> distinctRandomMembers(String key, long count) {return redisTemplate.opsForSet().distinctRandomMembers(key,count);}/*** 获取2个变量中的交集。*@param key 键*@param otherKey 比较的缓存键*@return 返回交集*/public Set<Object> intersect(String key, String otherKey) {return redisTemplate.opsForSet().intersect(key,otherKey);}public Set intersect(String key, List list) {return redisTemplate.opsForSet().intersect(key,list);}/*** 获取2个变量交集后保存到最后一个参数上*@param key 键*@param otherKey 其它的缓存键*@param destKey 交集键*/public void intersectAndStore(String key, String otherKey, String destKey) {redisTemplate.opsForSet().intersectAndStore(key, otherKey, destKey);}/*** 获取2个变量交集后保存到最后一个参数上*@param key 键*@param otherKey 其它的缓存键列表*@param destKey 交集键*/public void intersectAndStore(String key, List otherKey, String destKey) {redisTemplate.opsForSet().intersectAndStore(key, otherKey, destKey);}/*** 获取2个变量的合集。*@param key 键*@param otherKey 要合的键*@return 返回合并后的SET*/public Set<Object> union(String key, String otherKey) {return redisTemplate.opsForSet().union(key,otherKey);}public Set union(String key, Set set) {return redisTemplate.opsForSet().union(key,set);}/*** 获取2个变量合集后保存到最后一个参数上。*@param key 键*@param otherKey 要合的键*@param destKey 合并后的键*/public void unionAndStore(String key, String otherKey, String destKey) {redisTemplate.opsForSet().unionAndStore(key, otherKey, destKey);}/***获取2个变量合集后保存到最后一个参数上*@param key 键*@param list 要合的键列表*@param destKey 合并后的键*/public void unionAndStore(String key, List list, String destKey) {redisTemplate.opsForSet().unionAndStore(key, list, destKey);}/*** 随机获取变量中的元素。* @param key 键* @return 返回其中一个随机元素*/public Object randomMember(String key){return redisTemplate.opsForSet().randomMember(key);}/*** 随机获取变量中指定个数的元素* @param key 键* @param count 取随机数的个数* @return 返回随机数LIST*/public List<Object> randomMembers(String key, Long count){return redisTemplate.opsForSet().randomMembers(key,count);}public void del(String... keys) {if (keys != null && keys.length > 0) {if (keys.length == 1) {redisTemplate.delete(keys[0]);} else {redisTemplate.delete(CollectionUtils.arrayToList(keys));}}}/*** 设置过期时间* @param key 键* @param seconds 超时时间*/public void expire(String key, Long seconds) {redisTemplate.expire(key, seconds, TimeUnit.SECONDS);}}
应用场景:
常应用于:对两个集合间的数据[计算]进行交集、并集、差集运算
1、利用集合操作,可以取不同兴趣圈子的交集,以非常方便的实现如共同关注、共同喜好、二度好友等功能。对上面 的所有集合操作,你还可以使用不同的命令选择将结果返回给客户端还是存储到一个新的集合中。
2、利用唯一性,可以统计访问网站的所有独立 IP、存取当天[或某天]的活跃用户列表。
案例1
判断用户名是否存在
案例2
抽奖活动。
现有员工10个。1等奖1名。2等奖2名。3等奖3名。用Redis实现
案例3
有两组数据,求两组数据的 交集、差集、并集
zset
opsForZSet()
工具类:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.connection.RedisZSetCommands;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;import javax.annotation.Resource;
import java.util.List;
import java.util.Set;/***@ClassName ZSetCacheServiceImpl*@Description TODO*@Author wzq*@Version 1.0*/
@Service("zsetCacheService")
public class ZSetCacheServiceImpl{private final static Logger log = LoggerFactory.getLogger(ZSetCacheServiceImpl.class);@Resourceprivate RedisTemplate<String, Object> redisTemplate;/*** 增添加元素到变量中同时指定元素的分值。*@param key 键*@param value 值*@param score 分值*@return true 成功 false 失败*/public boolean add(String key, Object value, double score){try {redisTemplate.opsForZSet().add(key,value,score);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 获取变量指定区间的元素。START为0,END为-1代表取全部*@param key 键*@param start 开始位置*@param end 结束位置*@return 返回SET*/public Set<Object> range(String key, long start, long end) {return redisTemplate.opsForZSet().range(key,start,end);}/*** 用于获取满足非score的排序取值。这个排序只有在有相同分数的情况下才能使用,如果有不同的分数则返回值不确定。*@param key 键*@param range*@return 返回SET*/public Set<Object> rangeByLex(String key, RedisZSetCommands.Range range){return redisTemplate.opsForZSet().rangeByLex(key,range);}/*** 获取变量中元素的个数*@param key 键*@return 返回个数*/public Long zCard(String key){return redisTemplate.opsForZSet().zCard(key);}/*** 获取区间值的个数。*@param key 键*@param min 最小SCORE*@param max 最大SCORE*@return 返回数量*/public Long count(String key, double min, double max) {return redisTemplate.opsForZSet().count(key,min,max);}/*** 修改变量中的元素的分值。*@param key*@param value*@param delta*@return*/public Double incrementScore(String key, Object value, double delta) {return redisTemplate.opsForZSet().incrementScore(key,value,delta);}/*** 获取元素的分值*@param key 键*@param o 要查找的值*@return 返回分值*/public Double score(String key, Object o){return redisTemplate.opsForZSet().score(key,o);}/*** 用于获取满足非score的设置下标开始的长度排序取值。*@param key 键*@param range 范围*@param limit 限制区域*@return 返回SET*/public Set<Object> rangeByLex(String key, RedisZSetCommands.Range range, RedisZSetCommands.Limit limit) {return redisTemplate.opsForZSet().rangeByLex(key, range,limit);}/***通过TypedTuple方式新增数据。*@param key 键*@param tuples 元组*/public void add(String key, Set<ZSetOperations.TypedTuple<Object>> tuples) {redisTemplate.opsForZSet().add(key,tuples);}/*** 根据设置的score获取区间值*@param key 键*@param min 最小值*@param max 最大值*@return 返回SET*/public Set<Object> rangeByScore(String key, double min, double max) {return redisTemplate.opsForZSet().rangeByScore(key,min,max);}/*** 根据设置的score获取区间值从给定下标和给定长度获取最终值。*@param key 键*@param min 最小值*@param max 最大值*@param offset 偏移时*@param count 取的长度*@return 返回SET*/public Set<Object> rangeByScore(String key, double min, double max, long offset, long count) {return redisTemplate.opsForZSet().rangeByScore(key,min,max,offset,count);}/***获取RedisZSetCommands.Tuples的区间值。*@param key 键*@param start 开始SCORE值*@param end 结束SCORE值*@return 返回区间值*/public Set<ZSetOperations.TypedTuple<Object>> rangeWithScores(String key, long start, long end) {return redisTemplate.opsForZSet().rangeWithScores(key,start,end);}/***获取RedisZSetCommands.Tuples的区间值通过分值。*@param key 键*@param min 最小分值*@param max 最大分值*@return 返回SET*/public Set<ZSetOperations.TypedTuple<Object>> rangeByScoreWithScores(String key, double min, double max) {return redisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max);}/*** 获取RedisZSetCommands.Tuples的区间值从给定下标和给定长度获取最终值通过分值。*@param key 键*@param min 最小分值*@param max 最大分值*@param offset 偏移量*@param count 总数*@return 返回SET*/public Set<ZSetOperations.TypedTuple<Object>> rangeByScoreWithScores(String key, double min, double max, long offset, long count) {return redisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max,offset,count);}/*** 获取变量中元素的索引,下标开始位置为*@param key 键*@param o 要查找的值*@return 返回下标*/public Long rank(String key, Object o) {return redisTemplate.opsForZSet().rank(key,o);}/*** 匹配获取键值对,ScanOptions.NONE为获取全部键值对;ScanOptions.scanOptions().match("C").build()匹配获取键位map1的键值对,不能模糊匹配。*@param key 键*@param options 选项*@return 返回键值对*/public Cursor<ZSetOperations.TypedTuple<Object>> scan(String key, ScanOptions options) {return redisTemplate.opsForZSet().scan(key, options);}/*** 索引倒序排列指定区间元素。*@param key 键*@param start 开始位置*@param end 结束位置* @return 返回倒排后的结果*/public Set<Object> reverseRange(String key, long start, long end) {return redisTemplate.opsForZSet().reverseRange(key,start,end);}/*** 倒序排列指定分值区间元素。*@param key 键*@param min 最小SCORE*@param max 最大SCORE* @return 返回区间元素*/public Set<Object> reverseRangeByScore(String key, double min, double max) {return redisTemplate.opsForZSet().reverseRangeByScore(key,min,max);}/*** 倒序排列从给定下标和给定长度分值区间元素。*@param key 键*@param min 最小SCORE*@param max 最大SCORE*@param offset 偏移量*@param count 数量*@return 返回列表*/public Set<Object> reverseRangeByScore(String key, double min, double max, long offset, long count) {return redisTemplate.opsForZSet().reverseRangeByScore(key,min,max,offset,count);}/***倒序排序获取RedisZSetCommands.Tuples的分值区间值。*@param key 键*@param min 最小SCORE*@param max 最大SCORE*@return 返回SET集合*/public Set<ZSetOperations.TypedTuple<Object>> reverseRangeByScoreWithScores(String key, double min, double max) {return redisTemplate.opsForZSet().reverseRangeByScoreWithScores(key,min,max);}/***序排序获取RedisZSetCommands.Tuples的从给定下标和给定长度分值区间值*@param key 键*@param min 最小SCORE*@param max 最大SCORE*@param offset 偏移量*@param count 总数*@return 返回SET*/public Set<ZSetOperations.TypedTuple<Object>> reverseRangeByScoreWithScores(String key, double min, double max, long offset, long count) {return redisTemplate.opsForZSet().reverseRangeByScoreWithScores(key,min,max,offset,count);}/*** 索引倒序排列区间值。*@param key 键*@param start 开始Score*@param end 结束SCORE*@return 返回列表*/public Set<ZSetOperations.TypedTuple<Object>> reverseRangeWithScores(String key, long start, long end) {return redisTemplate.opsForZSet().reverseRangeWithScores(key,start,end);}/*** 获取倒序排列的索引值。*@param key 键*@param o 值*@return 返回倒序排列的索引值*/public Long reverseRank(String key, Object o) {return redisTemplate.opsForZSet().reverseRank(key,o);}/*** 获取2个变量的交集存放到第3个变量里面。*@param key 键*@param otherKey 要交集的键*@param destKey 目标键*@return 返回交集长度*/public Long intersectAndStore(String key, String otherKey, String destKey) {return redisTemplate.opsForZSet().intersectAndStore(key,otherKey,destKey);}/*** 获取多个变量的交集存放到第3个变量里面。*@param key 键*@param list 多个要交集的KEY*@param destKey 要存入的KEY*@return 返回数量*/public Long intersectAndStore(String key, List list, String destKey) {return redisTemplate.opsForZSet().intersectAndStore(key,list,destKey);}/*** 获取2个变量的合集存放到第3个变量里面。*@param key 键*@param otherKey 要合并的KEY*@param destKey 共同的并集元素存到destK* @return 返回元素个数*/public Long unionAndStore(String key, String otherKey, String destKey) {return redisTemplate.opsForZSet().unionAndStore(key,otherKey,destKey);}/*** 获取多个变量的合集存放到第3个变量里面。*@param key 键*@param list 要合的集合KEY*@param destKey 目票集合KEY* @return 返回合集长度*/public Long unionAndStore(String key, List list, String destKey) {return redisTemplate.opsForZSet().unionAndStore(key,list,destKey);}/*** 批量移除元素根据元素值。*@param key 键*@param values 要删除的元素*@return 返回删除的数量*/public Long remove(String key, Object... values) {return redisTemplate.opsForZSet().remove(key,values);}/*** 根据分值移除区间元素。*@param key 键*@param min 最小的SCORE*@param max 最大的SCORE* @return 返回移除的元素数量*/public Long removeRangeByScore(String key, double min, double max) {return redisTemplate.opsForZSet().removeRangeByScore(key,min,max);}/*** 根据索引值移除区间元素。*@param key 键*@param start 索引开始*@param end 索引结束*@return 返回移除的数量* */public Long removeRange(String key, long start, long end) {return redisTemplate.opsForZSet().removeRange(key,start,end);}/*** 删除指定的KEY的缓存* @param keys*/public void del(String... keys) {if (keys != null && keys.length > 0) {if (keys.length == 1) {redisTemplate.delete(keys[0]);} else { redisTemplate.delete(CollectionUtils.arrayToList(keys)); }}}}
使用场景:
排行榜 销量排名,积分排名等,也可以按时间戳排序。
1.比如twitter 的public timeline可以以发表时间作为score来存储,这样获取时就是自动按时间排好序的。
2.比如一个存储全班同学成绩的Sorted Set,其集合value可以是同学的学号,而score就可以是其考试得分,这 样在数据插入集合的时候,就已经进行了天然的排序。
3.还可以用Sorted Set来做带权重的队列,比如普通消息的score为1,重要消息的score为2,然后工作线程可以 选择按score的倒序来获取工作任务。让重要的任务优先执行。
使用例子
限制登录功能
需求描述:
用户在2分钟内,仅允许输入错误密码5次。
如果超过次数,限制其登录1小时。(要求每登录失败时,都要给相应提式)

业务Service层:


Controller层:

本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
