Redis初步认识2021-08-20
记录一下数据类型和对数据类型的简单操作,相关的命令可以在官网上看见,我看命令的名称也都一样。
上边是源码,下边是官网介绍的,可以对照着看,挺方便的。
数据类型:
String: redis中的基本类型,是二进制安全的(字符串不会因为存在特殊字符而导致歧义,c语言中\0表示为字符串的结束,但是redis中的字符串没有这样类似的特殊字符),可以包含任意类型的数据,比如:一个图片的或者一个对象。
可以用INCR(incr,decr,incrby)把字符串当作一个原子计数器来操作,保证原子性。
List: 简单的字符串集合,可以选择插入头部(LPUSH ),或者从尾部插入(RPUSH),一个列表最多可以放入2^32 -1个元素,访问两端的数据非常的快速,但是访问一个元素特别多的集合的中间数据还是比较慢的。可以用LPUSH操作放入一些最新的数据,然后用LRANGE去查阅最新的数据。
Set: 无序不重复的集合,增加元素,删除元素,否是包含元素都是很快的,负责度为O(1),在往set集合中添加新元素的时候,不用手动去判断是否已经包含此元素,可以很短的时间内求出两个集合 交集,并集的数据,最多包含元素同上。可以用来计算某个网站的访问量,
Hash: 字符串字段和字符串值的映射关系,可以用来表示对象,一个hash包含最多的键值对同上。一个hash所拥有的字段少于100个只需要很少的空间来存储。
Sort Set:有序集合,每个元素都关联着一个评分,这个评分就是用来排序使用的。因为元素在添加的时候就会排序,所以可以很快的查阅一个范围的数据,
maven依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
@Bean
public RedisTemplate<String,Object> redisTemplate(LettuceConnectionFactory factory){
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
Jackson2JsonRedisSerializer jackson = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
jackson.setObjectMapper(om);
// 值采用json序列化
template.setValueSerializer(jackson);
//使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
// 设置hash key 和value序列化模式
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(jackson);
template.afterPropertiesSet();
return template;
}
使用这个序列化配置之后,自己测试了下String,List,Hash的简单存取都成功了,然后接着开始测试pipeline操作
尝试存好几个String 的操作出错了,报错是因为类型不支持转换,但是我看源代码确实是应该可以的,因为是继承关系。
后来就不转换类型了,直接用 RedisConnection类的方法进行操作,存储是成功了,但是通过pipeline操作存储成功的String的值,无法通过redisTemplate.opsForValue().get(key)获取,
是因为pipeline进行存储没有指定序列化,指定一下就可以了。
在pipeline操作查看多个键的时候,我开始是这么写的,但是每次这个redisConnection.get(xxxx)都是返回空,然后百度了半天,最终找到问题所在,是个简单的问题,但是我有点蠢了。。
redisTemplate.executePipelined(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {
list.stream().forEach(x -> {
redisConnection.get("string13".getBytes(StandardCharsets.UTF_8));
});
return null;
}
});这段代码是我重写executePipelined方法,点进去看一下源码
public List<Object> executePipelined(RedisCallback<?> action) {
return this.executePipelined(action, this.valueSerializer);//调用的就是下面的方法
}
public List<Object> executePipelined(RedisCallback<?> action, @Nullable RedisSerializer<?> resultSerializer) {
return (List)this.execute((connection) -> {
connection.openPipeline();
boolean pipelinedClosed = false;
List var7;
try {
Object result = action.doInRedis(connection);// 这个就是实际调用我重写的doInRedis方法
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;
});
}
然后我就进到之前看了好多遍的redisConnection.get(xxxx)的具体实现类,打断点之后发现了具体的实现方法
步骤一就是进入到方法,步骤二因为我是pipeline进入的,所以一直是true,所以我之前在自己重写的doInRedis方法中得到的null就是的步骤四这个地方返回的,可以看到步骤三中的get方法是将结果绑定了到connection中。再回到上面代码:
public List<Object> executePipelined(RedisCallback<?> action) {
return this.executePipelined(action, this.valueSerializer);
}
public List<Object> executePipelined(RedisCallback<?> action, @Nullable RedisSerializer<?> resultSerializer) {
return (List)this.execute((connection) -> {
connection.openPipeline();
boolean pipelinedClosed = false;
List var7;
try {
Object result = action.doInRedis(connection); //实际查询的结果此时是在这个connection中的 本来我想是不是result中的 但是下面代码并没有用到result这个变量 只是继续用了connection
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();//只有这块用到了connection
pipelinedClosed = true;
var7 = this.deserializeMixedResults(closePipeline, resultSerializer, this.hashKeySerializer, this.hashValueSerializer);//这个方法也 间接用到了connection,然后看一下这个方法的具体实现
} finally {
if (!pipelinedClosed) {
connection.closePipeline();
}
}
return var7;
});
}
@Nullable
private List<Object> deserializeMixedResults(@Nullable List<Object> rawValues, @Nullable RedisSerializer valueSerializer, @Nullable RedisSerializer hashKeySerializer, @Nullable RedisSerializer hashValueSerializer) {
if (rawValues == null) {
return null;
} else {
List<Object> values = new ArrayList();
Iterator var6 = rawValues.iterator();这个地方就很明显了,是根据具体子元素的类型判断一下 然后反序列化一下,最终就是根据这个结果集返回的
while(true) {
while(var6.hasNext()) {
Object rawValue = var6.next();
if (rawValue instanceof byte[] && valueSerializer != null) {
values.add(valueSerializer.deserialize((byte[])((byte[])rawValue)));
} else if (rawValue instanceof List) {
values.add(this.deserializeMixedResults((List)rawValue, valueSerializer, hashKeySerializer, hashValueSerializer));
} else if (rawValue instanceof Set && !((Set)rawValue).isEmpty()) {
values.add(this.deserializeSet((Set)rawValue, valueSerializer));
} else if (rawValue instanceof Map && !((Map)rawValue).isEmpty() && ((Map)rawValue).values().iterator().next() instanceof byte[]) {
values.add(SerializationUtils.deserialize((Map)rawValue, hashKeySerializer, hashValueSerializer));
} else {
values.add(rawValue);
}
}
return values;
}
}
}