一. String类型基础

1.类型介绍

  典型的Key-Value集合,如果要存实体,需要序列化成字符串,获取的时候需要反序列化一下。

2. 指令Api说明

3.常用Api说明

(1).StringSet:写入数据,如果数据已经存在,则覆盖;可以一次性存入1个key-value,也可以一次性存入多个Key-value集合,并且可以设置其过期时间。

(2).StringGet:读取数据,可以一次性读取一个key的value,也可以一次性读取多个key对应的value的集合。

(3).StringAppend:在原有值的基础上进行拼接追加.

(4).StringLength:获取值的长度

(5).StringIncrement:数值自增n,返回自增后的值

(6).StringDecrement:数值自减n,返回自减后的值

3.通用Api操作

(1).Execute("FLUSHDB"):删除所有数据,类似SqlServer的truncate

(2).KeyDelete:根据key删除数据,可以删除单个key,也可以删除多个key

(3).KeyExists:判断key是否存在,也可以单个key或者多个key

(4).KeyRename:重命名key

(5).KeyExpire:设置对应key的的过期时间

常用string类型Api代码:

  1. //1. 最简单的key-value的添加,如果该key已存在,则执行的是附加操作
  2. //可以设置过期时间哦
  3. bool a1 = db.StringSet("", "keen");
  4.  
  5. //2. 根据key获取值
  6. string data1 = db.StringGet("");
  7.  
  8. //3. 在原有的value上进行追加
  9. //在原有值的基础上追加,返回值是最终字符串的长度,如果没有这个key,则当做一个新的key进行添加
  10. long data2 = db.StringAppend("", "Marren");
  11.  
  12. //4. 获取值的长度
  13. long data3 = db.StringLength("");
  14.  
  15. //5. 数值自增/减,返回自增、自减后的值
  16. db.StringSet("", );
  17. //自增2,可以自增负值
  18. var data4 = db.StringIncrement("", );
  19. //自减5
  20. var data5 = db.StringDecrement("", );
  21.  
  22. //6. 插入实体和读取实体 (需要序列化和反序列化)
  23. //由于序列化的原因,肯定不如存到Hash里速度快
  24. UserInfor userInfor = new UserInfor()
  25. {
  26. userName = "ypf",
  27. userPwd = "",
  28. userAge =
  29. };
  30. db.StringSet("userInfor_101", JsonConvert.SerializeObject(userInfor));
  31. UserInfor data6 = JsonConvert.DeserializeObject<UserInfor>(db.StringGet("userInfor_101"));
  32.  
  33. //7. 一次性添加多个key-value集合
  34. Dictionary<string, string> dic = new Dictionary<string, string>();
  35. dic.Add("", Guid.NewGuid().ToString("N"));
  36. dic.Add("", Guid.NewGuid().ToString("N"));
  37. dic.Add("", Guid.NewGuid().ToString("N"));
  38. dic.Add("", Guid.NewGuid().ToString("N"));
  39. dic.Add("", Guid.NewGuid().ToString("N"));
  40. dic.Add("", Guid.NewGuid().ToString("N"));
  41. var keyValues = dic.Select(p => new KeyValuePair<RedisKey, RedisValue>(p.Key, p.Value)).ToArray();
  42. bool data7 = db.StringSet(keyValues);
  43.  
  44. //8.获取多个key的 value值集合
  45. string[] keys = { "", "", "" };
  46. RedisKey[] redisKeys = keys.Select(u => (RedisKey)u).ToArray();
  47. //此处如果是别的复杂类型要借助JsonConvert进行转换
  48. List<string> data8 = db.StringGet(redisKeys).Select(u => u.ToString()).ToList();

通用Api代码:

  1. //1. 删除所有数据
  2. db.Execute("FLUSHDB");
  3.  
  4. //2. 删除单个key
  5. bool d1 = db.KeyDelete(""); //删除成功,返回true
  6. bool d2 = db.KeyDelete("ffff"); //删除不存在的数据,返回false
  7.  
  8. //3. 删除多个key
  9. string[] arry = { "", "", "" };
  10. RedisKey[] keys = arry.Select(u => (RedisKey)u).ToArray();
  11. long d3 = db.KeyDelete(keys); //返回的是删除成功的个数
  12.  
  13. //4. 判断key是否存在(不推荐使用,会有并发问题)
  14. bool d4 = db.KeyExists("");
  15. bool d5 = db.KeyExists("");
  16.  
  17. //5. 重命名key
  18. bool d6 = db.KeyRename("", "");
  19.  
  20. //6. 设置key的过期时间(1分钟后自动销毁)
  21. bool d7 = db.KeyExpire("", DateTime.Now.AddMinutes());

二. String类型案例

1. 普通的Key-Value缓存

  string类型最简单的一个应用就是Key-value缓存,value可以是简单string、int,也可以是序列化后的实体,可以替代Session进行存储,和其它缓存一样,也可以设置缓存的过期时间(常用的键设计:表名_id  ,如: UserInfor_001 )

2. 秒杀-超卖问题

(1).背景

  某商家拿出来100件iphone11在某天的0点以超低价开卖,势必有很多人等着购买. 简单分析一下业务逻辑:判断库存,库存>0,继续往后执行下单逻辑(比如:插入订单表、发货地址表记录等等); 否则提示顾客“商品已经被抢光”.

  看到这个需求,可能新手会直接操控关系型数据库,并没有采取一下措施,这样就会导致同一时间进来的顾客判断库存都>0, 购买成功的人数就>100了,也就是出现了超卖现象,对于商家而言, 我太难了!!

常用的解决方案:

A. 下单页面加Lock锁,会造成大量的客户等待卡死等现象. (注:只能锁住单进程,如果是分布式,多个IIS,需要引进分布式锁)

B. 利用乐观锁, 会造成一种现象即使该用户是前100个进来的也没有买到,不合理,不适用

C. 把下单的用户加到队列中,然后开启另外一个线程从队列中读取进行下单,下单业务执行完,再出队,下单成功/失败 利用实时通讯技术通知客户端 或者 客户端主动刷新页面进行查看结果.

(此处需要区分是出队后,接着出队,还是出队后执行完下单业务才能出下一个对呢, 还要注意如果秒杀服务是个集群,无法保证原队列的顺序,且同样存在超买超卖问题)

(2).利用Redis单线程的原理解决 

  事先将该商品的库存初始化到Redis中,然后利用StringDecrement自减1同时返回自减后的值,如果值>=0,表示有库存然后执行后面的下单逻辑,这里利用Redis很大程度的给关系型数据库减压了(不用查询sqlserver 判断库存了), 库存不足,直接返回库存不足。

(单体Redis好用,集群Redis不适用,如果用集群的话,可以考虑用lua脚本把查库存和减库存写到一起,这样就可以用于集群了)

代码分享:

  1. public static void CaseDemo1(IDatabase db)
  2. {
  3. //删除所有数据
  4. db.Execute("FLUSHDB");
  5. //事先初始化库存
  6. db.StringSet("order_Num", );
  7.  
  8. List<Task> taskList = new List<Task>();
  9. for (int i = ; i < ; i++) //模拟多个用户并发
  10. {
  11. var task = Task.Run(() =>
  12. {
  13. try
  14. {
  15. //先自减,获取自减后的值
  16. int order_Num = (int)db.StringDecrement("order_Num", );
  17. if (order_Num >= )
  18. {
  19. //下面执行订单逻辑(这里不考虑业务出错的情况)
  20. Task.Delay();
  21. Console.WriteLine("下单成功了");
  22. }
  23. else
  24. {
  25. Console.WriteLine("商品已经被抢光了");
  26. }
  27.  
  28. }
  29. catch (Exception ex)
  30. {
  31. Console.WriteLine(ex.Message);
  32. throw;
  33. }
  34. });
  35. taskList.Add(task);
  36. }
  37. Task.WaitAll(taskList.ToArray());
  38. }

PS:关于秒杀问题,详见后面单独的章节:

                    第六节:秒杀业务/超买超卖的几种解决思路

3. 点击量、点赞量、访问量

(1). 背景

  要统计一个网站的访问次数,一个ip一天只能点击一次。

(2). 解决方案

  先判断是否存在该ip,如果不存在,以ip为key,value随意,存储到string类型中,同时利用StringIncrement对访问次数自增1。

  1. /// <summary>
  2. /// 访问量案例
  3. /// </summary>
  4. /// <returns></returns>
  5. public IActionResult Index()
  6. {
  7. //获取Ip,这里利用个随机数模拟ip效果
  8. var ip = Guid.NewGuid().ToString("N");
  9. if (!_redis.KeyExists(ip))
  10. {
  11. //把该ip存进去,并且设置有效期为1天
  12. _redis.StringSet(ip, "随意值", TimeSpan.FromDays());
  13. //同时访问次数自增1
  14. _redis.StringIncrement("fw_count", );
  15. }
  16. ViewBag.FwCount = _redis.StringGet("fw_count");
  17. return View();
  18. }

总结: 

  String除了key-value当缓存外,主要是利用其原子性,围绕自增自减并返回当前值(计数器作用)来使用,比如:单个网站的点击量(访问量、收藏量),单个商品的秒杀等等。如果是某个类别下多个物品的计数,同时要获取物品的计数排名,则利用SortedSet来实现,比如:某个班级每个小孩的投票数并排序、某个栏目下每篇文章的阅读数并排序 等等。

PS:String和SortedSet具有计数器功能,String是针对单个,Sorted是针对某个类别下的多个或每一个,并且实现排序功能。 Hash类型也能实现某个类别下多个物品的计数,但它不具有排序功能。

三. Hash类型基础

1.类型说明

  一个key,对应一个Key-Value集合, hashid -{key:value;key:value;key:value;}, 相当于value又是一个“键值对集合” 或者值是另外一个 Dictionary。

2. 常用指令Api

3.常用Api说明

(1).HashSet:存储单个 hashid-key-value

(2).HashGet:单个value的获取; 获取1个hashid对应的所有key集合; 获取1个hashid对应所有的key和value集合.

(3).HashDelete:删除1个hashid-key; 删除1个hashid-多个key

(4).HashIncrement:自增,返回自增后的值

(5).HashDecrement:自减,返回自减后的值

(6).HashExists:判断 hashid-key 是否存在,返回true和value

代码分享:

  1. //1.添加
  2. db.HashSet("UserInfor_001", "name", "ypf");
  3. db.HashSet("UserInfor_001", "age", "");
  4. db.HashSet("UserInfor_001", "sex1", "男1");
  5. db.HashSet("UserInfor_001", "sex2", "男2");
  6. db.HashSet("UserInfor_001", "sex3", "男3");
  7. db.HashSet("UserInfor_001", "sex4", "男4");
  8. db.HashSet("UserInfor_001", "name", "Marren"); //会覆盖上面的值
  9. //没找到一下把实体添加进去的方法
  10. UserInfor userInfor = new UserInfor()
  11. {
  12. userName = "ypf",
  13. userPwd = "",
  14. userAge =
  15. };
  16.  
  17. //2.获取
  18. //2.1 单个value的获取
  19. string age = db.HashGet("UserInfor_001", "age");
  20. string name = db.HashGet("UserInfor_001", "name");
  21. //2.2 获取1个hashid对应所有的key的集合(前提必须是同数据类型的)
  22. List<string> keyList = db.HashKeys("UserInfor_001").Select(u => (string)u).ToList();
  23. //2.3 获取hashid对应的所有key和value,必须保证该hashid对应的所有数据类型一致
  24. Dictionary<string, string> dic = new Dictionary<string, string>();
  25. foreach (var item in db.HashGetAll("UserInfor_001"))
  26. {
  27. dic.Add(item.Name, item.Value);
  28. }
  29. //没法一下获取一个实体
  30.  
  31. //3. 删除
  32. //单个key
  33. bool d1 = db.HashDelete("UserInfor_001", "name");
  34. //多个key
  35. string[] dataKeyArry = { "sex1", "sex2", "sex3" };
  36. RedisValue[] redisValueArry = dataKeyArry.Select(u => (RedisValue)u).ToArray();
  37. long deleteNum = db.HashDelete("UserInfor_001", redisValueArry);
  38.  
  39. //4. 自增,自减, 返回自增或自减后的值
  40. db.HashSet("UserInfor_002", "age", );
  41. long d2 = db.HashIncrement("UserInfor_002", "age", ); //自增2,返回值为22
  42. long d3 = db.HashDecrement("UserInfor_002", "age", ); //自减3,返回值为19
  43.  
  44. //5. 判断数据是否存在
  45. bool d4 = db.HashExists("UserInfor_002", "age");
  46. bool d5 = db.HashExists("UserInfor_002", "age2");

 4. 优缺点

四. Hash类型案例

  Hash类型用于存储某个类别下多个物品的存储,也可以实现物品的计数器功能,但是和SortedSet相比,它不具有排序功能。

1. 购物车

 分析:

  以用户id作为hashid,商品id作为key,商品数量作为value,利用自增和自减功能来实现增加商品数量和减少商品数量功能。也可以删除商品,获取商品总数,获取购物车中所有商品。

2. 存储群聊消息。

  存储群聊消息,比如:群名为hashid, 用户id当做key,内容作为value。 这样存储可以,但是取数据的时候必须一下全部取出来,不能根据时间取前n条。

!

  • 作       者 : Yaopengfei(姚鹏飞)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 声     明1 : 本人才疏学浅,用郭德纲的话说“我是一个小学生”,如有错误,欢迎讨论,请勿谩骂^_^。
  • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
 

第一节: Redis之String类型和Hash类型的介绍和案例应用的更多相关文章

  1. 第二节: Redis之Set类型和SortedSet类型的介绍和案例应用

    一. Set类型基础 1. 类型说明 1个key→多个value,value的值不重复! Set一种无序且元素内容不重复的集合,不用做重复性判断了,和我们数学中的集合概念相同,可以对多个集合求交集.并 ...

  2. Redis 笔记与总结2 String 类型和 Hash 类型

    Linux 版本信息: cat /etc/issue 或cat /etc/redhat-release(Linux查看版本当前操作系统发行版信息) CentOS release 6.6 (Final) ...

  3. 02_NoSQL数据库之Redis数据库:string类型和hash类型

     Strings类型及操作 String是最简单的类型,一个key对应一个Value,String类型是二进制安全的.Redis的String可以包含任何数据,比如jpg图片或者序列化的对象. S ...

  4. 【redis】02string类型和hash类型

    Redis的数据类型   Redis主要分为五个数据类型,一个是string,最简单的一个数据类型,hash,list, 还有set集合,还有zset有序集合,这是咱们redis的五种基础类型, 接下 ...

  5. 【redis】04set类型和zset类型

    sets类型   sets类型及操作   Set类型是一个集合,他是string类型的无序集合,也就是说咱们的set是没有顺序的, Set是通过hash table实现的,添加.删除和查找的复杂度都是 ...

  6. C++ unordered_map 在key为string类型和char*类型时测试时间性能差异

    测试系统liunx centos6.5 代码如下 #include <string.h> #include <sstream> #include <list> #i ...

  7. C# string类型和byte[]类型相互转换

    string类型转成byte[]: byte[] byteArray = System.Text.Encoding.Default.GetBytes ( str ); byte[]转成string: ...

  8. string类型和int类型之间的转换

    一.string转int 1. 使用string流 /* 字符串转整型 */ /* * istringstream:从 string 读取数据 * ostringstream:向 string 写入数 ...

  9. 关于 实体类中 时间字段 为string 类型和 datatime类型 比较

    经发现, 数据库中保存时间格式数据  可以正常 排序, 数据中保存时间格式字符串 排序出现问题 /// <summary> /// 修改时间 /// </summary> pu ...

随机推荐

  1. 20个Python代码段,你需要立刻学会,好用到哭!

    Python是一种非BS编程语言.设计简单和易读性是它广受欢迎的两大原因.正如Python的宗旨:美丽胜于丑陋,显式胜于隐式. 记住一些帮助提高编码设计的常用小诀窍是有用的.在必要时刻,这些小诀窍能够 ...

  2. Java入门——编写并运行第一个程序

    Java入门——编写并运行第一个程序 摘要:本文主要介绍如何使用Java语言编写并通过DOS运行简单的程序. 编写简单的程序 在D盘新建一个文本文档,输入如下代码: class Hello { pub ...

  3. Java日期时间API系列4-----Jdk7及以前的日期时间类的线程安全问题

    1.Date类为可变的,在多线程并发环境中会有线程安全问题. (1)可以使用锁来处理并发问题. (2)使用JDK8  Instant 或 LocalDateTime替代. 2.Calendar的子类为 ...

  4. 聚焦性能技术和实践, MTSC全面揭秘PerfDog演进之路

    商业转载请联系腾讯WeTest获得授权,非商业转载请注明出处. 12月14日,2019年度中国移动互联网测试开发大会(Mobile Testing Summit China,简称 MTSC)深圳站于深 ...

  5. vue学习笔记(一): 建立 vue-cli 初始网站

    在安装vue-cli之前,要先安装node.js这个大家百度一下就可以了 1.安装 vue-cli npm install -g @vue/cli-init 2.初始化一个项目,名为 hcmanage ...

  6. 挑战常规 -- 为什么不要再用cookie作为储存?

    不要使用cookie当存储 Cookie 是什么? Cookie 由浏览器储存在本地,每次访问目标网址会带上的请求头,服务器可以通过Set-Cookie响应头设置Cookie. Cookie的用途 由 ...

  7. Dynamics CRM 客户端程序开发:在实体的列表界面添加按钮

    关注本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复114或者20140312可方便获取本文,同时可以在第一时间得到我发布的最新的博文信息,follow me! 如果没有安装Ribbon Wor ...

  8. Django和前端用ajax传输json等数据

    需要传输的是下图中所有的input中客户端设置的数据 整个页面是用js生成,代码不长,但是用了许多拼接,看起来开比较乱,页面的核心就是下面的部分,有一些关键参数部分就不放了,可以跳过这个 下面开始重点 ...

  9. 在PyCharm中打开文件的位置

    选中文件,右键选择  Show in Explorer (在资源管理器中显示) 只需要路径时,选择第四个Copy Path ,会复制文件的路径

  10. OSI网络模型和网络连接设备

    OSI网络模型和网络连接设备 OSI模型 7层之间传输的协议传输单元(PDU)的专业叫法. 第7-5层(应用层)传输的pdu叫:data 第4层(传输层)传输的pdu叫:segment(数据段) 第3 ...