1.简介
在 set 的基础上给集合中每个元素关联了一个分数,往有序集合中插入数据时会自动根据这个分数排序。不同的是每个元素都会关联一个double类型的分数。
redis正是通过分数来为集合中的成员进行从小到大的排序。
zset的成员是唯一的,但分数(score)却可以重复。
zset也指sorted set(有序集合)。
2.使用场景
在集合类型的场景上加入排序就是有序集合的应用场景了。比如根据好友的“亲密度”排序显示好友列表。
3.zset数据结构示意图
zset类型在存储数据时,是以key-SortList<value>格式存储的,如下图所示:
ZSet相比于Set类型多了一个排序属性 score(分值),对于有序集合 ZSet 来说,每个存储元素相当于有两个值组成的,一个是有序结合的元素值,一个是排序值。有序集合保留了集合不能有重复成员的特性(分值可以重复),但不同的是,有序集合中的元素可以排序。
如下是对zset封装后的api方法,RedisZSetService.cs,代码如下:
using MyRedis.Redis.Interface;
using System.Collections.Generic;
namespace MyRedis.Redis.Service
{
public class RedisZSetService : RedisBase
{
#region 添加
/// <summary>
/// 添加key/value,默认分数是从1.多*10的9次方以此递增的,自带自增效果
/// </summary>
public bool AddItemToSortedSet(string key, string value)
{
return base.iClient.AddItemToSortedSet(key, value);
}
/// <summary>
/// 添加key/value,并设置value的分数
/// </summary>
public bool AddItemToSortedSet(string key, string value, double score)
{
return base.iClient.AddItemToSortedSet(key, value, score);
}
/// <summary>
/// 为key添加values集合,values集合中每个value的分数设置为score
/// </summary>
public bool AddRangeToSortedSet(string key, List<string> values, double score)
{
return base.iClient.AddRangeToSortedSet(key, values, score);
}
/// <summary>
/// 为key添加values集合,values集合中每个value的分数设置为score
/// </summary>
public bool AddRangeToSortedSet(string key, List<string> values, long score)
{
return base.iClient.AddRangeToSortedSet(key, values, score);
}
#endregion
#region 获取
/// <summary>
/// 获取key的所有集合
/// </summary>
public List<string> GetAll(string key)
{
return base.iClient.GetAllItemsFromSortedSet(key);
}
/// <summary>
/// 获取key的所有集合,倒叙输出
/// </summary>
public List<string> GetAllDesc(string key)
{
return base.iClient.GetAllItemsFromSortedSetDesc(key);
}
/// <summary>
/// 获取集合,带分数
/// </summary>
public IDictionary<string, double> GetAllWithScoresFromSortedSet(string key)
{
return base.iClient.GetAllWithScoresFromSortedSet(key);
}
/// <summary>
/// 获取key为value的下标值
/// </summary>
public long GetItemIndexInSortedSet(string key, string value)
{
return base.iClient.GetItemIndexInSortedSet(key, value);
}
/// <summary>
/// 倒叙排列获取key为value的下标值
/// </summary>
public long GetItemIndexInSortedSetDesc(string key, string value)
{
return base.iClient.GetItemIndexInSortedSetDesc(key, value);
}
/// <summary>
/// 获取key为value的分数
/// </summary>
public double GetItemScoreInSortedSet(string key, string value)
{
return base.iClient.GetItemScoreInSortedSet(key, value);
}
/// <summary>
/// 获取key所有集合的数据总数
/// </summary>
public long GetSortedSetCount(string key)
{
return base.iClient.GetSortedSetCount(key);
}
/// <summary>
/// key集合数据从分数为fromscore到分数为toscore的数据总数
/// </summary>
public long GetSortedSetCount(string key, double fromScore, double toScore)
{
return base.iClient.GetSortedSetCount(key, fromScore, toScore);
}
/// <summary>
/// 获取key集合从高分到低分排序数据,分数从fromscore到分数为toscore的数据
/// </summary>
public List<string> GetRangeFromSortedSetByHighestScore(string key, double fromscore, double toscore)
{
return base.iClient.GetRangeFromSortedSetByHighestScore(key, fromscore, toscore);
}
/// <summary>
/// 获取key集合从低分到高分排序数据,分数从fromscore到分数为toscore的数据
/// </summary>
public List<string> GetRangeFromSortedSetByLowestScore(string key, double fromscore, double toscore)
{
return base.iClient.GetRangeFromSortedSetByLowestScore(key, fromscore, toscore);
}
/// <summary>
/// 获取key集合从高分到低分排序数据,分数从fromscore到分数为toscore的数据,带分数
/// </summary>
public IDictionary<string, double> GetRangeWithScoresFromSortedSetByHighestScore(string key, double fromscore, double toscore)
{
return base.iClient.GetRangeWithScoresFromSortedSetByHighestScore(key, fromscore, toscore);
}
/// <summary>
/// 获取key集合从低分到高分排序数据,分数从fromscore到分数为toscore的数据,带分数
/// </summary>
public IDictionary<string, double> GetRangeWithScoresFromSortedSetByLowestScore(string key, double fromscore, double toscore)
{
return base.iClient.GetRangeWithScoresFromSortedSetByLowestScore(key, fromscore, toscore);
}
/// <summary>
/// 获取key集合数据,下标从fromRank到分数为toRank的数据
/// </summary>
public List<string> GetRangeFromSortedSet(string key, int fromRank, int toRank)
{
return base.iClient.GetRangeFromSortedSet(key, fromRank, toRank);
}
/// <summary>
/// 获取key集合倒叙排列数据,下标从fromRank到分数为toRank的数据
/// </summary>
public List<string> GetRangeFromSortedSetDesc(string key, int fromRank, int toRank)
{
return base.iClient.GetRangeFromSortedSetDesc(key, fromRank, toRank);
}
/// <summary>
/// 获取key集合数据,下标从fromRank到分数为toRank的数据,带分数
/// </summary>
public IDictionary<string, double> GetRangeWithScoresFromSortedSet(string key, int fromRank, int toRank)
{
return base.iClient.GetRangeWithScoresFromSortedSet(key, fromRank, toRank);
}
/// <summary>
/// 获取key集合倒叙排列数据,下标从fromRank到分数为toRank的数据,带分数
/// </summary>
public IDictionary<string, double> GetRangeWithScoresFromSortedSetDesc(string key, int fromRank, int toRank)
{
return base.iClient.GetRangeWithScoresFromSortedSetDesc(key, fromRank, toRank);
}
#endregion
#region 删除
/// <summary>
/// 删除key为value的数据
/// </summary>
public bool RemoveItemFromSortedSet(string key, string value)
{
return base.iClient.RemoveItemFromSortedSet(key, value);
}
/// <summary>
/// 删除下标从minRank到maxRank的key集合数据
/// </summary>
public long RemoveRangeFromSortedSet(string key, int minRank, int maxRank)
{
return base.iClient.RemoveRangeFromSortedSet(key, minRank, maxRank);
}
/// <summary>
/// 删除分数从fromscore到toscore的key集合数据
/// </summary>
public long RemoveRangeFromSortedSetByScore(string key, double fromscore, double toscore)
{
return base.iClient.RemoveRangeFromSortedSetByScore(key, fromscore, toscore);
}
/// <summary>
/// 删除key集合中分数最大的数据
/// </summary>
public string PopItemWithHighestScoreFromSortedSet(string key)
{
return base.iClient.PopItemWithHighestScoreFromSortedSet(key);
}
/// <summary>
/// 删除key集合中分数最小的数据
/// </summary>
public string PopItemWithLowestScoreFromSortedSet(string key)
{
return base.iClient.PopItemWithLowestScoreFromSortedSet(key);
}
#endregion
#region 其它
/// <summary>
/// 判断key集合中是否存在value数据
/// </summary>
public bool SortedSetContainsItem(string key, string value)
{
return base.iClient.SortedSetContainsItem(key, value);
}
/// <summary>
/// 为key集合值为value的数据,分数加scoreby,返回相加后的分数
/// </summary>
public double IncrementItemInSortedSet(string key, string value, double scoreBy)
{
return base.iClient.IncrementItemInSortedSet(key, value, scoreBy);
}
/// <summary>
/// 获取keys多个集合的交集,并把交集添加的newkey集合中,返回交集数据的总数
/// </summary>
public long StoreIntersectFromSortedSets(string newkey, string[] keys)
{
return base.iClient.StoreIntersectFromSortedSets(newkey, keys);
}
/// <summary>
/// 获取keys多个集合的并集,并把并集数据添加到newkey集合中,返回并集数据的总数
/// </summary>
public long StoreUnionFromSortedSets(string newkey, string[] keys)
{
return base.iClient.StoreUnionFromSortedSets(newkey, keys);
}
#endregion
}
}
上面的代码创建完成后,项目的基础就构建完成了,下面开始学习zset数据类型常用api的使用。
在控制台应用程序中的ServiceStackTest.cs类中,调用Redis的api方法。代码如下:
using MyRedis.Redis.Service;
using System;
using System.Collections.Generic;
using System.Threading;
namespace MyRedis
{
/// <summary>
/// ServiceStackTest api 封装测试
/// </summary>
public class ServiceStackTest
{
public static void Show()
{
#region RedisZSetService
using (RedisZSetService zsetService = new RedisZSetService())
{
zsetService.FlushAll();
zsetService.AddItemToSortedSet("zsetTest", "1", 1);
zsetService.AddItemToSortedSet("zsetTest", "1", 1);
zsetService.AddItemToSortedSet("zsetTest", "7", 2);
zsetService.AddItemToSortedSet("zsetTest", "2", 3);
zsetService.AddItemToSortedSet("zsetTest", "4", 4);
var res1 = zsetService.GetAll("zsetTest");
var res2 = zsetService.GetAllDesc("zsetTest");
zsetService.AddItemToSortedSet("kkk", "路人甲", 10);
zsetService.AddItemToSortedSet("kkk", "爱笑的向日葵", 50);
zsetService.AddItemToSortedSet("kkk", "丑小鸭", 2);
zsetService.AddRangeToSortedSet("kkk", new List<string>() { "花生米", "企鹅" }, 100);
var res3 = zsetService.GetAllWithScoresFromSortedSet("kkk");
foreach (var item in res3)
{
Console.WriteLine($"{item.Key} : {item.Value}");
}
}
//主播礼物实时排行榜
#endregion
}
}
}
执行结果:
基于有序集合自动排序的特点,可以实现主播打赏排名,下面用程序模拟直播间人们为主播打赏实时排名统计:
using MyRedis.Redis.Service;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MyRedis.Demo
{
/// <summary>
/// 模拟用户给主播刷礼物
/// 刷礼物时增加分数
/// 可以实时获取最新排行
/// 刷礼物时候保存数据库并更新Redis
/// </summary>
public class ZSetGiftDemo
{
//用户
private static List<string> _listUser = new List<string>()
{
"张三","李四","王五","赵六","麻子","二狗"
};
public static void Show()
{
using (RedisZSetService service = new RedisZSetService())
{
service.FlushAll(); //清理全部数据
//模拟给主播刷礼物
Task.Run(() =>
{
while (true)
{
foreach (var user in _listUser)
{
Thread.Sleep(100);
service.IncrementItemInSortedSet("冯提莫", user, new Random().Next(1, 100)); //表示在原来刷礼物的基础上增加礼物
}
Thread.Sleep(10 * 1000);
}
});
//查看实时排行榜
Task.Run(() =>
{
while (true)
{
Thread.Sleep(6 * 1000);
Console.WriteLine("*****************当前排行*****************");
int i = 1;
foreach (var item in service.GetRangeWithScoresFromSortedSetDesc("冯提莫", 0, 9)) //排行前10名
{
Console.WriteLine($"第{i++}名 {item.Key} 分数{item.Value}");
}
}
});
Console.Read(); //不能删除
}
}
}
}
执行结果:
上面程序模拟了用户为主播打赏的过程,假定10秒钟用户为主播打赏一次,程序每隔6秒钟统计出当前最新的排行数据。这里也正是利用了有序集合排序的特性。