c# redis系列二
Hash
Hash:key--Dictionary,
1 节约空间(zipmap的紧密摆放的存储模式)
2 更新/访问方便(hashid+key) Hash数据很像关系型数据库的表的一行数据, 但是字段是可以随意定制的,没有严格约束的,非常灵活
如果说我们要存储一个模型,如果还是存储string类型的话就得将其序列化,使用的时候取出来之后还是要再次反序列化,这样太麻烦了。
Hash:类似dictionary,通过索引快速定位到指定元素的,耗时均等,跟string的区别在于不用反序列化,直接修改某个字段;string的话要么是 001:序列化整个实体,要么是 001_name: 001_pwd: 多个key-value,Hash的话,一个hashid-{key:value;key:value;key:value;}可以一次性查找实体,也可以单个,还可以单个修改
如果是存string类型的话,后面做更改的话就需要全部取出来,然后更改,更改完之后再重新存储,这样的话就会存在问题,万一更改过程中出现其他问题,比如多线程之类的导致其他数据出现问题。如果使用hash的话可以直接更改指定元素,不必取出,保证了原子性,性能还高。
模型类:
public class UserInfo { public int Id { get; set; } public string Name { get; set; } public string Account { get; set; } public string Password { get; set; } public string Email { get; set; } public string Address { get; set; } public long QQ { get; set; } }
string类型示例:
{ //service.FlushAll();//删除所有的Redis缓存 string key = $"userInfo_{user.Id}"; //这是redis内部自动对UserInfo做了序列化 service.Set<UserInfo>(key, user); //取出来也是redis内部做的反序列化 List<UserInfo> userList = service.Get<UserInfo>(new List<string>() { key }); UserInfo updateUser = userList.FirstOrDefault(); updateUser.Account = "Admin"; service.Set<UserInfo>(key, updateUser); }
hash:
using (RedisHashService service = new RedisHashService()) { service.FlushAll(); service.SetEntryInHash($"UserInfo_{user.Id}", "Name", user.Name); service.SetEntryInHash($"UserInfo_{user.Id}", "Account", user.Account); //如果在这个hashid中已经存在一个key,再次执行SetEntryInHash方法就会覆盖上一个值,也可以当做修改方法使用 service.SetEntryInHash($"UserInfo_{user.Id}", "Name", user.Address); service.SetEntryInHash($"UserInfo_{user.Id}", "Name", user.Email); service.GetValueFromHash($"UserInfo_{user.Id}", "Name"); //目标是最好是一个Api 去定位到某一个数据,直接在Redis 层面就给改掉! //Hash存储是有Api 直接修改某一个; }
hash存储是这种结构:
Set存储
Set存储value值自动去重,使用场景: IP统计去重; 添加好友申请; 投票限制; 点赞;
封装的一些常用的api:
/// <summary> /// Set:用哈希表来保持字符串的唯一性,没有先后顺序,存储一些集合性的数据 /// 1.共同好友、二度好友 /// 2.利用唯一性,可以统计访问网站的所有独立 IP /// </summary> public class RedisSetService : RedisBase { #region 添加 /// <summary> /// key集合中添加value值 /// </summary> public void Add(string key, string value) { base.iClient.AddItemToSet(key, value); } /// <summary> /// key集合中添加list集合 /// </summary> public void Add(string key, List<string> list) { base.iClient.AddRangeToSet(key, list); } #endregion #region 获取 /// <summary> /// 随机获取key集合中的一个值 /// </summary> public string GetRandomItemFromSet(string key) { return base.iClient.GetRandomItemFromSet(key); } /// <summary> /// 获取key集合值的数量 /// </summary> public long GetCount(string key) { return base.iClient.GetSetCount(key); } /// <summary> /// 获取所有key集合的值 /// </summary> public HashSet<string> GetAllItemsFromSet(string key) { return base.iClient.GetAllItemsFromSet(key); } #endregion #region 删除 /// <summary> /// 随机删除key集合中的一个值 /// </summary> public string RandomRemoveItemFromSet(string key) { return base.iClient.PopItemFromSet(key); } /// <summary> /// 删除key集合中的value /// </summary> public void RemoveItemFromSet(string key, string value) { base.iClient.RemoveItemFromSet(key, value); } #endregion #region 其它 /// <summary> /// 从fromkey集合中移除值为value的值,并把value添加到tokey集合中 /// </summary> public void MoveBetweenSets(string fromkey, string tokey, string value) { base.iClient.MoveBetweenSets(fromkey, tokey, value); } /// <summary> /// 返回keys多个集合中的并集,返还hashset /// </summary> public HashSet<string> GetUnionFromSets(params string[] keys) { return base.iClient.GetUnionFromSets(keys); } /// <summary> /// 返回keys多个集合中的交集,返还hashset /// </summary> public HashSet<string> GetIntersectFromSets(params string[] keys) { return base.iClient.GetIntersectFromSets(keys); } /// <summary> /// 返回keys多个集合中的差集,返还hashset /// </summary> /// <param name="fromKey">原集合</param> /// <param name="keys">其他集合</param> /// <returns>出现在原集合,但不包含在其他集合</returns> public HashSet<string> GetDifferencesFromSet(string fromKey, params string[] keys) { return base.iClient.GetDifferencesFromSet(fromKey,keys); } /// <summary> /// keys多个集合中的并集,放入newkey集合中 /// </summary> public void StoreUnionFromSets(string newkey, string[] keys) { base.iClient.StoreUnionFromSets(newkey, keys); } /// <summary> /// 把fromkey集合中的数据与keys集合中的数据对比,fromkey集合中不存在keys集合中,则把这些不存在的数据放入newkey集合中 /// </summary> public void StoreDifferencesFromSet(string newkey, string fromkey, string[] keys) { base.iClient.StoreDifferencesFromSet(newkey, fromkey, keys); } #endregion }
一些简单的api方法:
using (RedisSetService service = new RedisSetService()) { service.FlushAll();//清理全部数据 service.Add("advanced", "111"); service.Add("advanced", "112"); service.Add("advanced", "114"); service.Add("advanced", "114"); service.Add("advanced", "115"); service.Add("advanced", "115"); service.Add("advanced", "113"); var result = service.GetAllItemsFromSet("advanced"); //随机获取key其中一个value var random = service.GetRandomItemFromSet("advanced"); service.GetCount("advanced");//独立的ip数 //删除指定数据 service.RemoveItemFromSet("advanced", "114"); { service.Add("begin", "111"); service.Add("begin", "112"); service.Add("begin", "115"); service.Add("end", "111"); service.Add("end", "114"); service.Add("end", "113"); //获取begin和end中的 交集 var result1 = service.GetIntersectFromSets("begin", "end"); //获取begin和end中的 差集 var result2 = service.GetDifferencesFromSet("begin", "end"); //获取begin和end中的 并集,就是2者 var result3 = service.GetUnionFromSets("begin", "end"); //共同好友 共同关注 } }
set存储的格式:
下面列举了一个添加好友的一个例子:
public static void Show() { using (RedisSetService service = new RedisSetService()) { service.FlushAll(); service.Add("Richard", "云怀"); service.Add("Richard", "fresh"); service.Add("Richard", "腾坤"); service.Add("Richard", "心有所属"); service.Add("Richard", "莎士比亚"); service.Add("fresh", "奥利给"); service.Add("fresh", "明日梦"); service.Add("fresh", "棒棒糖"); service.Add("fresh", "放下"); service.Add("fresh", "阳光下的微信"); service.Add("fresh", "腾坤"); //1.查询共同好友; 求交集 var resut1 = service.GetIntersectFromSets("Richard", "fresh");// //2.好友推荐/可能认识人,这是给Richard推荐的 返回keys多个集合中的差集,返还hashset 出现在fresh集合中,但不包含在Richard集合中 var resut2 = service.GetDifferencesFromSet("fresh", "Richard"); //同上 var resut3 = service.GetDifferencesFromSet("Richard", "fresh");// //汇总所有的 并集 var result4 = service.GetUnionFromSets("Richard", "fresh"); } }
ZSet存储
可以去重复还可以排序。比如排行榜的时候可以使用!
封装的一些常用的api类:
/// <summary> /// Sorted Sets是将 Set 中的元素增加了一个权重参数 score,使得集合中的元素能够按 score 进行有序排列 /// 1.带有权重的元素,比如一个游戏的用户得分排行榜 /// 2.比较复杂的数据结构,一般用到的场景不算太多 /// </summary> public class RedisZSetService : RedisBase { #region 添加 /// <summary> /// 添加key/value,默认分数是从1.多*10的9次方以此递增的,自带自增效果 /// </summary> public bool Add(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 }
简单调用对应的一些常用api:
using (RedisZSetService service = new RedisZSetService()) { service.FlushAll();//清理全部数据 //添加之后会默认一个score,默认递增 service.Add("advanced", "1"); service.Add("advanced", "2"); service.Add("advanced", "5"); service.Add("advanced", "4"); service.Add("advanced", "7"); service.Add("advanced", "5"); service.Add("advanced", "9"); var result1 = service.GetAll("advanced"); //获取key为advanced的集合信息,倒序输出 var result2 = service.GetAllDesc("advanced"); //也可以手动指定score值,默认按照score值递增 service.AddItemToSortedSet("Sort", "BY", 123234); service.AddItemToSortedSet("Sort", "走自己的路", 123); service.AddItemToSortedSet("Sort", "redboy", 45); service.AddItemToSortedSet("Sort", "大蛤蟆", 7567); service.AddItemToSortedSet("Sort", "路人甲", 9879); var result3 = service.GetAllWithScoresFromSortedSet("Sort"); //交叉并 }
redis可视化工具中查看到的数据:
接下来模拟一个排行榜的场景,比如一个直播间中的观众的礼物排行榜,这东西是实时的刷新的,不能一直来刷新数据库,所以可以用redis,并且使用zset来存储:
/// <summary> /// 直播平台:很多频道---不同的房间/主播 /// 刷小礼物! /// /// 排行榜:频道排行!平台的排行!房间排行榜! 需要实时展示数据! /// Redis--Zset;排序 /// /// </summary> public class RankManager { //模拟的房间的听众 private static List<string> userList = new List<string>() { "棒棒糖","苏洋","思索","奥利给","Alex","君临天下" }; public static void Show() { using (RedisZSetService service = new RedisZSetService()) { service.FlushAll(); Task.Run(() => //刷礼物---每隔2秒钟就刷一个礼物 { while (true) { foreach (var user in userList) { Thread.Sleep(20); //设置 刷礼物的人,每次刷的礼物是1到100之间的一个随机数 service.IncrementItemInSortedSet("主播1", user, new Random().Next(1, 100)); } Thread.Sleep(20 * 100); } }); //实时展示排行榜 每5秒展示一次最新数据 Task.Run(() => { while (true) { Console.WriteLine("**************Eleven房间排行榜******************"); Thread.Sleep(5 * 1000); //获取主播1中所有刷礼物的人的信息 var dic = service.GetAllWithScoresFromSortedSet("主播1"); int i = 1; foreach (var item in dic) { Console.WriteLine($"第{i++}名 {item.Key} 分数{item.Value}"); } } }); Console.Read(); //现在大家开始提问,22:20 开始答疑! 期间老师就不说话了! } } }
转载:
https://www.cnblogs.com/anjingdian/p/15380530.html
c# redis系列三
https://www.cnblogs.com/anjingdian/p/15383351.html
c# redis之持久化方案
https://www.cnblogs.com/anjingdian/p/15383352.html
c# redis之缓存击穿雪崩
https://www.cnblogs.com/anjingdian/p/15383355.html
sqlserver之事务
https://www.cnblogs.com/anjingdian/p/15388603.html
作者很有计算含量持续关注!!1