0、导入 maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
</dependency>
1、SDR说明
Spring Data Redis(SDR),是SpringFramework提供的一套简化访问Redis的API,是对Jedis的又一层封装。
SDR集成了Jedis,JRedis,SRP,Lettuce这四种开源的Redis Connector,这些Connector都是针对于Redis的开源Java库。
其中,JRedis和SRP从spring-data-redis1.7开始,就不支持了。
2、RedisTemplate说明
SDR支持底层次的通过连接器connector连接到Redis,支持高层次的友好的模板类RedisTemplate,RedisTemplate是建立在低级别的connection基础之上。
RedisConnection接收或返回字节数组需要自身处理连接,比如关闭连接,而RedisTemplate负责处理串行化和反串行化,并且对连接进行管理。
RedisTemplate是线程安全的,能够用于多个实例中。
RedisTemplate是SDR的一个核心Helper类,该类是一个高级的抽象(对Jedis的又一层封装),它封装了对Redis中的数据的CRUD操作,包含“高级封装”。
---????-->
RedisTemplate默认选择java-based串行化,也可以切换为其它的串行化方式,
或者设置enabledDefaultSerializer为false或者设置串行化器为null,则RedisTemplate用raw byte arrays表示数据。
(1)高级封装(推荐使用)
高级封装的操作包含:OpsForValue(),OpsForList(),OpsForSet(),OpsForZset(),OpsForHash()等等。
SDR官方文档中对Redistemplate的介绍:
the template is in fact the central class of the Redis module due to its rich feature set. The template offers a high-level abstraction for Redis interactions.
通过Redistemplate可以调用ValueOperations和ListOperations等等方法,分别是对Redis命令的高级封装。
但是ValueOperations等等这些命令最终是要转化成为RedisCallback来执行的。
也就是说通过使用RedisCallback可以实现更强的功能,
SDR文档对RedisCallback的介绍:
RedisTemplate and StringRedisTemplate allow the developer to talk directly to Redis through the RedisCallback interface.
This gives complete control to the developer as it talks directly to the RedisConnection。
(2)从高级封装获得低级封装的过渡:
RedisOperations<String, Object> operations = opsForValue.getOperations();
3、RedisConnection提供了“低级封装”。
低级封装的操作是通过连接到Redis的Connection对象,直接对Redis数据进行操作。
低级封装的核心是:
redisTemplate.execute(
new RedisCallback(){
// .....
}
)
=========== redis 源码头=============================
RedisTemplate的使用
这个类作为一个模版类,提供了很多快速使用redis的api,而不需要自己来维护连接,事务。
public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V> { private boolean enableTransactionSupport = false;
private boolean exposeConnection = false;
private boolean initialized = false;
private boolean enableDefaultSerializer = true;
private RedisSerializer<?> defaultSerializer = new JdkSerializationRedisSerializer(); private RedisSerializer keySerializer = null;
private RedisSerializer valueSerializer = null;
private RedisSerializer hashKeySerializer = null;
private RedisSerializer hashValueSerializer = null;
private RedisSerializer<String> stringSerializer = new StringRedisSerializer(); private ScriptExecutor<K> scriptExecutor; // cache singleton objects (where possible)
private ValueOperations<K, V> valueOps;
private ListOperations<K, V> listOps;
private SetOperations<K, V> setOps;
private ZSetOperations<K, V> zSetOps;
================== 实战 ======================
package cn.test.config.data; import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig; import java.util.HashSet;
import java.util.Set; /**
* Created by Miraculous on 15/7/4.
*/
@Configuration
public class RedisConfig { @Value("${test.data.redis.defaultExpiration}")
private Long defaultExpiration;
@Value("${test.redis.master.host}")
private String masterHost;
@Value("${test.redis.master.port}")
private int masterPort;
@Value("${test.redis.master.name}")
private String masterName;
@Value("${test.redis.sentinel1.host}")
private String sentinel1Host;
@Value("${test.redis.sentinel1.port}")
private int sentinel1port;
@Value("${test.redis.sentinel2.host}")
private String sentinel2Host;
@Value("${test.redis.sentinel2.port}")
private int sentinel2port;
@Value("${test.redis.sentinel3.host}")
private String sentinel3Host;
@Value("${test.redis.sentinel3.port}")
private int sentinel3port; /**
连接工厂
*/
private RedisConnectionFactory generateDevConnectionFactory() {
JedisConnectionFactory factory = new JedisConnectionFactory();
factory.setHostName(masterHost);
factory.setPort(masterPort);
factory.setUsePool(true);
factory.setConvertPipelineAndTxResults(true);
JedisPoolConfig poolConfig = generatePoolConfig();
factory.setPoolConfig(poolConfig); // 注入连接池
factory.afterPropertiesSet();
return factory;
} private RedisConnectionFactory generateReleaseConnectionFactory() {
RedisSentinelConfiguration sentinelConfiguration = new RedisSentinelConfiguration();
RedisNode master = new RedisNode(masterHost, masterPort);
master.setName(masterName);
Set<RedisNode> sentinels = new HashSet<>();
RedisNode sentinel1 = new RedisNode(sentinel1Host, sentinel1port);
RedisNode sentinel2 = new RedisNode(sentinel2Host, sentinel2port);
RedisNode sentinel3 = new RedisNode(sentinel3Host, sentinel3port);
sentinels.add(sentinel1);
sentinels.add(sentinel2);
sentinels.add(sentinel3);
sentinelConfiguration.setMaster(master);
sentinelConfiguration.setSentinels(sentinels);
JedisPoolConfig poolConfig = generatePoolConfig();
JedisConnectionFactory factory = new JedisConnectionFactory(sentinelConfiguration, poolConfig);
factory.setHostName(masterHost);
factory.setPort(masterPort);
factory.setTimeout(10000);
factory.setUsePool(true);
factory.setConvertPipelineAndTxResults(true);
factory.afterPropertiesSet();
return factory;
} /**redis连接池基本配置
*/
private JedisPoolConfig generatePoolConfig() {
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMinIdle(20);
poolConfig.setMaxTotal(300);
poolConfig.setMaxWaitMillis(5000);
poolConfig.setTestOnBorrow(true);
return poolConfig;
} @Bean(name = "redisConnectionFactory")
RedisConnectionFactory factory() {
if (StringUtils.isEmpty(masterName)) {
return generateDevConnectionFactory();
} else {
return generateReleaseConnectionFactory();
}
} @Bean(name = "redisTemplate")
public RedisTemplate<String, Object> redisTemplate(
RedisConnectionFactory factory) {
final RedisTemplate<String, Object> template = new RedisTemplate<>();
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
JdkSerializationRedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer(); template.setEnableTransactionSupport(false); //是否启用事务支持
template.setKeySerializer(stringRedisSerializer); //keySerializer:这个是对key的默认序列化器。默认值是StringSerializer。
template.setHashKeySerializer(stringRedisSerializer);//对hash结构数据的hashkey序列化器,默认值是取自DefaultSerializer的JdkSerializationRedisSerializer。
template.setValueSerializer(jdkSerializationRedisSerializer); //这个是对value的默认序列化器,默认值是取自DefaultSerializer的JdkSerializationRedisSerializer。
template.setDefaultSerializer(jdkSerializationRedisSerializer);
template.setConnectionFactory(factory); // 注入工厂
return template;
} @Bean(name = "stringRedisTemplate")
public RedisTemplate<String, String> stringRedisTemplate(
RedisConnectionFactory factory) {
final RedisTemplate<String, String> template = new RedisTemplate<>();
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
template.setEnableTransactionSupport(true);
template.setKeySerializer(stringRedisSerializer);
template.setHashKeySerializer(stringRedisSerializer);
template.setValueSerializer(stringRedisSerializer);
template.setDefaultSerializer(stringRedisSerializer);
template.setConnectionFactory(factory);
return template;
} @Bean(name = "cacheManager")
public CacheManager cacheManager(RedisTemplate<String, Object> redisTemplate) {
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
cacheManager.setDefaultExpiration(defaultExpiration);
// cacheManager.setExpires(ImmutableMap.of("apixDistrictCache", 3600 * 24 * 7L));
cacheManager.setUsePrefix(true);
return cacheManager;
}
}
一、为什么要使用Serializer
因为redis是以key-value的形式将数据存在内存中,key就是简单的string,key似乎没有长度限制,不过原则上应该尽可能的短小且可读性强,无论是否基于持久存储,key在服务的整个生命周期中都会在内存中,因此减小key的尺寸可以有效的节约内存,同时也能优化key检索的效率。
value在redis中,存储层面仍然基于string,在逻辑层面,可以是string/set/list/map,不过redis为了性能考虑,使用不同的“encoding”数据结构类型来表示它们。(例如:linkedlist,ziplist等)。
所以可以理解为,其实redis在存储数据时,都把数据转化成了byte[]数组的形式,那么在存取数据时,需要将数据格式进行转化,那么就要用到序列化和反序列化了,这也就是为什么需要配置Serializer的原因。
二、 RedisTempalte的Serializer
用过jedis操作的都知道,所有connection的操作方法,都是传入字节数组。那么,将一个对象和字节相互转换,就需要通过序列化和反序列化。
模版方法中,Spring提供了默认的StringSerializer和JdkSerializer,第一个很简单,就是通过String.getBytes()来实现的。而且在Redis中,所有存储的值都是字符串类型的。所以这种方法保存后,通过Redis-cli控制台,是可以清楚的查看到我们保存了什么key,value是什么。
但是对于JdkSerializationRedisSerializer来说,这个序列化方法就是Jdk提供的了。
首先要求我们要被序列化的类继承自Serializable接口,然后通过,然后通过Jdk对象序列化的方法保存。
(注:这个序列化保存的对象,即使是个String类型的,在redis控制台,也是看不出来的,因为它保存了一些对象的类型什么的额外信息)
这么一长串,其实就是一个int类型的123。
三、事物支持
enableTransactionSupport:是否启用事务支持。我们在代码中搜索下用到这个变量的地方,会看到,在调用RedisCallback之前,有一行代码是如果启用事务支持,那么conn = RedisConnectionUtils.bindConnection(factory, enableTransactionSupport),也就是说,系统自动帮我们拿到了事务中绑定的连接。
可以在一个方法的多次对Redis增删该查中,始终使用同一个连接。但是,即使使用了同样的连接,没有进行connection.multi()和connection.exec(),依然是无法启用事务的。
我没有仔细的查阅代码,但是可以知道的是,Spring已经对这个,给了我们一个更好的支持:@Transactional
在调用RedisTempalte中的execute()方法的地方,加入这个注解(是spring包下面提供的,不要引用成rt包下的注解),能让这个方法中的所有execute,自动加入multi()以及异常的回滚或者是正常运行时候的提交!
redis对事务提供支持,包括multi,exec,discard命令,这些命令也能用于RedisTemplate,然后redisTemplate不保证用相同的连接在同一个事务执行所有操作。
SDR提供SessionCallback接口用于同线程的多操作执行。
四、 RedisSerializer 接口 的是实现子类
public interface RedisSerializer<T> {
}
其中JdkSerializationRedisSerializer和StringRedisSerializer是最基础的序列化策略,
其中“JacksonJsonRedisSerializer”与“OxmSerializer”都是基于stirng存储,因此它们是较为“高级”的序列化(最终还是使用string解析以及构建java对象)。
基本推荐使用JdkSerializationRedisSerializer和StringRedisSerializer,因为其他两个序列化策略使用起来配置很麻烦,
如果实在有需要序列化成Json和XML格式,可以使用java代码将String转化成相应的Json和XML。