1.Redis事务
Redis事务本质是一组命令的集合。执行时,一个事务中所有的命令都会被序列化,在事务执行,会按照顺序执行、一次性、排他性执行所有的命令。
Redis事务没有隔离级别的概念。所有的事务中,并没有直接被执行,只有发起执行命令时才会被执行。执行命令Exec。
Redis执行单条命令是保证原子性的,但是Redis的事务不保证原子性。
Redis执行事务的流程:
①开启事务(multi);
②命令进入Queued队列;
③执行事务(exec)
1.1.执行事务操作
①正常流程执行事务具体操作如下:
127.0.0.1:6379> multi #开启事务 OK
#命令入队列 127.0.0.1:6379> set k1 v1 QUEUED 127.0.0.1:6379> set k2 v2 QUEUED 127.0.0.1:6379> get k2 QUEUED 127.0.0.1:6379> set k3 v3 QUEUED
#执行事务 127.0.0.1:6379> exec 1) OK 2) OK 3) "v2" 4) OK
②在执行事务的过程中,放弃事务执行:
127.0.0.1:6379> multi # 开启事务 OK 127.0.0.1:6379> set k1 v1 QUEUED 127.0.0.1:6379> set k2 v2 QUEUED 127.0.0.1:6379> set k4 v4 QUEUED 127.0.0.1:6379> discard #取消事务,取消事务之后,可以看到,取消之前的队列中的操作所有都没有被执行 OK 127.0.0.1:6379> get k1 (nil) 127.0.0.1:6379> get k2 (nil) 127.0.0.1:6379> get k4 (nil)
③编译型异常(代码有问题,命令有错误),此时执行事务中所有的命令都不会被执行:
127.0.0.1:6379> multi OK 127.0.0.1:6379> set k1 v1 QUEUED 127.0.0.1:6379> set k2 v2 QUEUED 127.0.0.1:6379> set k3 v3 QUEUED 127.0.0.1:6379> getset k3 #没有这个命令,编译环境都没有通过 (error) ERR wrong number of arguments for 'getset' command 127.0.0.1:6379> set k4 v4 QUEUED 127.0.0.1:6379> set k5 v5 QUEUED 127.0.0.1:6379> exec #执行了一条无效错误命令,则不会执行任何命令操作 (error) EXECABORT Transaction discarded because of previous errors. 127.0.0.1:6379> get k1 (nil) 127.0.0.1:6379> get k5 (nil)
④运行时异常(1/0),此时执行事务中只会影响有异常的那个命令而不会影响其他命令执行,错误命令抛出异常:
127.0.0.1:6379> set k1 "v1" OK 127.0.0.1:6379> multi OK 127.0.0.1:6379> incr k1 #执行时会失败,最终不影响其它命令正常执行 QUEUED 127.0.0.1:6379> set k2 v2 QUEUED 127.0.0.1:6379> set k3 v3 QUEUED 127.0.0.1:6379> get k3 QUEUED 127.0.0.1:6379> get k2 QUEUED 127.0.0.1:6379> exec 1) (error) ERR value is not an integer or out of range #虽然第一条命令报错了,但是依旧正常执行成功了,运行时错误 2) OK 3) OK 4) "v3" 5) "v2" 127.0.0.1:6379> get k2 "v2" 127.0.0.1:6379> get k3 "v3"
1.2.Redis事务监控Watch
这里先讲述一个悲观锁与乐观锁的概念:
- 悲观锁:很悲观,认为什么情况下都会出错,任何时候都必须加锁。
- 乐观锁:很乐观,认为任何情况下都不会出错,所以不会上锁。Mysql中使用version标识乐观锁,更新数据时去判断一下在此期间有没有人修改过,如果修改过,则不会去更新数据(更新时会去比较version的值)。
Redis测试监视:
正常场景情况:
127.0.0.1:6379> set money 100 OK 127.0.0.1:6379> set out 0 OK 127.0.0.1:6379> watch money #监视money对象 OK 127.0.0.1:6379> multi # 事务正常结束,数据在此期间没有发生变动,就正常执行成功 OK 127.0.0.1:6379> DECRBY money 20 QUEUED 127.0.0.1:6379> INCRBY out 20 QUEUED 127.0.0.1:6379> exec #事务一旦执行成功之后,监控就会取消 1) (integer) 80 2) (integer) 20
测试多线程修改值后,修改失败,使用watch,相当于乐观锁:
127.0.0.1:6379> watch money #监视money OK 127.0.0.1:6379> multi #线程1操作money OK 127.0.0.1:6379> DECRBY money 10 QUEUED 127.0.0.1:6379> INCRBY out 10 QUEUED 127.0.0.1:6379> exec (nil)
127.0.0.1:6379> get money #线程2修改money
"80"
127.0.0.1:6379> set money 1000
OK
执行乐观锁失败后,需要进行解锁操作,unwatch,再重新进行监视,执行事务操作:
127.0.0.1:6379> unwatch OK 127.0.0.1:6379> watch money OK 127.0.0.1:6379> multi OK 127.0.0.1:6379> DECRBY money 10 QUEUED 127.0.0.1:6379> incrby money 10 QUEUED 127.0.0.1:6379> exec 1) (integer) 990 2) (integer) 1000 127.0.0.1:6379> watch money OK 127.0.0.1:6379> multi OK 127.0.0.1:6379> DECRBY money 10 QUEUED 127.0.0.1:6379> INCRBY out 10 QUEUED 127.0.0.1:6379> exec 1) (integer) 990 2) (integer) 30
2.Jedis操作Redis
Jedis是Redis官方推荐的java连接开发工具,是java操作redis的中间件。
2.1.Jedis连接操作redis
①创建java maven项目,导入jedis依赖包:
<!--导入jedis的包--> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.3.0</version> </dependency> <!--fastjson--> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.62</version> </dependency>
②连接测试,断开连接操作:
public class JedisDemo { public static void main(String[] args) { //1.创建Jedis对象 Jedis jedis = new Jedis("127.0.0.1", 6379); //验证密码,如果没有设置密码则无需该操作 //jedis.auth(""); jedis.connect(); //2.执行命令操作 System.out.println(jedis.ping()); //输出PONG表示成功 //3.执行redis相关命令操作 jedis.flushAll(); //清空所有的key操作 //4.关闭断开连接 jedis.disconnect(); } }
2.2.Jedis常用API操作
这里为了简化操作使用了本机地址进行连接测试,如果需要连接远端云服务redis地址,需要进行云服务器相关的端口设置,具体设置如下见博客:
https://blog.csdn.net/weixin_43878332/article/details/108741367
①键值对操作
public class JedisKeyDemo { public static void main(String[] args) { Jedis jedis = new Jedis("127.0.0.1", 6379); System.out.println("清空数据:" + jedis.flushDB()); System.out.println("判断某个键是否存在:" + jedis.exists("username")); System.out.println("新增<'username','kuangshen'>的键值对:"+jedis.set(" username", " kuangshen")); System.out.println("新增<'password','password'>的键值对:"+jedis.set(" password", " password")); System.out.print("系统中所有的键如下:"); Set<String> keys = jedis.keys("*"); System.out.println(keys); System.out.println("删除键password:" + jedis.del("password")); System.out.println("判断键password是否存在:"+jedis.exists(" password")); System.out.println("查看键username所存储的值的类型:"+jedis.type(" username")); System.out.println("随机返回key空间的一个:" + jedis.randomKey()); System.out.println("重命名key:" + jedis.rename("username", "name")); System.out.println("取出改后的name:" + jedis.get("name")); System.out.println("按索引查询:" + jedis.select(0)); System.out.println("删除当前选择数据库中的所有key:" + jedis.flushDB()); System.out.println("返回当前数据库中key的数目:" + jedis.dbSize()); System.out.println("删除所有数据库中的所有key:" + jedis.flushAll()); } }
②对String操作命令:
public class JedisStringDemo { public static void main(String[] args) { Jedis jedis = new Jedis("127.0.0.1", 6379); jedis.flushDB(); System.out.println("===========增加数据==========="); System.out.println(jedis.set("key1", "value1")); System.out.println(jedis.set("key2", "value2")); System.out.println(jedis.set("key3", "value3")); System.out.println("删除键key2:" + jedis.del("key2")); System.out.println("获取键key2:" + jedis.get("key2")); System.out.println("修改key1:" + jedis.set("key1", "value1Changed")); System.out.println("获取key1的值:" + jedis.get("key1")); System.out.println("在key3后面加入值:" + jedis.append("key3", "End")); System.out.println("key3的值:" + jedis.get("key3")); System.out.println("增加多个键值对:" + jedis.mset("key01", "value01", "key02", "value02", "key03", "value03")); System.out.println("获取多个键值对:" + jedis.mget("key01", "key02", "key03")); System.out.println("获取多个键值对:" + jedis.mget("key01", "key02", "key03", "key04")); System.out.println("删除多个键值对:" + jedis.del("key01", "key02")); System.out.println("获取多个键值对:" + jedis.mget("key01", "key02", "key03")); jedis.flushDB(); System.out.println("===========新增键值对防止覆盖原先值=============="); System.out.println(jedis.setnx("key1", "value1")); System.out.println(jedis.setnx("key2", "value2")); System.out.println(jedis.setnx("key2", "value2-new")); System.out.println(jedis.get("key1")); System.out.println(jedis.get("key2")); System.out.println("===========新增键值对并设置有效时间============="); System.out.println(jedis.setex("key3", 2, "value3")); System.out.println(jedis.get("key3")); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(jedis.get("key3")); System.out.println("===========获取原值,更新为新值=========="); System.out.println(jedis.getSet("key2", "key2GetSet")); System.out.println(jedis.get("key2")); System.out.println("获得key2的值的字串:" + jedis.getrange("key2", 2, 4)); } }
③对List的操作:
public class JedisListDemo { public static void main(String[] args) { Jedis jedis = new Jedis("127.0.0.1", 6379); jedis.flushDB(); System.out.println("===========添加一个list==========="); jedis.lpush("collections", "ArrayList", "Vector", "Stack", "HashMap", "WeakHashMap", "LinkedHashMap"); jedis.lpush("collections", "HashSet"); jedis.lpush("collections", "TreeSet"); jedis.lpush("collections", "TreeMap"); //-1代表倒数第一个元素,-2代表倒数第二个元素,end为-1表示查询全部 System.out.println("collections的内容:" + jedis.lrange("collections", 0, -1)); System.out.println("collections区间0-3的元素:" + jedis.lrange("collections", 0, 3)); System.out.println("==============================="); // 删除列表指定的值 ,第二个参数为删除的个数(有重复时),后add进去的值先被删,类似于出栈 System.out.println("删除指定元素个数:" + jedis.lrem("collections", 2, "HashMap")); System.out.println("collections的内容:" + jedis.lrange("collections", 0, -1)); System.out.println("删除下表0-3区间之外的元素:" + jedis.ltrim("collections", 0, 3)); System.out.println("collections的内容:" + jedis.lrange("collections", 0, -1)); System.out.println("collections列表出栈(左端):" + jedis.lpop("collections")); System.out.println("collections的内容:" + jedis.lrange("collections", 0, -1)); System.out.println("collections添加元素,从列表右端,与lpush相对应:" + jedis.rpush("collections", "EnumMap")); System.out.println("collections的内容:" + jedis.lrange("collections", 0, -1)); System.out.println("collections列表出栈(右端):" + jedis.rpop("collections")); System.out.println("collections的内容:" + jedis.lrange("collections", 0, -1)); System.out.println("修改collections指定下标1的内容:" + jedis.lset("collections", 1, "LinkedArrayList")); System.out.println("collections的内容:" + jedis.lrange("collections", 0, -1)); System.out.println("==============================="); System.out.println("collections的长度:" + jedis.llen("collections")); System.out.println("获取collections下标为2的元素:" + jedis.lindex("collections", 2)); System.out.println("==============================="); jedis.lpush("sortedList", "3", "6", "2", "0", "7", "4"); System.out.println("sortedList排序前:" + jedis.lrange("sortedList", 0, -1)); System.out.println(jedis.sort("sortedList")); System.out.println("sortedList排序后:" + jedis.lrange("sortedList", 0, -1)); } }
④对Set的操作:
public class JedisSetDemo { public static void main(String[] args) { Jedis jedis = new Jedis("127.0.0.1", 6379); jedis.flushDB(); System.out.println("============向集合中添加元素(不重复)============"); System.out.println(jedis.sadd("eleSet","e1","e2","e4","e3","e0","e8","e7","e5")); System.out.println(jedis.sadd("eleSet", "e6")); System.out.println(jedis.sadd("eleSet", "e6")); System.out.println("eleSet的所有元素为:"+jedis.smembers("eleSet")); System.out.println("删除一个元素e0:"+jedis.srem("eleSet", "e0")); System.out.println("eleSet的所有元素为:"+jedis.smembers("eleSet")); System.out.println("删除两个元素e7和e6:"+jedis.srem("eleSet","e7","e6")); System.out.println("eleSet的所有元素为:"+jedis.smembers("eleSet")); System.out.println("随机的移除集合中的一个元素:"+jedis.spop("eleSet")); System.out.println("随机的移除集合中的一个元素:"+jedis.spop("eleSet")); System.out.println("eleSet的所有元素为:"+jedis.smembers("eleSet")); System.out.println("eleSet中包含元素的个数:"+jedis.scard("eleSet")); System.out.println("e3是否在eleSet中:"+jedis.sismember("eleSet","e3")); System.out.println("e1是否在eleSet中:"+jedis.sismember("eleSet","e1")); System.out.println("e1是否在eleSet中:"+jedis.sismember("eleSet","e5")); System.out.println("================================="); System.out.println(jedis.sadd("eleSet1","e1","e2","e4","e3","e0","e8","e7","e5")); System.out.println(jedis.sadd("eleSet2", "e1","e2","e4","e3","e0","e8")); System.out.println("将eleSet1中删除e1并存入eleSet3中:"+jedis.smove("eleSet1", "eleSet3", "e1"));//移到集合元素 System.out.println("将eleSet1中删除e2并存入eleSet3中:"+jedis.smove("eleSet1", "eleSet3", "e2")); System.out.println("eleSet1中的元素:"+jedis.smembers("eleSet1")); System.out.println("eleSet3中的元素:"+jedis.smembers("eleSet3")); System.out.println("============集合运算================="); System.out.println("eleSet1中的元素:"+jedis.smembers("eleSet1")); System.out.println("eleSet2中的元素:"+jedis.smembers("eleSet2")); System.out.println("eleSet1和eleSet2的交集:"+jedis.sinter("eleSet1","eleSet2")); System.out.println("eleSet1和eleSet2的并集:"+jedis.sunion("eleSet1","eleSet2")); System.out.println("eleSet1和eleSet2的差集:"+jedis.sdiff("eleSet1","eleSet2"));//eleSet1中有,eleSet2中没有 jedis.sinterstore("eleSet4","eleSet1","eleSet2");//求交集并将交集保存到dstkey的集合 System.out.println("eleSet4中的元素:"+jedis.smembers("eleSet4")); } }
⑤对Hash的操作:
public class JedisHashDemo { public static void main(String[] args) { Jedis jedis = new Jedis("127.0.0.1", 6379); jedis.flushDB(); Map<String, String> map = new HashMap(); map.put("key1", "value1"); map.put("key2", "value2"); map.put("key3", "value3"); map.put("key4", "value4"); //添加名称为hash(key)的hash元素 jedis.hmset("hash", map); //向名称为hash的hash中添加key为key5,value为value5元素 jedis.hset("hash", "key5", "value5"); System.out.println("散列hash的所有键值对为:" + jedis.hgetAll("hash"));//return Map<String,String> System.out.println("散列hash的所有键为:"+jedis.hkeys("hash"));//return Set<String> System.out.println("散列hash的所有值为:"+jedis.hvals("hash"));//return List<String> System.out.println("将key6保存的值加上一个整数,如果key6不存在则添加key6:"+jedis.hincrBy("hash", "key6", 6)); System.out.println("散列hash的所有键值对为:"+jedis.hgetAll("hash")); System.out.println("将key6保存的值加上一个整数,如果key6不存在则添加key6:"+jedis.hincrBy("hash", "key6", 3)); System.out.println("散列hash的所有键值对为:"+jedis.hgetAll("hash")); System.out.println("删除一个或者多个键值对:"+jedis.hdel("hash", "key2")); System.out.println("散列hash的所有键值对为:"+jedis.hgetAll("hash")); System.out.println("散列hash中键值对的个数:"+jedis.hlen("hash")); System.out.println("判断hash中是否存在key2:"+jedis.hexists("hash","key2")); System.out.println("判断hash中是否存在key3:"+jedis.hexists("hash","key3")); System.out.println("获取hash中的值:"+jedis.hmget("hash","key3")); System.out.println("获取hash中的值:"+jedis.hmget("hash","key3","key4")); } }
2.3.Jedis执行事务操作
public class MultiDemo { public static void main(String[] args) { //创建客户端连接服务端,redis服务端需要被开启 Jedis jedis = new Jedis("127.0.0.1", 6379); jedis.flushDB(); JSONObject jsonObject = new JSONObject(); jsonObject.put("hello", "world"); jsonObject.put("name", "java"); //开启事务 Transaction multi = jedis.multi(); String result = jsonObject.toJSONString(); try { //向redis存入一条数据 multi.set("json", result); //再存入一条数据 multi.set("json2", result); //这里引发了异常,用0作为被除数 int i = 100 / 0; //如果没有引发异常,执行进入队列的命令 multi.exec(); } catch (Exception e) { e.printStackTrace(); //如果出现异常,回滚 multi.discard(); } finally { System.out.println(jedis.get("json")); System.out.println(jedis.get("json2")); //最终关闭客户端 jedis.close(); } } }
3.SpringBoot整合Redis
3.1.源码依赖分析
在SpringBoot2.x之后,原来使用的jedis被替换成了lettuce。
lettuce底层采用的是netty,实例中可以再多个线程中进行共享,不存在线程不安全的情况,可以减少线程的数量(NIO模式);
jedis底层采用的是直联,多个线程操作的话,是不安全的,如果要避免不安全的情况,则需要使用jedis pool连接池,耗费资源开销比较大(BIO模式)。
源码分析:
@Bean @ConditionalOnMissingBean(name = {"redisTemplate"}) //我们可以自定义一个redisTemplate来替换这个默认的bean public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
//默认的RedisTemplate没有过多的设置,redis对象都是需要序列化的
//这里的template对象的泛型都是Object的,我们后续使用需要进行替换成String类型的 RedisTemplate<Object, Object> template = new RedisTemplate(); template.setConnectionFactory(redisConnectionFactory); return template; } @Bean @ConditionalOnMissingBean //由于String类型是redis中常用的类型,所以单独提取了一个StringRedisTempalte出来 public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { StringRedisTemplate template = new StringRedisTemplate(); template.setConnectionFactory(redisConnectionFactory); return template; }
所以,包括连接池的配置,在SpringBoot2.x中需要使用lettuce的pool。
3.2.整合配置
①导入依赖:
<!--操作redis--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
②配置连接:
spring: datasource: # 驱动配置信息 url: jdbc:mysql://localhost:3306/spring_boot?useUnicode=true&characterEncoding=utf8 username: root password: xxxxx type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver # 连接池的配置信息 filters: stat maxActive: 20 initialSize: 1 maxWait: 60000 minIdle: 1 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: select 'x' testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true maxOpenPreparedStatements: 20 #redis相关配置 redis: # 数据库索引 database: 0 # 远程服务器地址 host: 101.37.xx.xxx # 远程服务器端口 port: 6379 password: xxxxxx jedis: pool: # 最大空闲连接 max-idle: 8 # 最小空闲链接 min-idle: 0 # 最大连接数(负数表示没有限制) max-active: 8 # 最大阻塞等待时间(负数表示没有限制) max-wait: 0 # 链接超时时间(毫秒) timeout: 200
3.3.RedisTemplate操作Redis
①默认的redisTemplate操作API:
@Autowired
private RedisTemplate redisTemplate;
@Test
void contextLoads() {
/***
* redisTemplate使用:
* opsForValue:操作字符串,类似于String
* opsForList:操作List
* opsForSet
* opsForHash
* opsForZset
* opsForGeo
*opsForHyperLogLog
*/
//获取redis的连接对象
/*RedisConnection redisConnection = redisTemplate.getConnectionFactory().getConnection();
redisConnection.flushAll();
redisConnection.flushDb();*/
redisTemplate.opsForValue().set("mykey", "这个是中文");
System.out.println(redisTemplate.opsForValue().get("mykey"));
}
②自定义重写序列化redieTemplate:
重写的RedisTemplate实现了String key的操作,并对json序列化进行了封装,只要对象实现Serializable接口,就可以进行存取封装操作:
@Configuration
public class RedisConfig {
@Bean
@SuppressWarnings("all")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
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();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
3.4.封装RedisUtil工具类
写一个Redis工具类:
相比于直接用RedisTemplate操作Redis,需要很多行代码,因此直接封装好一个 RedisUtils,这样写代码更方便点。这个RedisUtils交给Spring容器实例化,使用时直接注解注入。
@Component public class RedisUtil { @Autowired private RedisTemplate<String, Object> redisTemplate; // =============================common============================ /** * 指定缓存失效时间 * * @param key 键 * @param time 时间(秒) */ 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 获取过期时间 * * @param key 键 不能为null * @return 时间(秒) 返回0代表为永久有效 */ public long getExpire(String key) { return redisTemplate.getExpire(key, TimeUnit.SECONDS); } /** * 判断key是否存在 * * @param key 键 * @return true 存在 false不存在 */ public boolean hasKey(String key) { try { return redisTemplate.hasKey(key); } catch (Exception e) { e.printStackTrace(); return false; } } /** * 删除缓存 * * @param key 可以传一个值 或多个 */ @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============================= /** * 普通缓存获取 * * @param key 键 * @return 值 */ public Object get(String key) { return key == null ? null : redisTemplate.opsForValue().get(key); } /** * 普通缓存放入 * * @param key 键 * @param value 值 * @return true成功 false失败 */ public boolean set(String key, Object value) { try { redisTemplate.opsForValue().set(key, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 普通缓存放入并设置时间 * * @param key 键 * @param value 值 * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期 * @return true成功 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; } } /** * 递增 * * @param key 键 * @param delta 要增加几(大于0) */ public long incr(String key, long delta) { if (delta < 0) { throw new RuntimeException("递增因子必须大于0"); } return redisTemplate.opsForValue().increment(key, delta); } /** * 递减 * * @param key 键 * @param delta 要减少几(小于0) */ public long decr(String key, long delta) { if (delta < 0) { throw new RuntimeException("递减因子必须大于0"); } return redisTemplate.opsForValue().increment(key, -delta); } // ================================Map================================= /** * HashGet * * @param key 键 不能为null * @param item 项 不能为null */ 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); } /** * HashSet * * @param key 键 * @param map 对应多个键值 */ 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) */ public double hincr(String key, String item, double by) { return redisTemplate.opsForHash().increment(key, item, by); } /** * hash递减 * * @param key 键 * @param item 项 * @param by 要减少记(小于0) */ public double hdecr(String key, String item, double by) { return redisTemplate.opsForHash().increment(key, item, -by); } // ============================set============================= /** * 根据key获取Set中的所有值 * * @param key 键 */ public Set<Object> sGet(String key) { try { return redisTemplate.opsForSet().members(key); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 根据value从一个set中查询,是否存在 * * @param key 键 * @param value 值 * @return true 存在 false不存在 */ public boolean sHasKey(String key, Object value) { try { return redisTemplate.opsForSet().isMember(key, value); } catch (Exception e) { e.printStackTrace(); return false; } } /** * 将数据放入set缓存 * * @param key 键 * @param values 值 可以是多个 * @return 成功个数 */ public long sSet(String key, Object... values) { try { return redisTemplate.opsForSet().add(key, values); } catch (Exception e) { e.printStackTrace(); return 0; } } /** * 将set数据放入缓存 * * @param key 键 * @param time 时间(秒) * @param values 值 可以是多个 * @return 成功个数 */ 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缓存的长度 * * @param key 键 */ public long sGetSetSize(String key) { try { return redisTemplate.opsForSet().size(key); } catch (Exception e) { e.printStackTrace(); return 0; } } /** * 移除值为value的 * * @param key 键 * @param values 值 可以是多个 * @return 移除的个数 */ 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缓存的内容 * * @param key 键 * @param start 开始 * @param end 结束 0 到 -1代表所有值 */ 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缓存的长度 * * @param key 键 */ public long lGetListSize(String key) { try { return redisTemplate.opsForList().size(key); } catch (Exception e) { e.printStackTrace(); return 0; } } /** * 通过索引 获取list中的值 * * @param key 键 * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0 * 时,-1,表尾,-2倒数第二个元素,依次类推 */ public Object lGetIndex(String key, long index) { try { return redisTemplate.opsForList().index(key, index); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 将list放入缓存 * * @param key 键 * @param value 值 */ public boolean lSet(String key, Object value) { try { redisTemplate.opsForList().rightPush(key, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 将list放入缓存 * * @param key 键 * @param value 值 * @param time 时间(秒) */ 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放入缓存 * * @param key 键 * @param value 值 * @return */ public boolean lSet(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 */ 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中的某条数据 * * @param key 键 * @param index 索引 * @param value 值 * @return */ 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 * * @param key 键 * @param count 移除多少个 * @param value 值 * @return 移除的个数 */ 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; } } }
封装一个redisServiceUtil类:
@Component public class RedisUtilService { @Resource private RedisTemplate<String, String> redisTemplate; /** * 普通缓存放入并设置时间 * * @param key 键 * @param value 值 * @param time 时间(秒)time要大于0如果time小于等于0将设置无限期 * @return true成功false失败 */ public boolean cacheSet(String key, String value, long time) { boolean result = false; if (!Objects.isNull(key)) { if (time > 0) { redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); } else { cacheSet(key, value); } result = true; } return result; } /** * 普通缓存放入 * * @param key 键 * @param value 值 * @return true成功false失败 */ public boolean cacheSet(String key, String value) { boolean result = false; if (!Objects.isNull(key)) { redisTemplate.opsForValue().set(key, value); result = true; } return result; } /** * 缓存获取 * * @param key 键 * @return 值 */ public String cacheGet(String key) { return this.redisTemplate.opsForValue().get(key); } /** * 删除缓存 * * @param key 可以传一个值或者多个 */ public void del(String key) { redisTemplate.delete(key); } }
示例代码已上传至Github地址:
https://github.com/devyf/SpringBoot_Study/tree/master/Springboot_redis