Redis之使用教程(Java版)
关键词
Jedis
: redis java客户端实现.
Lettuce
: redis java客户端实现, 基于netty.
spring-data-redis
: Spring针对redis的封装, 配置简单, 提供了与Redis存储交互的抽象封装, 十分优雅, 也极具扩展性. 可集成Jedis、Lettuce等redis客户端. springboot2.0后官方默认集成的客户端从Jedis改为Lettuce.
前言 本文将针对使用Java集成Redis进行讲解, Jedis
及Lettuce
的使用仅作简单描述, spring
的redis
集成及使用将作为主要讲解内容.
Jedis
https://github.com/xetorthio/jedis
引入依赖:
1 2 3 4 5 <dependency > <groupId > redis.clients</groupId > <artifactId > jedis</artifactId > <version > 2.9.3</version > </dependency >
Jedis
是对redis命令的封装, 使用上基本与redis-cli
无异, 操作string的使用示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 private static final JedisPool POOL = new JedisPool(new JedisPoolConfig(), "test-redis-server" , 6379 );try (Jedis jedis = POOL.getResource()){ { String result = jedis.set("mykey" , "myvalue" ); LOGGER.info("cmd: SET mykey myvalue, result: {}" , result); } { String result = jedis.get("mykey" ); LOGGER.info("cmd: GET mykey, result: {}" , result); } { Set<String> keys = jedis.keys("my*" ); LOGGER.info("cmd: KEYS my*, result: {}" , JsonUtils.writeValueAsString(keys, true )); } { Boolean result = jedis.exists("mykey" ); LOGGER.info("cmd: EXISTS mykey, result: {}" , result); } { Long result = jedis.del("mykey" ); LOGGER.info("cmd: DEL mykey, result: {}" , result); } { 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
https://lettuce.io/
引入依赖:
1 2 3 4 5 <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的使用示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 private static final RedisClient CLIENT = RedisClient.create("redis://@test-redis-server:6379/0" );try (StatefulRedisConnection<String, String> connection = CLIENT.connect()) { RedisCommands<String, String> commands = connection.sync(); { String result = commands.set("mykey" , "myvalue" ); LOGGER.info("cmd: SET mykey myvalue, result: {}" , result); } { String result = commands.get("mykey" ); LOGGER.info("cmd: GET mykey, result: {}" , result); } { List<String> keys = commands.keys("my*" ); LOGGER.info("cmd: KEYS my*, result: {}" , JsonUtils.writeValueAsString(keys, true )); } { Long result = commands.exists("mykey" ); LOGGER.info("cmd: EXISTS mykey, result: {}" , result); } { Long result = commands.del("mykey" ); LOGGER.info("cmd: DEL mykey, result: {}" , result); } { String result = commands.get("mykey" ); LOGGER.info("cmd: GET mykey, result: {}" , result); } }
Spring 集成
spring-data-redis
是Spring Data
家族的一部分, 提供了简单的配置以轻松访问redis, 针对存储操作提供了低级别和高级别的抽象, 将开发人员从基础实现中解放出来.
引入依赖:
1 2 3 4 5 <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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @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相关配置:
1 2 3 4 spring: redis: host: test-redis-server port: 6379
准备工作完成了, 现在就来体验一下, 同样地与前文对应, 操作string的使用示例如下: :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @Resource private StringRedisTemplate stringRedisTemplate;@Test public void testStringRedisTemplateSimple () { { stringRedisTemplate.opsForValue().set("mykey" , "myvalue" ); } { String result = stringRedisTemplate.opsForValue().get("mykey" ); LOGGER.info("cmd: GET mykey, result: {}" , result); } { Set<String> keys = stringRedisTemplate.keys("my*" ); LOGGER.info("cmd: KEYS my*, result: {}" , JsonUtils.writeValueAsString(keys, true )); } { Boolean result = stringRedisTemplate.hasKey("mykey" ); LOGGER.info("cmd: EXISTS mykey, result: {}" , result); } { Boolean result = stringRedisTemplate.delete("mykey" ); LOGGER.info("cmd: DEL mykey, result: {}" , result); } { String result = stringRedisTemplate.opsForValue().get("mykey" ); LOGGER.info("cmd: GET mykey, result: {}" , result); } }
opsForValue
: 获取Binary-safe strings
的操作类ValueOperations
( 即spring对redis操作的一个封装类. 同样地, 对hash
、set
等也有其对应的封装HashOperations
、SetOperations
等).
进阶 划分应用缓存
不同应用的缓存可以简单地通过key的前缀 来划分
让我们来思考这样一个问题, 如果我们想要对不同应用(服务)的缓存进行划分, 以便于管理和维护, 应该如何实现?
或许增加前缀是一个不错的想法, 但如果每次编码中都需要将前缀prefix
拼接到key中, 一方面增加了工作量, 另一份面也增加了出错的风险, 如果忘记拼接了怎么办. 对, 也许你也想到了, 前文提到RedisSerializer
是spring-data-redis
对象到二进制数组序列化和反序列化接口, 用来序列化和反序列化key和value, 我们可以从这里做文章:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public interface RedisSerializer <T > { @Nullable byte [] serialize(@Nullable T t) throws SerializationException; @Nullable T deserialize (@Nullable byte [] bytes) throws SerializationException ; }
serialize
: 对象 -> byte数组.
deserialize
: byte数组 -> 对象.
RedisTemplate
对redis的操作和RedisSerializer
都有必然的联系, 那么可以通过实现该接口并指定RedisTemplate
的KeySerializer
来实现增加前缀的功能. 如此一来, 增加前缀的操作就从业务中剥离出来, 对于调用方来说, 完全是透明的, 还算优雅, 具体实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 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
稍作修改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Value ("${spring.application.name:undefined}" )private String applicationName;@Bean public StringRedisTemplate stringRedisTemplate (RedisConnectionFactory factory) { StringRedisTemplate template = new StringRedisTemplate(factory); GenericRedisKeySerializer serializer = new GenericRedisKeySerializer(applicationName); template.setKeySerializer(serializer); template.setHashKeySerializer(serializer); template.setValueSerializer(serializer); template.setHashValueSerializer(serializer); return template; }
StringRedisSerializer
替换为自定义的GenericRedisKeySerializer
并指定前缀为应用名
体验一下:
1 2 stringRedisTemplate.opsForValue().set("mykey" , "myvalue" ); String result = stringRedisTemplate.opsForValue().get("mykey" );
连接到redis查看key, 已经带有前缀了
1 2 3 root@ubuntu:/home/ubuntu 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的其他使用技巧进行讲解, 敬请关注.
参考文献
欢迎关注公众号(代码如诗):