Redis使用进阶
source link: https://www.tuicool.com/articles/um2e2ya
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Redis使用进阶(Java)
Redis之使用教程(Java版)
博客地址 https://blog.piaoruiqing.com/blog/2019/06/11/redis使用进阶关键词
Jedis Lettuce spring-data-redis
前言
本文将针对使用Java集成Redis进行讲解, Jedis
及 Lettuce
的使用仅作简单描述, spring
的 redis
集成及使用将作为主要讲解内容.
Jedis
https://github.com/xetorthio/jedis
引入依赖:
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.3</version> </dependency>
Jedis
是对redis命令的封装, 使用上基本与 redis-cli
无异, 操作string的使用示例如下:
/** jedis pool */ private static final JedisPool POOL = new JedisPool(new JedisPoolConfig(), "test-redis-server", 6379); // test Binary-safe strings with Jedis try (Jedis jedis = POOL.getResource()){ { // SET mykey myvalue String result = jedis.set("mykey", "myvalue"); LOGGER.info("cmd: SET mykey myvalue, result: {}", result); } { // GET mykey String result = jedis.get("mykey"); LOGGER.info("cmd: GET mykey, result: {}", result); } { // KEYS my* Set<String> keys = jedis.keys("my*"); LOGGER.info("cmd: KEYS my*, result: {}", JsonUtils.writeValueAsString(keys, true)); } { // EXISTS mykey Boolean result = jedis.exists("mykey"); LOGGER.info("cmd: EXISTS mykey, result: {}", result); } { // DEL mykey Long result = jedis.del("mykey"); LOGGER.info("cmd: DEL mykey, result: {}", result); } { // GET mykey String result = jedis.get("mykey"); LOGGER.info("cmd: GET mykey, result: {}", result); } }
-
JedisPool
: Jedis并不是线程安全的, 所以多线程情况下不应共用Jedis
实例, 但创建大量的Jedis会造成不必要的开销甚至对性能产生较大影响, 故使用JedisPool
来避免这些问题, 它是一个线程安全的网络连接池. 可以使用它可靠地创建多个Jedis实例, 完成后将Jedis实例回收到连接池中. -
JedisPool.getResource
: 从连接池获取一个Jedis连接, 注意:Jedis
使用完毕后需要调用Jedis.close
方法释放资源.(Jedis
实现了AutoCloseable
, 推荐使用try-with-resource
的写法)
Lettuce
引入依赖:
<dependency> <groupId>io.lettuce</groupId> <artifactId>lettuce-core</artifactId> <version>5.1.7.RELEASE</version> </dependency>
Lettuce是一个可扩展的Redis客户端,用于构建非阻塞的Reactive应用程序. 它基于Netty框架构建, 性能较高, 且支持很多redis的高级特性. 目前springboot2.0已将Lettuce作为默认redis客户端. 与上一小节对应, 操作string的使用示例如下:
/** redis client */ private static final RedisClient CLIENT = RedisClient.create("redis://@test-redis-server:6379/0"); // test Binary-safe strings with Lettuce try (StatefulRedisConnection<String, String> connection = CLIENT.connect()) { RedisCommands<String, String> commands = connection.sync(); { // SET mykey myvalue String result = commands.set("mykey", "myvalue"); LOGGER.info("cmd: SET mykey myvalue, result: {}", result); } { // GET mykey String result = commands.get("mykey"); LOGGER.info("cmd: GET mykey, result: {}", result); } { // KEYS my* List<String> keys = commands.keys("my*"); LOGGER.info("cmd: KEYS my*, result: {}", JsonUtils.writeValueAsString(keys, true)); } { // EXISTS mykey Long result = commands.exists("mykey"); LOGGER.info("cmd: EXISTS mykey, result: {}", result); } { // DEL mykey Long result = commands.del("mykey"); LOGGER.info("cmd: DEL mykey, result: {}", result); } { // GET mykey String result = commands.get("mykey"); LOGGER.info("cmd: GET mykey, result: {}", result); } }
Spring 集成
spring-data-redis
是 Spring Data
家族的一部分, 提供了简单的配置以轻松访问redis, 针对存储操作提供了低级别和高级别的抽象, 将开发人员从基础实现中解放出来.
引入依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>2.1.5.RELEASE</version> </dependency>
使用 spring-data-redis
开发时, 可能最常使用的就是 RedisTemplate
, 所以在开始前我们先了解下 RedisTemplate
:
-
RedisTemplate
是一个简化了Redis访问的工具类. - 线程安全(thread-safe), 作为单例使用即可.
-
其实现围绕
execute
方法, 支持callback, 它提供的RedisConnection
处理方式不需要关心连接的声明周期(简言之就是不用创建也不用关连接)
使用方法很简单, 首先在 Configuration
中定义 StringRedisTemplate
的Bean:
/** * StringRedisTemplate * @param factory * @return */ @Bean public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) { StringRedisTemplate template = new StringRedisTemplate(factory); StringRedisSerializer serializer = new StringRedisSerializer(); // (一) template.setKeySerializer(serializer); // (二) template.setHashKeySerializer(serializer); template.setValueSerializer(serializer); template.setHashValueSerializer(serializer); return template; }
-
(一):
RedisSerializer
: 对象到二进制数组序列化和反序列化接口, 序列化和反序列化key和value,StringRedisSerializer
、GenericJackson2JsonRedisSerializer
都是其实现. -
(二):
KeySerializer
用来序列化redis key,HashKeySerializer
用来序列化redis hash数据结构的field. 请勿混淆.
当然, 不要忘记了 application.yml
中添加redis相关配置:
spring: redis: host: test-redis-server port: 6379
准备工作完成了, 现在就来体验一下, 同样地与前文对应, 操作string的使用示例如下: :
@Resource private StringRedisTemplate stringRedisTemplate; /** * test Binary-safe strings with RedisTemplate */ @Test public void testStringRedisTemplateSimple() { { // SET mykey myvalue stringRedisTemplate.opsForValue().set("mykey", "myvalue"); } { // GET mykey String result = stringRedisTemplate.opsForValue().get("mykey"); LOGGER.info("cmd: GET mykey, result: {}", result); } { // KEYS my* Set<String> keys = stringRedisTemplate.keys("my*"); LOGGER.info("cmd: KEYS my*, result: {}", JsonUtils.writeValueAsString(keys, true)); } { // EXISTS mykey Boolean result = stringRedisTemplate.hasKey("mykey"); LOGGER.info("cmd: EXISTS mykey, result: {}", result); } { // DEL mykey Boolean result = stringRedisTemplate.delete("mykey"); LOGGER.info("cmd: DEL mykey, result: {}", result); } { // GET mykey String result = stringRedisTemplate.opsForValue().get("mykey"); LOGGER.info("cmd: GET mykey, result: {}", result); } }
-
opsForValue
: 获取Binary-safe strings
的操作类ValueOperations
( 即spring对redis操作的一个封装类. 同样地, 对hash
、set
等也有其对应的封装HashOperations
、SetOperations
等).
[版权声明]
本文发布于朴瑞卿的博客, 允许非商业用途转载, 但转载必须保留原作者朴瑞卿 及链接: https://blog.piaoruiqing.com .
如有授权方面的协商或合作, 请联系邮箱: [email protected] .
进阶
划分应用缓存
不同应用的缓存可以简单地通过key的 前缀 来划分
让我们来思考这样一个问题, 如果我们想要对不同应用(服务)的缓存进行划分, 以便于管理和维护, 应该如何实现?
或许增加前缀是一个不错的想法, 但如果每次编码中都需要将前缀 prefix
拼接到key中, 一方面增加了工作量, 另一份面也增加了出错的风险, 如果忘记拼接了怎么办. 对, 也许你也想到了, 前文提到 RedisSerializer
是 spring-data-redis
对象到二进制数组序列化和反序列化接口, 用来序列化和反序列化key和value, 我们可以从这里做文章:
public interface RedisSerializer<T> { /** * Serialize the given object to binary data. * * @param t object to serialize. Can be {@literal null}. * @return the equivalent binary data. Can be {@literal null}. */ @Nullable byte[] serialize(@Nullable T t) throws SerializationException; /** * Deserialize an object from the given binary data. * * @param bytes object binary representation. Can be {@literal null}. * @return the equivalent object instance. Can be {@literal null}. */ @Nullable T deserialize(@Nullable byte[] bytes) throws SerializationException; }
-
serialize
: 对象 -> byte数组, 当调用RedisTemplate
"存"相关的方法时, 会用到这个方法, 将存入的对象转化为字节数组, 然后存储到redis. -
deserialize
: byte数组 -> 对象, 当调用RedisTemplate
"取"相关的方法时, 会用到这个方法, 将从redis取出的数据反序列化为对象.
既然存取都和 RedisSerializer
有必然的联系, 那么可以通过实现该接口并指定 RedisTemplate
的 KeySerializer
来实现增加前缀的功能. 如此一来, 增加前缀的操作就从业务中剥离出来, 对于调用方来说, 完全是透明的, 还算优雅, 具体实现如下:
/** * generic redis key serializer * @author piaoruiqing * @date: 2019-06-11 22:37 */ public class GenericRedisKeySerializer implements RedisSerializer<Object> { private final Charset charset; private String prefix; private int index; public GenericRedisKeySerializer(String prefix) { this(prefix, StandardCharsets.UTF_8); } public GenericRedisKeySerializer(String prefix, Charset charset) { Assert.notNull(charset); Assert.notNull(prefix); this.charset = charset; this.prefix = prefix + ":"; index = this.prefix.length(); } @Override public String deserialize(byte[] bytes) { if (null == bytes) { return null; } String key = new String(bytes, charset); if (key.indexOf(prefix) == 0) { return key.substring(index, key.length()); } return key; } @Override public byte[] serialize(Object key) { if (null == key) { return null; } String string = key.toString(); if (!string.startsWith(prefix)) { string = prefix + string; } return string.getBytes(charset); } }
将前文的 StringRedisTemplate
稍作修改:
@Value("${spring.application.name:undefined}") private String applicationName; /** * StringRedisTemplate * @param factory * @return */ @Bean public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) { StringRedisTemplate template = new StringRedisTemplate(factory); // StringRedisSerializer serializer = new StringRedisSerializer(); GenericRedisKeySerializer serializer = new GenericRedisKeySerializer(applicationName); template.setKeySerializer(serializer); template.setHashKeySerializer(serializer); template.setValueSerializer(serializer); template.setHashValueSerializer(serializer); return template; }
-
StringRedisSerializer
替换为自定义的GenericRedisKeySerializer
并指定前缀为应用名
体验一下:
stringRedisTemplate.opsForValue().set("mykey", "myvalue"); String result = stringRedisTemplate.opsForValue().get("mykey"); // "myvalue"
连接到redis查看key, 已经带有前缀了
root@ubuntu:/home/ubuntu# docker exec -it redis redis-cli 127.0.0.1:6379> keys * 1) "redis-simple:mykey"
自定义序列化
RedisTemplate
默认使用JDK序列化 JdkSerializationRedisSerializer
, 我们可以指定使用其他方式的序列化, 比如JSON、protostuff
前文已经描述了如何自定义key的序列化方式, value的序列化配置与其相同, 都是实现 RedisSerializer
并在创建 RedisTemplate
时指定, 就不重复贴代码了.
常用的序列化方式有几种:
JDK JSON Protostuff
结语
本文针对redis讲解了redis java客户端的使用、与spring集成以及进阶使用, 后续将针对Redis的其他使用技巧进行讲解, 敬请关注.
参考文献
- https://redis.io
- https://spring.io/projects/spring-data-redis
- https://github.com/xetorthio/jedis
- https://lettuce.io/
© 2019,朴瑞卿. 版权所有.
0
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK