专业做域名的网站吗,随州seo推广,电脑做科目一网站,男男床做第一次视频网站在前几篇文章中#xff0c;我们详细介绍了 Redis 的一些功能特性以及主流的 java 客户端 api 使用方法。 
在当前流行的微服务以及分布式集群环境下#xff0c;Redis 的使用场景可以说非常的广泛#xff0c;能解决集群环境下系统中遇到的不少技术问题#xff0c;在此列举几…在前几篇文章中我们详细介绍了 Redis 的一些功能特性以及主流的 java 客户端 api 使用方法。 
在当前流行的微服务以及分布式集群环境下Redis 的使用场景可以说非常的广泛能解决集群环境下系统中遇到的不少技术问题在此列举几个使用 Redis 经常用到的功能 
分布式缓存在分布式的集群架构中将缓存存储在内存中会出现很多的问题比如用户回话信息因为这部分信息需要与其他机器共享此时利用 Redis 可以很好的解决机器之间数据共享的问题缓存也是 Redis 中使用最多的场景分布式锁在高并发的情况下我们需要一个锁来防止并发带来的脏数据Java 自带的锁机制显然对进程间的并发并不好使此时利用 Redis 的单线程特性实现分布式锁控制接口限流在集群环境下可以利用 Redis 的分布式自增 ID 功能精准的统计每个接口在指定时间内的请求次数利用这个特性可以定向限制某个接口恶意频刷 
当然 Redis 的使用场景并不仅仅只有这么多还有很多未列出的场景如发布/订阅分布锁集合等。 
现实中我们大部分的微服务项目都是基于 SpringBoot 框架进行快速开发在 SpringBoot 项目中我们应该如何使用 Redis 呢代码实践如下。 
1. 开发环境 
IDEA2021.3.3JDK1.8SpringBoot2.7.14Maven3.6.3 
咱们通过程序是不能直接连接 Redis得利用客户端工具才能进行连接。比较常用的有两种Jedis、Lettuce。 
在 springboot 1.5.x 版本的默认的 Redis 客户端是 Jedis 实现的springboot 2.x 版本中默认客户端是用 lettuce实现的。 
既然 Lettuce 和 Jedis 的都是连接 Redis 的客户端那么它们有什么区别呢 
Jedis 在实现上是直连 Redis Server多线程环境下非线程安全除非使用连接池为每个 Redis 实例增加 物理连接Lettuce 是 一种可伸缩线程安全完全非阻塞的Redis客户端多个线程可以共享一个 RedisConnection它利用 Netty NIO 框架来高效地管理多个连接从而提供了异步和同步数据访问方式用于构建非阻塞的反应性应用程序 
2. 代码实战 
在 SpringBoot 集成的 Redis 时我这里采用的是 Lettuce 
2.1 默认使用 Lettuce 
1、引入依赖 
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId
/dependency2、添加配置 
spring:redis:host: localhostport: 6379password:timeout: 2000s# 配置文件中添加 lettuce.pool 相关配置则会使用到lettuce连接池lettuce:pool:max-active: 8  # 连接池最大连接数(使用负值表示没有限制) 默认为8max-wait: -1ms # 接池最大阻塞等待时间(使用负值表示没有限制) 默认为-1msmax-idle: 8    # 连接池中的最大空闲连接 默认为8min-idle: 0    # 连接池中的最小空闲连接 默认为 02.2 换成 Jedis 
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactIdexclusionsexclusiongroupIdio.lettuce/groupIdartifactIdlettuce-core/artifactId/exclusion/exclusions
/dependency
dependencygroupIdredis.clients/groupIdartifactIdjedis/artifactId
/dependencyjedis 中会引入 commons-pool2 依赖如果没有引入 
dependencygroupIdorg.apache.commons/groupIdartifactIdcommons-pool2/artifactId
/dependency添加配置 
spring:redis:host: localhostport: 6379password:timeout: 2000s# 配置文件中添加 jedis.pool 相关配置则会使用到 jedis 连接池jedis:pool:max-active: 10max-idle: 8min-idle: 0max-wait: 60s2.3 使用 RedisTemplate 对象操作 Redis 
在 SpringBoot 中是使用 RedisTemplate 对象来操作 Redis 的。 
在 Springboot 自动配置原理中涉及到以下两方面 
SpringBoot 中所有的配置类都有一个自动配置类。RedisAutoConfiguration自动配置类都会绑定一个配置文件 properties。RedisProperties 
RedisAutoConfiguration.class 
public class RedisAutoConfiguration {public RedisAutoConfiguration() {}BeanConditionalOnMissingBean(name  {redisTemplate})ConditionalOnSingleCandidate(RedisConnectionFactory.class)public RedisTemplateObject, Object redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplateObject, Object template  new RedisTemplate();template.setConnectionFactory(redisConnectionFactory);return template;}BeanConditionalOnMissingBeanConditionalOnSingleCandidate(RedisConnectionFactory.class)public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {return new StringRedisTemplate(redisConnectionFactory);}
}StringRedisTemplate 
public class StringRedisTemplate extends RedisTemplateString, String {public StringRedisTemplate() {this.setKeySerializer(RedisSerializer.string());this.setValueSerializer(RedisSerializer.string());this.setHashKeySerializer(RedisSerializer.string());this.setHashValueSerializer(RedisSerializer.string());}public StringRedisTemplate(RedisConnectionFactory connectionFactory) {this();this.setConnectionFactory(connectionFactory);this.afterPropertiesSet();}protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {return new DefaultStringRedisConnection(connection);}
}通过上述看注入了两个类型的 RedisTemplate 对象 
如果没有注入名称为 redisTemplate 的 RedisTemplate 对象则注入 RedisTemplateObject, Object 对象注入 StringRedisTemplate 对象。而 StringRedisTemplate 对象又是继承 RedisTemplateString, String 类的 
使用上面两个类型 RedisTemplate 的对象操作 Redis 
RestController
RequestMapping(/redis)
public class RedisController {Autowiredprivate StringRedisTemplate stringRedisTemplate;Autowiredprivate RedisTemplateObject, Object redisTemplate;GetMapping(/set)public String set() {stringRedisTemplate.opsForValue().set(name, zzc);redisTemplate.opsForValue().set(age, zzc);return set;}
}调用成功后我们使用 Redis 客户端工具进行查看 发现 
redisTemplate.opsForValue().set(age, zzc); 操作的 key、value 都变成乱码。 
springboot系列——redisTemplate和stringRedisTemplate对比、redisTemplate几种序列化方式比较 
通过 debug 源代码知RedisTemplateObject, Object 的 key、value 序列化默认都是 JdkSerializationRedisSerializer序列化方法如下 
default byte[] serializeToByteArray(T object) throws IOException {ByteArrayOutputStream out  new ByteArrayOutputStream(1024);this.serialize(object, out);return out.toByteArray();
}public void serialize(Object object, OutputStream outputStream) throws IOException {if (!(object instanceof Serializable)) {throw new IllegalArgumentException(this.getClass().getSimpleName()   requires a Serializable payload but received an object of type [  object.getClass().getName()  ]);} else {ObjectOutputStream objectOutputStream  new ObjectOutputStream(outputStream);objectOutputStream.writeObject(object);objectOutputStream.flush();}
}将 key、value 进行序列化成 byte 类型所以看上去会乱码。可读性差 
而 StringRedisTemplate 对象使用 RedisSerializer 序列的 
2.4 自定义 RedisTemplate 对象 
为了可读性可以使用 StringRedisTemplate 类但有一个要求key、value 都要求是 String 类型。 
但这就有一个问题我们平时用得对象比较多那又如何存储对象呢 
例如我们这里的 User 对象 
public class User {private String id;private String userName;private Integer age;// getter/setter
}由于 RedisTemplateString, String 的泛型参数都是 String 类型的那我们只需要将 Java 对象转换为 String 对象即可 
Override
public boolean addUser(User user) {redisTemplate.opsForValue().set(user, JSON.toJSONString(user));String strUser  redisTemplate.opsForValue().get(user1);User resultUser  JSON.parseObject(strUser, User.class);return true;
}存 Redis 之前将 Java 对象转换为 Json 字符串读取后将 Json 字符串转换为 Java 对象。 
这样做确实可行但是如果要存储的对象较多的话那岂不是要重复地将 Java 对象转换为 Json 字符串这样是不是很繁琐 
继续看 RedisAutoConfiguration.class 源码发现被注入的 RedisTemplateObject, Object 被 ConditionalOnMissingBean(nameredisTemplate) 注解修饰如果 Spring 容器中有了 RedisTemplate 对象了这个自动配置的 RedisTemplate 不会实例化。因此我们可以直接自己写个配置类配置 RedisTemplate。并且我们更希望 key 是 String 类型value 是 Object 类型String、int、对象等类型 
Configuration
public class RedisConfig {Beanpublic RedisTemplateString, Object redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplateString, Object template  new RedisTemplate();template.setConnectionFactory(redisConnectionFactory);ObjectMapper om  new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);// json 序列化配置Jackson2JsonRedisSerializerObject jackson2JsonRedisSerializer  new Jackson2JsonRedisSerializer(Object.class);jackson2JsonRedisSerializer.setObjectMapper(om);// String 序列化StringRedisSerializer stringRedisSerializer  new StringRedisSerializer();// 所有的 key 采用 string 的序列化template.setKeySerializer(stringRedisSerializer);// 所有的 value 采用 jackson 的序列化template.setValueSerializer(jackson2JsonRedisSerializer);// hash 的 key 采用 string 的序列化template.setHashKeySerializer(stringRedisSerializer);// hash 的 value 采用 jackson 的序列化template.setHashValueSerializer(jackson2JsonRedisSerializer);template.afterPropertiesSet();return template;}
}测试 
RestController
RequestMapping(/redis)
public class RedisController {Autowiredprivate RedisTemplateString, Object redisTemplate;GetMapping(/set)public String set() {User user  new User();user.setName(zzc);user.setAge(18);redisTemplate.opsForValue().set(user, user);return set;}}2.5 RedisUtil 工具类 
Component
public class RedisUtil {Autowiredprivate RedisTemplateString, Object redisTemplate;// Commonpublic void setHashValueSerializer(RedisSerializer serializer) {redisTemplate.setHashValueSerializer(serializer);}/*** 指定缓存失效时间** author zzc* date 2023/8/2 11:06* param key    键* param time   时间(秒)* return boolean*/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 获取过期时间** author zzc* date 2023/8/2 11:07* param key    键 不能为null* return long  时间(秒) 返回0代表为永久有效*/public Long getExpire(String key) {return redisTemplate.getExpire(key, TimeUnit.SECONDS);}/*** 判断key是否存在** author zzc* date 2023/8/2 11:07* param key      键* return boolean 存在 false不存在*/public boolean hasKey(String key) {try {return Boolean.TRUE.equals(redisTemplate.hasKey(key));} catch (Exception e) {e.printStackTrace();return false;}}/*** 删除缓存** author zzc* date 2023/8/2 11:08* param key   可以传一个值 或多个*/SuppressWarnings(unchecked)public void del(String... key) {if (key ! null  key.length  0) {if (key.length  1) {redisTemplate.delete(key[0]);} else {//springboot2.4后用法redisTemplate.delete(Arrays.asList(key));}}}/*** 获取指定前缀的一系列key* 使用scan命令代替keys, Redis是单线程处理keys命令在KEY数量较多时* 操作效率极低【时间复杂度为O(N)】该命令一旦执行会严重阻塞线上其它命令的正常请求** author zzc* date 2023/8/2 11:53* param keyPrefix* return java.util.Setjava.lang.String*/public SetString keys(String keyPrefix) {String realKey  keyPrefix  *;try {return redisTemplate.execute((RedisCallbackSetString) connection - {SetString binaryKeys  new HashSet();//springboot2.4后用法Cursorbyte[] cursor  connection.scan(ScanOptions.scanOptions().match(realKey).count(Integer.MAX_VALUE).build());while (cursor.hasNext()) {binaryKeys.add(new String(cursor.next()));}return binaryKeys;});} catch (Throwable e) {e.printStackTrace();}return null;}/*** 删除指定前缀的一系列key** author zzc* date 2023/8/2 11:53* param keyPrefix*/public void removeAll(String keyPrefix) {try {SetString keys  keys(keyPrefix);redisTemplate.delete(keys);} catch (Throwable e) {e.printStackTrace();}}// 执行 lua 脚本public T T execute(RedisScriptT script, ListString keys, Object... args) {return redisTemplate.execute(script, keys, args);}public boolean convertAndSend(String channel, Object message) {if (!StringUtils.hasText(channel)) {return false;}try {redisTemplate.convertAndSend(channel, message);return true;} catch (Exception e) {e.printStackTrace();}return false;}// String/*** 普通缓存获取** author zzc* date 2023/8/2 11:08* param key                   键* return java.lang.Object     值*/public Object get(String key) {return key  null ? null : redisTemplate.opsForValue().get(key);}/*** 普通缓存放入* author zzc* date 2023/8/2 11:09* param key           键* param value         值* return boolean      true成功 false失败*/public boolean set(String key, Object value) {try {redisTemplate.opsForValue().set(key, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 普通缓存放入并设置过期时间** author zzc* date 2023/8/2 11:09* param key     键* param value   值* param time    时间(秒) time要大于0 如果time小于等于0 将设置无限期* return boolean  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;}}/*** 递增* author zzc* date 2023/8/2 11:10* param key         键* param delta       要增加几(大于0)* return java.lang.Long*/public Long incr(String key, long delta) {if (delta  0) {throw new RuntimeException(递增因子必须大于0);}return redisTemplate.opsForValue().increment(key, delta);}/*** 递减** author zzc* date 2023/8/2 11:11* param key                 键* param delta               要减少几(小于0)* return java.lang.Long*/public Long decr(String key, long delta) {if (delta  0) {throw new RuntimeException(递减因子必须大于0);}return redisTemplate.opsForValue().increment(key, -delta);}public boolean setNx(String key, Object value) {return Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, value));}public boolean setNx(String key, Object value, long time) {return Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, value, time, TimeUnit.SECONDS));}public void multiSet(MapString, Object map) {redisTemplate.opsForValue().multiSet(map);}public ListObject multiGet(ListString keys) {return redisTemplate.opsForValue().multiGet(keys);}// Hash/*** Hash Get* author zzc* date 2023/8/2 11:12* param key                键 不能为null* param item               项 不能为null* return java.lang.Object*/public Object hget(String key, String item) {return redisTemplate.opsForHash().get(key, item);}/*** 获取Key对应的所有键值** author zzc* date 2023/8/2 11:12* param key                                                  键* return java.util.Mapjava.lang.Object,java.lang.Object    对应的多个键值*/public MapObject, Object hmget(String key) {return redisTemplate.opsForHash().entries(key);}/*** Hash Set** author zzc* date 2023/8/2 11:13* param key         键* param map         对应多个键值* return boolean    true 成功 false 失败*/public boolean hmset(String key, MapString, Object map) {try {redisTemplate.opsForHash().putAll(key, map);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** Hash Set 并设置过期时间* author zzc* date 2023/8/2 11:13* param key        键* param map        对应多个键值* param time       时间(秒)* return boolean   true成功 false失败*/public boolean hmset(String key, MapString, 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表中的项** author zzc* date 2023/8/2 11:38* param key   键 不能为null* param item  项 可以使多个 不能为null*/public void hdel(String key, Object... item) {redisTemplate.opsForHash().delete(key, item);}/*** 判断 hash 表中是否有该项的值** author zzc* date 2023/8/2 11:38* param key    键 不能为null* param item   项 不能为null* return boolean  true 存在 false不存在*/public boolean hHasKey(String key, String item) {return redisTemplate.opsForHash().hasKey(key, item);}/*** hash 递增 如果不存在,就会创建一个 并把新增后的值返回** author zzc* date 2023/8/2 11:40* param key    键* param item   项* param by     要增加几(大于0)* return double*/public double hincr(String key, String item, double by) {return redisTemplate.opsForHash().increment(key, item, by);}public Long hincr(String key, String item, long by) {return redisTemplate.opsForHash().increment(key, item, by);}/*** hash 递减** author zzc* date 2023/8/2 11:40* param key    键* param item   项* param by     要减少几(小于0)* return double*/public double hdecr(String key, String item, double by) {return redisTemplate.opsForHash().increment(key, item, -by);}public ListObject hmultiGet(String key, ListObject items) {return redisTemplate.opsForHash().multiGet(key, items);}// set/*** 根据key获取Set中的所有值** author zzc* date 2023/8/2 11:41* param key* return java.util.Setjava.lang.Object*/public SetObject sGet(String key) {try {return redisTemplate.opsForSet().members(key);} catch (Exception e) {e.printStackTrace();return null;}}/*** 根据value从一个set中查询,是否存在** author zzc* date 2023/8/2 11:41* param key       键* param value     值* return boolean  true 存在 false不存在*/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缓存** author zzc* date 2023/8/2 11:42* param key       键* param values    值 可以是多个* return long     成功个数*/public Long sSet(String key, Object... values) {try {return redisTemplate.opsForSet().add(key, values);} catch (Exception e) {e.printStackTrace();return 0L;}}/*** 将 set 数据放入缓存** author zzc* date 2023/8/2 11:42* param key     键* param time    时间(秒)* param values  值 可以是多个* return long   成功个数*/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 0L;}}/*** 获取set缓存的长度** author zzc* date 2023/8/2 11:45* param key* return long*/public Long sGetSetSize(String key) {try {return redisTemplate.opsForSet().size(key);} catch (Exception e) {e.printStackTrace();return 0L;}}/*** 移除值为value的** author zzc* date 2023/8/2 11:45* param key    键* param values 值 可以是多个* return long  移除的个数*/public Long setRemove(String key, Object... values) {try {return redisTemplate.opsForSet().remove(key, values);} catch (Exception e) {e.printStackTrace();return 0L;}}// List/*** 获取list缓存的内容** author zzc* date 2023/8/2 11:46* param key      键* param start    开始* param end      结束 0 到 -1代表所有值* return java.util.Listjava.lang.Object*/public ListObject lGet(String key, long start, long end) {try {return redisTemplate.opsForList().range(key, start, end);} catch (Exception e) {e.printStackTrace();return null;}}/*** 获取list缓存的长度** author zzc* date 2023/8/2 11:47* param key* return long*/public Long lGetListSize(String key) {try {return redisTemplate.opsForList().size(key);} catch (Exception e) {e.printStackTrace();return 0L;}}/*** 通过索引 获取list中的值** author zzc* date 2023/8/2 11:47* param key     键* param index   索引 index0时 0 表头1 第二个元素依次类推index0时-1表尾-2倒数第二个元素依次类推* return java.lang.Object*/public Object lGetIndex(String key, long index) {try {return redisTemplate.opsForList().index(key, index);} catch (Exception e) {e.printStackTrace();return null;}}/*** 将list放入缓存* author zzc* date 2023/8/2 11:48* param key       键* param value     值* return boolean*/public boolean lSet(String key, Object value) {try {redisTemplate.opsForList().rightPush(key, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 将list放入缓存* author zzc* date 2023/8/2 11:48* param key       键* param value     值* param time  时间(秒)* return boolean*/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放入缓存** author zzc* date 2023/8/2 11:49* param key        键* param value      值* return boolean   时间(秒)*/public boolean lSet(String key, ListObject value) {try {redisTemplate.opsForList().rightPushAll(key, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 将list放入缓存** author zzc* date 2023/8/2 11:49* param key        键* param value      值* param time       时间(秒)* return boolean*/public boolean lSet(String key, ListObject 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中的某条数据** author zzc* date 2023/8/2 11:51* param key     键* param index   索引* param value   值* return boolean*/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** author zzc* date 2023/8/2 11:51* param key    键* param count  移除多少个* param value  值* return long  移除的个数*/public Long lRemove(String key, long count, Object value) {try {return redisTemplate.opsForList().remove(key, count, value);} catch (Exception e) {e.printStackTrace();return 0L;}}}