Redis 从入门到精通【应用篇】之RedisTemplate详解
0. 前言
在SpringBoot中,可以使用RedisTemplate来操作Redis数据库。RedisTemplate是Spring Data Redis提供的一个强大的Redis客户端,它支持各种Redis数据结构,并提供了许多方便的方法来操作这些数据结构。下面是一些RedisTemplate的用法示例:
1. RedisTemplate 方法
1. 设置RedisTemplate的序列化方式
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 设置key和value的序列化方式
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
// 设置hash key和value的序列化方式
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
return redisTemplate;
}
}
在此示例中,创建了一个RedisTemplate对象,并设置了key和value的序列化方式为StringRedisSerializer和Jackson2JsonRedisSerializer。同时,还设置了hash key和value的序列化方式。
2. RedisTemplate的基本操作
其实在项目中我们通常会将RedisTemplate 再封装一层,作为一个Redis操作类处理,相当于提供了一层语法糖。
@Service
public class RedisService {
// RedisTemplate是Spring提供的对Redis的操作模板类,使用泛型限定key和value的类型为String和Object
private final RedisTemplate<String, Object> redisTemplate;
// 构造函数,注入RedisTemplate对象
public RedisService(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
// 设置key-value键值对
public void set(String key, Object value) {
redisTemplate.opsForValue().set(key, value);
}
// 根据key获取value
public Object get(String key) {
return redisTemplate.opsForValue().get(key);
}
// 根据key删除键值对
public void delete(String key) {
redisTemplate.delete(key);
}
// 判断key是否存在
public boolean exists(String key) {
return redisTemplate.hasKey(key);
}
// 将key对应的value增加delta
public long increment(String key, long delta) {
return redisTemplate.opsForValue().increment(key, delta);
}
// 获取hash结构中所有键值对
public Map<Object, Object> hashGetAll(String key) {
return redisTemplate.opsForHash().entries(key);
}
// 向hash结构中添加键值对
public void hashPut(String key, Object hashKey, Object value) {
redisTemplate.opsForHash().put(key, hashKey, value);
}
// 根据hash结构中的key获取对应的value
public Object hashGet(String key, Object hashKey) {
return redisTemplate.opsForHash().get(key, hashKey);
}
// 根据hash结构中的key删除对应的hashKey
public void hashDelete(String key, Object... hashKeys) {
redisTemplate.opsForHash().delete(key, hashKeys);
}
// 判断hash结构中是否存在hashKey
public boolean hashExists(String key, Object hashKey) {
return redisTemplate.opsForHash().hasKey(key, hashKey);
}
// 获取set结构中所有元素
public Set<Object> setGetAll(String key) {
return redisTemplate.opsForSet().members(key);
}
// 向set结构中添加元素
public void setAdd(String key, Object... values) {
redisTemplate.opsForSet().add(key, values);
}
// 判断set结构中是否存在某个元素
public boolean setExists(String key, Object value) {
return redisTemplate.opsForSet().isMember(key, value);
}
// 根据value删除set结构中的元素
public void setDelete(String key, Object... values) {
redisTemplate.opsForSet().remove(key, values);
}
// 获取list结构中所有元素
public List<Object> listGetAll(String key) {
return redisTemplate.opsForList().range(key, 0, -1);
}
// 向list结构中左侧插入元素
public void listPush(String key, Object value) {
redisTemplate.opsForList().leftPush(key, value);
}
// 从list结构中左侧弹出元素
public Object listPop(String key) {
return redisTemplate.opsForList().leftPop(key);
}
// 获取list结构中元素的数量
public long listSize(String key) {
return redisTemplate.opsForList().size(key);
}
}
2. 源码浅析
2.1. 构造方法
RedisTemplate的构造方法需要一个RedisConnectionFactory对象作为参数,它通过这个对象来获取Redis连接。RedisConnectionFactory 是 Spring Data Redis 提供的一个接口,用于创建和管理 Redis 连接。它是将 Redis 连接池(连接 Redis 数据库的客户端)与 Spring 应用程序集成的关键。
public RedisTemplate() {
RedisConnectionFactory redisConnectionFactory = RedisConnectionConfiguration.determineConnectionFactory(redisSentinelConfiguration, redisClusterConfiguration, connectionFactory, jedisConnectionFactory);
setConnectionFactory(redisConnectionFactory);
afterPropertiesSet();
}
2.2. 序列化方式
RedisTemplate支持各种数据类型的序列化和反序列化,它提供了以下四种序列化方式:
- keySerializer:key的序列化方式。
- valueSerializer:value的序列化方式。
- hashKeySerializer:hash key的序列化方式。
- hashValueSerializer:hash value的序列化方式。
RedisTemplate默认使用JdkSerializationRedisSerializer作为序列化方式,但是在实际使用中,通常需要根据实际情况选择更加高效的序列化方式,如StringRedisSerializer、Jackson2JsonRedisSerializer等。
public void setKeySerializer(RedisSerializer<?> keySerializer) {
Assert.notNull(keySerializer, "RedisSerializer must not be null!");
this.keySerializer = keySerializer;
}
public void setValueSerializer(RedisSerializer<?> valueSerializer) {
Assert.notNull(valueSerializer, "RedisSerializer must not be null!");
this.valueSerializer = valueSerializer;
}
public void setHashKeySerializer(RedisSerializer<?> hashKeySerializer) {
Assert.notNull(hashKeySerializer, "RedisSerializer must not be null!");
this.hashKeySerializer = hashKeySerializer;
}
public void setHashValueSerializer(RedisSerializer<?> hashValueSerializer) {
Assert.notNull(hashValueSerializer, "RedisSerializer must not be null!");
this.hashValueSerializer = hashValueSerializer;
}
2.3. RedisTemplate的操作方法
RedisTemplate提供了各种操作方法,如opsForValue()、opsForList()、opsForSet()、opsForZSet()、opsForHash()等,它们返回的是具体数据结构的操作对象,如ValueOperations、ListOperations、SetOperations、ZSetOperations、HashOperations等。这些操作对象提供了各种操作方法,如get()、set()、push()、pop()、add()、remove()、score()、range()、increment()等,它们对应了Redis的各种操作。
public ValueOperations<K, V> opsForValue() {
if (valueOps == null) {
valueOps = new DefaultValueOperations<>(this);
}
return valueOps;
}
public ListOperations<K, V> opsForList() {
if (listOps == null) {
listOps = new DefaultListOperations<>(this);
}
return listOps;
}
public SetOperations<K, V> opsForSet() {
if (setOps == null) {
setOps = new DefaultSetOperations<>(this);
}
return setOps;
}
public ZSetOperations<K, V> opsForZSet() {
if (zSetOps == null) {
zSetOps = new DefaultZSetOperations<>(this);
}
return zSetOps;
}
public HashOperations<K, HK, HV> opsForHash() {
if (hashOps == null) {
hashOps = new DefaultHashOperations<>(this);
}
return hashOps;
}
2.4. RedisTemplate的事务
RedisTemplate支持事务,它提供了multi()、exec()和discard()三个方法来实现事务。multi()方法用于开启事务,exec()方法用于提交事务,discard()方法用于回滚事务。
public void multi() {
RedisConnectionUtils.bindConnection(getRequiredConnectionFactory(), true);
try {
RedisConnectionUtils.getRequiredConnection(getConnectionFactory()).multi();
} catch (RuntimeException ex) {
RedisConnectionUtils.unbindConnection(getRequiredConnectionFactory());
throw ex;
}
}
public List<Object> exec() {
RedisConnectionUtils.unbindConnectionIfPossible(getConnectionFactory());
return execute((RedisCallback<List<Object>>) connection -> {
List<Object> results = connection.exec();
return results != null ? results : Collections.emptyList();
}, true);
}
public void discard() {
RedisConnectionUtils.unbindConnectionIfPossible(getConnectionFactory());
execute(RedisConnectionUtils::discard, true);
}
2.5. RedisTemplate的执行方法
RedisTemplate提供了execute()方法来执行Redis操作,它需要传入一个RedisCallback对象作为参数,RedisCallback是一个函数式接口,它定义了一个回调函数,用于执行具体的Redis操作。execute()方法会获取一个Redis连接,执行RedisCallback对象的回调函数,并返回回调函数的结果。
public <T> T execute(RedisCallback<T> action, boolean exposeConnection) {
Assert.notNull(action, "Callback object must not be null");
RedisConnection conn = null;
try {
conn = getConnection(exposeConnection);
boolean existingConnection = TransactionSynchronizationManager.hasResource(getConnectionFactory());
RedisConnection connToUse = preProcessConnection(conn, existingConnection);
T result = action.doInRedis(connToUse);
return postProcessResult(result, conn, existingConnection);
} catch (RuntimeException ex) {
releaseConnection(conn, existingConnection);
throw ex;
} finally {
if (!exposeConnection) {
RedisConnectionUtils.releaseConnection(conn, getConnectionFactory(), false);
}
}
}
在execute()方法中,首先获取Redis连接,然后调用preProcessConnection()方法进行预处理,接着执行RedisCallback对象的回调函数,最后调用postProcessResult()方法进行后处理,并返回结果。如果执行过程中发生异常,会调用releaseConnection()方法释放Redis连接。
2.6. RedisTemplate的回调方法
RedisTemplate的回调方法主要有以下三个:
这里有三个方法:
-
execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline)
:执行Redis操作的核心方法。接受一个RedisCallback对象,该对象封装了要在Redis上执行的操作。还有两个布尔类型的参数,一个表示是否暴露连接(exposeConnection),另一个表示是否启用pipeline(pipeline)。如果启用pipeline,将使用Redis连接对象的openPipeline()和closePipeline()方法执行操作。 -
execute(SessionCallback<T> session)
:执行Redis事务的方法。接受一个SessionCallback对象,该对象封装了在 Redis 事务中执行的操作。该方法会绑定 Redis 连接并执行 SessionCallback 对象的 execute() 方法,最后解除绑定。 -
executePipelined(SessionCallback<?> session, @Nullable RedisSerializer<?> resultSerializer)
:执行 Redis pipeline 的方法。接受一个SessionCallback对象,该对象封装了要在 Redis pipeline 中执行的操作,以及一个可选的 RedisSerializer 对象,用于反序列化结果。该方法会绑定 Redis 连接并执行 SessionCallback 对象的 execute() 方法,在执行期间使用 Redis 连接对象的 openPipeline() 和 closePipeline() 方法开启和关闭 Redis pipeline。
常用场景:第一个方法执行单个Redis操作,第二个方法执行Redis事务,第三个方法执行Redis
pipeline。此外,第二个方法是为了执行多个 Redis 操作而设计的,而第一个方法和第三个方法只执行单个 Redis 操作。第三个方法需要额外的参数,用于反序列化 Redis pipeline 的结果。
@Nullable
public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) {
Assert.isTrue(this.initialized, "template not initialized; call afterPropertiesSet() before using it");
Assert.notNull(action, "Callback object must not be null");
RedisConnectionFactory factory = this.getRequiredConnectionFactory();
RedisConnection conn = RedisConnectionUtils.getConnection(factory, this.enableTransactionSupport);
Object var11;
try {
boolean existingConnection = TransactionSynchronizationManager.hasResource(factory);
RedisConnection connToUse = this.preProcessConnection(conn, existingConnection);
boolean pipelineStatus = connToUse.isPipelined();
if (pipeline && !pipelineStatus) {
connToUse.openPipeline();
}
RedisConnection connToExpose = exposeConnection ? connToUse : this.createRedisConnectionProxy(connToUse);
T result = action.doInRedis(connToExpose);
if (pipeline && !pipelineStatus) {
connToUse.closePipeline();
}
var11 = this.postProcessResult(result, connToUse, existingConnection);
} finally {
RedisConnectionUtils.releaseConnection(conn, factory, this.enableTransactionSupport);
}
return var11;
}
public <T> T execute(SessionCallback<T> session) {
Assert.isTrue(this.initialized, "template not initialized; call afterPropertiesSet() before using it");
Assert.notNull(session, "Callback object must not be null");
RedisConnectionFactory factory = this.getRequiredConnectionFactory();
RedisConnectionUtils.bindConnection(factory, this.enableTransactionSupport);
Object var3;
try {
var3 = session.execute(this);
} finally {
RedisConnectionUtils.unbindConnection(factory);
}
return var3;
}
public List<Object> executePipelined(SessionCallback<?> session) {
return this.executePipelined(session, this.valueSerializer);
}
public List<Object> executePipelined(SessionCallback<?> session, @Nullable RedisSerializer<?> resultSerializer) {
Assert.isTrue(this.initialized, "template not initialized; call afterPropertiesSet() before using it");
Assert.notNull(session, "Callback object must not be null");
RedisConnectionFactory factory = this.getRequiredConnectionFactory();
RedisConnectionUtils.bindConnection(factory, this.enableTransactionSupport);
List var4;
try {
var4 = (List)this.execute((connection) -> {
connection.openPipeline();
boolean pipelinedClosed = false;
List var7;
try {
Object result = this.executeSession(session);
if (result != null) {
throw new InvalidDataAccessApiUsageException("Callback cannot return a non-null value as it gets overwritten by the pipeline");
}
List<Object> closePipeline = connection.closePipeline();
pipelinedClosed = true;
var7 = this.deserializeMixedResults(closePipeline, resultSerializer, this.hashKeySerializer, this.hashValueSerializer);
} finally {
if (!pipelinedClosed) {
connection.closePipeline();
}
}
return var7;
});
} finally {
RedisConnectionUtils.unbindConnection(factory);
}
return var4;
}
代码示例
- 使用
execute(RedisCallback<T> action)
执行 Redis 命令
// 使用 execute() 方法执行 Redis 命令
String key = "myKey";
String value = redisTemplate.execute((RedisCallback<String>) connection -> {
connection.set(redisTemplate.getStringSerializer().serialize(key), redisTemplate.getStringSerializer().serialize("Hello"));
return redisTemplate.getStringSerializer().deserialize(connection.get(redisTemplate.getStringSerializer().serialize(key)));
});
// 输出 Redis 命令执行结果
System.out.println(value); // 输出 "Hello"
在上面的示例中, 我们使用 execute(RedisCallback<T> action)
方法将 Redis 命令封装在一个 RedisCallback
对象中,并将其传递给 execute()
方法。该命令使用 set()
方法将一个 key-value 对写入 Redis 中,然后使用 get()
方法从 Redis 中读取该 key 对应的值。
- 使用
execute(SessionCallback<T> session)
执行 Redis 事务
// 使用 execute(SessionCallback<T> session) 方法执行 Redis 事务
String key1 = "myKey1";
String key2 = "myKey2";
String value1 = "myValue1";
String value2 = "myValue2";
List<Object> results = redisTemplate.execute((SessionCallback<List<Object>>) session -> {
session.multi();
session.opsForValue().set(key1, value1);
session.opsForValue().set(key2, value2);
return session.exec();
});
// 输出 Redis 事务的结果
System.out.println(results); // 输出 "[true, true]"
使用 execute(SessionCallback<T> session)
方法将 Redis 事务封装在一个 SessionCallback<T>
对象中,并将其传递给 execute()
方法。该事务使用 multi()
方法开启事务,在事务中使用 opsForValue()
对象的 set()
方法将两个 key-value 对写入 Redis 中,最后使用 exec()
方法提交事务。事务执行完成后,我们可以通过 execute()
方法返回的结果列表查看每个 Redis 命令的执行结果。在上面的示例中,我们可以看到结果列表为 [true, true]
,表示两个 Redis 命令都成功执行。
- 使用
executePipelined(SessionCallback<?> session)
执行 Redis pipeline
// 使用 executePipelined(SessionCallback<?> session) 方法执行 Redis pipeline
String key1 = "myKey1";
String key2 = "myKey2";
List<Object> results = redisTemplate.executePipelined((SessionCallback<List<Object>>) session -> {
session.opsForValue().get(key1);
session.opsForValue().get(key2);
return null;
});
// 输出 Redis pipeline 的结果
System.out.println(results); // 输出 "[Hello1, Hello2]"
使用 executePipelined(SessionCallback<?> session)
方法将 Redis pipeline 封装在一个 SessionCallback<?>
对象中,并将其传递给 executePipelined()
方法。该 pipeline 使用 opsForValue()
对象的 get()
方法获取两个 key 的值,并返回一个结果列表。在执行期间,该方法将使用 Redis 连接对象的 openPipeline()
和 closePipeline()
方法开启和关闭 Redis pipeline,以便可以批量执行多个命令。
3.总结
看完这些基本上行只是学会了增删查看和批量操作。也就是只学会了RedisTemplate 的皮毛。其实在项目中还有更多的复杂需求,需要重新实现。
比如以下这些问题,也是很常见的,需要我们处理实际的问题。我大概做一个简答,后面将详细输出示例
3.1. 项目中如何使用 RedisTemplate 支持多个 Redis 数据库?
可以通过配置 RedisConnectionFactory 来支持多个 Redis 数据库,其中可以使用 JedisConnectionFactory 或 LettuceConnectionFactory 来创建不同的 RedisConnectionFactory 实例。详细教程可以参考我的其他博客
《SpringBoot 项目配置多数据源》
3.2. 如何使用 RedisTemplate 支持 Redis 集群?
可以使用 RedisTemplate 的 ClusterRedisConnectionFactory 来支持 Redis 集群,通过配置 Redis 集群中的多个节点来实现高可用性和负载均衡。详细教程参考我的其他博客
《SpringBoot 项目配置 Redis 集群》
3.3. 如何使用 RedisTemplate 实现 Redis 事务的乐观锁?
可以使用 RedisTemplate 的 watch() 方法和 multi() 方法来实现 Redis 事务的乐观锁,通过在事务开始前使用 watch() 方法监视 Redis 中的某个 key,然后在事务中使用 multi() 方法执行多个 Redis 命令,并使用 exec() 方法提交事务,如果在事务执行期间,被监视的 key 被修改,则事务会失败,从而实现乐观锁。详细教程可以参考我的其他博客
《SpringBoot 项目配置 Redis 集群》
3.4. 如何使用 RedisTemplate 实现 Redis 的分布式锁重入?
可以使用 RedisTemplate 的 ThreadLocal 方式来实现 Redis 的分布式锁重入,即在每个线程中保存一个 Redis 分布式锁的状态,并在需要重入时,检查当前线程是否已经获取了分布式锁。
3.5. 如何使用 RedisTemplate 实现 Redis 的分布式事务?
可以使用 RedisTemplate 的 execute(SessionCallback session) 方法来实现 Redis 的分布式事务,其中 SessionCallback 接口可以用来执行多个 Redis 命令,并保证这些命令以原子方式执行。
3.6. 如何使用 RedisTemplate 实现 Redis 的分布式限速?
可以使用 RedisTemplate 的 incr() 方法和 expire() 方法来实现 Redis 的分布式限速,通过在 Redis 中设置一个计数器和过期时间来实现分布式限速。
3.7. 如何使用 RedisTemplate 实现 Redis 的分布式锁可重入性?
可以使用 RedisTemplate 的 ReentrantRedisLock 类来实现 Redis 的分布式锁可重入性,该类可以在 Redis 中保存一个计数器来记录锁的重入次数,并在释放锁时,检查当前线程是否已经完全释放了锁,从而实现分布式锁的可重入性。
3.8. 如何使用 RedisTemplate 实现 Redis 的分布式信号量?
可以使用 RedisTemplate 的 RedisSemaphore 类来实现 Redis 的分布式信号量,该类可以在 Redis 中保存一个计数器来记录当前已经获得信号量的数量,并在释放信号量时,将计数器减一。
3.9. 如何使用 RedisTemplate 实现 Redis 的分布式缓存穿透?
可以使用 RedisTemplate 的缓存注解(例如 @Cacheable、@CachePut、@CacheEvict)和布隆过滤器来实现 Redis 的分布式缓存穿透,其中布隆过滤器可以用来过滤掉不存在的 key,从而避免缓存穿透的问题。文章来源:https://www.toymoban.com/news/detail-602371.html
3.10. 如何使用 RedisTemplate 实现 Redis 的分布式缓存击穿?
可以使用 RedisTemplate 的缓存注解(例如 @Cacheable、@CachePut、@CacheEvict)和 Redis 分布式锁来实现 Redis 的分布式缓存击穿,其中 Redis 分布式锁可以用来防止缓存击穿,即在缓存不存在的情况下,使用 Redis 分布式锁来避免多个线程同时访问数据库。文章来源地址https://www.toymoban.com/news/detail-602371.html
4. Redis从入门到精通系列文章
- 《Redis【应用篇】之RedisTemplate基本操作》
- 《Redis 从入门到精通【实践篇】之SpringBoot配置Redis多数据源》
- 《Redis 从入门到精通【进阶篇】之三分钟了解Redis HyperLogLog 数据结构》
- 《Redis 从入门到精通【进阶篇】之三分钟了解Redis地理位置数据结构GeoHash》
- 《Redis 从入门到精通【进阶篇】之高可用哨兵机制(Redis Sentinel)详解》
- 《Redis 从入门到精通【进阶篇】之redis主从复制详解》
- 《Redis 从入门到精通【进阶篇】之Redis事务详解》
- 《Redis从入门到精通【进阶篇】之对象机制详解》
- 《Redis从入门到精通【进阶篇】之消息传递发布订阅模式详解》
- 《Redis从入门到精通【进阶篇】之持久化 AOF详解》
- 《Redis从入门到精通【进阶篇】之持久化RDB详解》
- 《Redis从入门到精通【高阶篇】之底层数据结构字典(Dictionary)详解》
- 《Redis从入门到精通【高阶篇】之底层数据结构快表QuickList详解》
- 《Redis从入门到精通【高阶篇】之底层数据结构简单动态字符串(SDS)详解》
- 《Redis从入门到精通【高阶篇】之底层数据结构压缩列表(ZipList)详解》
-
《Redis从入门到精通【进阶篇】之数据类型Stream详解和使用示例》
大家好,我是冰点,今天的Redis【实践篇】之RedisTemplate基本操作详解,全部内容就是这些。如果你有疑问或见解可以在评论区留言。
到了这里,关于Redis【实践篇】之RedisTemplate基本操作的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!