Redis有序集合详解
有序集合和集合类似,只是说它是有序的,和⽆序集合的主要区别在于每⼀个元素除了值之外,它还会多⼀个分数。分数是⼀个浮点数,在Java 中是使⽤双精度表⽰的,根据分数,Redis 就可以⽀持对分数从⼩到⼤或者从⼤到⼩的排序。
这⾥和⽆序集合⼀样,对于每⼀个元素都是唯⼀的,但是对于不同元素⽽⾔,它的分数可以⼀样。元素也是 String 数据类型,也是⼀种基于 hash 的存储结构。
集合是通过哈希表实现的,所以添加、删除、查的复杂度都是 0(1)。集合中最⼤的成员数为 2 的 32 次⽅减 1(40 多亿个成员),有序集合的数据结构如图所⽰。
有序集合是依赖 key 标⽰它是属于哪个集合,依赖分数进⾏排序,所以值和分数是必须的,⽽实际上不仅可以对分数进⾏排序,在满⾜⼀定的条件下,也可以对值进⾏排序。
Redis基础命令
有序集合和⽆序集合的命令是接近的,只是在这些命令的基础上,会增加对于排序的操作,这些是我们在使⽤的时候需要注意的细节。
下⾯讲解这些常⽤的有序集合的部分命令。有些时候 Redis 借助数据区间的表⽰⽅法来表⽰包含或者不包含,⽐如在数学的区间表⽰中,[2,5] 表⽰包含 2,但是不包含 5 的区间。
Redis有序集合的部分命令
在对有序集合、下标、区间的表⽰⽅法进⾏操作的时候,需要⼗分⼩⼼命令,注意它是操作分数还是值,稍有不慎就会出现问题。
这⾥命令⽐较多,也有些命令⽐较难使⽤,在使⽤的时候,务必要⼩⼼,不过好在我们使⽤ zset 的频率并不是太⾼,下⾯是测试结果——有序集合命令展⽰。
spring-data-redis对有序集合的封装
在 Spring 中使⽤ Redis 的有序集合,需要注意的是 Spring 对 Redis 有序集合的元素的值和分数的范围(Range)和限制(Limit)进⾏了封装,在演⽰如何使⽤ Spring 操作有序集合前要进⼀步了解它的封装。
先介绍⼀个主要的接⼝——TypedTuple,它不是⼀个普通的接⼝,⽽⼀个内部接⼝,它是
org.ZSetOperations 接⼝的内部接⼝,它定义了两个⽅法,代码如下所⽰。
public interface ZSetOperations<K,V>{
......
public interface TypedTuple<V>extends Comparable<TypedTuple<V><{
V getValue();
Double getScore();
}
......
}
这⾥的 getValue() 是获取值,⽽ getScore() 是获取分数,但是它只是⼀个接⼝,⽽不是⼀个实现类。spring-data-redis 提供了⼀个默认的实现类—— DefaultTypedTuple,同样它会实现 TypedTuple 接⼝,在默认的情况下 Spring 就会把带有分数的有序集合的值和分数封装到这个类中,这样就可以通过这个类对象读取对应的值和分数了。
Spring 不仅对有序集合元素封装,⽽且对范围也进⾏了封装,⽅便使⽤。它是使⽤接⼝
org.tion.RedisZSetCommands 下的内部类 Range 进⾏封装的,它有⼀个静态的 range() ⽅法,使⽤它就可以⽣成⼀个 Range 对象了,只是要清楚 Range 对象的⼏个⽅法才⾏,为此我们来看看下⾯的伪代码。
//设置⼤于等于min
redis五种数据结构public Range gte(Object min)
//设置⼤于min
public Range gt(Object min)
//设置⼩于等于max
public Range lte(Object max)
//设置⼩于max
public Range lt(Object max)
这 4 个⽅法就是最常⽤的范围⽅法。下⾯讨论⼀下限制,它是接⼝
org.tion.RedisZSetCommands 下的内部类,它是⼀个简单的 POJO,它存在两个属性,它们的getter 和 setter ⽅法,如下⾯的代码所⽰。
// ......
public interface RedisZSetCommands {
// ......
public class Limit {
int offset;
int count;
//setter和getter⽅法
}
//......
}
通过属性的名称很容易知道:offset 代表从第⼏个开始截取,⽽ count 代表限制返回的总数量。
通过 Spring 操作有序集合
我们讨论了 spring-data-redis 项⽬对有序集合的封装。在测试代码前,要把 RedisTemplate 的 keySerializer 和 valueSerializer 属性都修改为字符串序列化器 StringRedisSerializer,测试代码如下所⽰。
public static void testZset(){
ApplicationContext applicationContext =new ClassPathXmlApplicationContext(
"l");
RedisTemplate redisTemplate = Bean(RedisTemplate.class);
// Spring提供接⼝ TypedTuple操作有序集合
Set<TypedTuple> set1 =new HashSet<TypedTuple>();
Set<TypedTuple> set2 =new HashSet<TypedTuple>();
int j =9;
for(int i =1; i <=9; i++){
j--;
// 计算分数和值
Double score1 = Double.valueOf(i);
String value1 ="x"+ i;
Double score2 = Double.valueOf(j);
String value2 = j %2==1?"y"+ j :"x"+ j;
// 使⽤ Spring 提供的默认 TypedTuple--DefaultTypedTuple
TypedTuple typedTuple1 =new DefaultTypedTuple(value1, score1);
set1.add(typedTuple1);
TypedTuple typedTuple2 =new DefaultTypedTuple(value2, score2);
set2.add(typedTuple2);
}
// 将元素插⼊有序集合zset1
redisTemplate.opsForZSet().add("zset1", set1);
redisTemplate.opsForZSet().add("zset2", set2);
// 统计总数
Long size = null;
size = redisTemplate.opsForZSet().zCard("set1");
// 计分数为score,那么下⾯的⽅法就是求 3<=score<=6的元素
size = redisTemplate.opsForZSet().count("zset1",3,6);
Set set = null;
// 从下标⼀开始截取5个元素,但是不返回分数,每⼀个元索是String
set = redisTemplate.opsForZSet().range("zset1",1,5);
printSet(set);
/
/ 截取集合所有元素,并且对集合按分数排序,并返回分数,每⼀个元素是TypedTuple set = redisTemplate.opsForZSet().rangeWithScores("zset1",0,-1); printTypedTuple(set);
// 将zset1和zset2两个集合的交集放⼊集合inter_zset
size = redisTemplate.opsForZSet().intersectAndStore("zset1","zset2","inter_zset"); // 区间
Range range = Range.range();
range.lt("x8");// ⼩于
<("x1");// ⼤于
set = redisTemplate.opsForZSet().rangeByLex("zset1", range);
printSet(set);
range.lte("x8");// ⼩于等于
<("xl");// ⼤于等于
set = redisTemplate.opsForZSet().rangeByLex("zset1", range);
printSet(set);
// 限制返回个数
Limit limit = Limit.limit();
// 限制返回个数
// 限制从第五个开始截取
limit.offset(5);
// 求区间内的元素,并限制返回4条
set = redisTemplate.opsForZSet().rangeByLex("zset1", range, limit);
printSet(set);
// 求排⾏,排名第1返回0,第2返回1
Long rank = redisTemplate.opsForZSet().rank("zset1","x4");
// 删除元素,返回删除个数
size = redisTemplate.opsForZSet().remove("zset1","x5","x6");
// 按照排⾏删除从0开始算起,这⾥将删除第排名第2和第3的元素
size = redisTemplate.opsForZSet().removeRange("zset2",1,2);
// 获取所有集合的元素和分数,以-1代表全部元素
set = redisTemplate.opsForZSet().rangeWithScores("zset2",0,-1); printTypedTuple(set);
// 删除指定的元素
size = redisTemplate.opsForZSet().remove("zset2","y5","y3");
// 给集合中的⼀个元素的分数加上11
Double dbl = redisTemplate.opsForZSet().incrementScore("zset1","x1",11);
redisTemplate.opsForZSet().removeRangeByScore("zset1",1,2);
set = redisTemplate.opsForZSet().reverseRangeWithScores("zset2",1,10); printTypedTuple(set);
}
/**
* 打印TypedTuple集合
* @param set
* -- Set<TypedTuple>
*/
public static void printTypedTuple(Set<TypedTuple> set){
if(set != null && set.isEmpty()){
return;
}
Iterator iterator = set.iterator();
while(iterator.hasNext()){
TypedTuple val =(TypedTuple) ();
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论