• Redis在C#中的应用:String和Hash


    安装Nuget包:ServiceStack.Redis
    连接数据库

    using (RedisClient client = new RedisClient("127.0.0.1", 6379))
    {
        //删除当前数据库中的所有Key  默认删除的是db0
        client.FlushDb();
        //删除所有数据库中的key 
        client.FlushAll();
        
        ......
    }
    

    使用String

    设置key-value

    client.Set<string>("name", "编程");
    Console.WriteLine("正确输出如下");
    Console.WriteLine(client.Get<string>("name")); // 编程
    Console.WriteLine("第二种输出");
    Console.WriteLine(client.GetValue("name")); // 做了序列化  "编程"
    Console.WriteLine(JsonConvert.DeserializeObject<string>(client.GetValue("name")));// 编程
    

    设置多个key的value

    // 批量写入redis key
    client.SetAll(new Dictionary<string, string>{{"id","001" },{"name","路飞" },});
    // 批量读取多个key结果,如果key不存在,程序会返回一个空字符串
    var getall = client.GetAll<string>(new string[]{ "id","name","number", });
    foreach (var item in getall)
    {
        Console.WriteLine(item);
    }
    //[id, 001]
    //[name, 路飞]
    //[number,]
    

    设置key的value并设置过期时间

    #region  设置key的value并设置过期时间
    client.Set<string>("name", "路飞", TimeSpan.FromSeconds(1));
    Task.Delay(1 * 1000).Wait();
    Console.WriteLine(client.Get<string>("name"));
    #endregion
    
    #region 设置key的value并设置过期时间
    client.Set<string>("name", "路飞", DateTime.Now.AddSeconds(1));
    //client.Set<string>("name", "路飞", DateTime.Now.AddDays(1));
    Console.WriteLine("刚写进去的结果");
    Console.WriteLine(client.Get<string>("name"));
    Task.Delay(1 * 1000).Wait();
    Console.WriteLine("1秒钟之后的结果");
    Console.WriteLine(client.Get<string>("name"));
    
    client.Set<string>("class", "优秀班级", TimeSpan.FromSeconds(10));
    Task.Delay(1 * 1000).Wait();
    Console.WriteLine(client.Get<string>("class"));
    #endregion
    

    更改value值

    #region 在原有key的value值之后追加value
    client.AppendToValue("name", "I");
    client.AppendToValue("name", " ");
    client.AppendToValue("name", "LOVE YOU");
    Console.WriteLine(client.Get<string>("name"));
    #endregion
    
    #region 获取旧值赋上新值
    client.Set("name", "路飞");
    //获取当前key的之前的值,然后把新的结果替换进入
    var value = client.GetAndSetValue("name", "路飞");
    Console.WriteLine("原先的值" + value);
    Console.WriteLine("新值" + client.GetValue("name"));
    #endregion
    

    自增自减

    #region 自增1,返回自增后的值
    //给key为sid的键自增1 ,返回了自增之后的结果
    Console.WriteLine(client.Incr("sid"));
    Console.WriteLine(client.Incr("sid"));
    Console.WriteLine(client.Incr("sid"));
    Console.WriteLine("哈哈哈");
    Console.WriteLine(client.GetValue("sid"));
    //每次通过传递的count累计,count就是累加的值
    client.IncrBy("sid", 2);
    Console.WriteLine(client.Get<string>("sid"));
    client.IncrBy("sid", 100);
    Console.WriteLine("最后的结果***" + client.GetValue("sid"));
    #endregion
    
    #region 自减1,返回自减后的值
    Console.WriteLine(client.Decr("sid"));
    Console.WriteLine(client.Decr("sid"));
    Console.WriteLine(client.Decr("sid"));
    Console.WriteLine("最后的结果" + client.GetValue("sid"));
    //通过传入的count去做减肥 之前的结果-count
    client.DecrBy("sid", 2);
    Console.WriteLine("最终的结果" + client.GetValue("sid"));
    #endregion
    

    add 和set 的区别

    #region add 和set 的区别?
    //当使用add 方法去操作redis的时候,如果key存在的话,
    //则不会再次进行操作 返回false 如果操作成功返回true
    Console.WriteLine(client.Add("name", "路飞"));
    Console.WriteLine(client.Add("name", "你很好路飞"));
    Console.WriteLine(client.Get<string>("name"));
    
    //使用set去操作 redis的时候,如果key不存在则写入当前值,
    //并且返回true,通过存在,则对之前的值进行了一个替换 返回操作的结果
    Console.WriteLine(client.Set("name", "路飞"));
    Console.WriteLine(client.Set("name", "你很好路飞"));
    Console.WriteLine(client.Get<string>("name"));
    #endregion
    

    模拟秒杀程序

    假如有10个商品,先存入redis数据库

    //命令行参数启动
    //dotnet RedisChapter1.dll --id=1 minute=18
    //安装microsoft.extensions.configuration、microsoft.extensions.configuration.commandline
    var builder = new ConfigurationBuilder().AddCommandLine(args);
    var configuration = builder.Build();
    string id = configuration["id"];
    int minute = int.Parse(configuration["minute"]);
    Console.WriteLine("开始" + id);
    
    using (RedisClient client = new RedisClient("127.0.0.1", 6379))
    {
        //首先给数据库预支了秒杀商品的数量
        client.Set<int>("number", 10);
    }
    Seckill.Show(id, minute);
    

    秒杀方法,开启10个线程进行抢购,当数据库中商品数量为0时抢购失败。

    public class Seckill
    {
        public static void Show(string id,int minute)
        {
            // 开启10个线程抢购
            Console.WriteLine($"在{minute}分0秒正式开启秒杀!");
            var flag = true;
            while (flag)
            {
                if(DateTime.Now.Minute == minute)
                {
                    flag = false;
                    for (int i = 0; i < 10; i++)
                    {
                        string name = $"客户端{id}号:{i}";
                        Task.Run(()=>
                        {
                            using (RedisClient client = new RedisClient("127.0.0.1", 6379))
                            {
                                // 取修改商品数量自减1
                                var num = client.Decr("number");
                                if (num < 0)
                                {
                                    Console.WriteLine(name + "抢购失败!");
                                }
                                else
                                {
                                    Console.WriteLine(name + "恭喜,抢购成功!");
                                }
                            }
                        });
                        Thread.Sleep(10);
                    }
                }
                Thread.Sleep(10);
            }
            Console.ReadLine();
        }
    }
    

    我们开启三个客户端,在42分钟时模拟用户开始秒杀。

    客户端1
    F:\RedisC1\bin\Debug\netcoreapp3.1>dotnet RedisC1.dll --id=1 minute=42
    开始1
    在42分0秒正式开启秒杀!
    客户端1号:0恭喜,抢购成功!
    客户端1号:1恭喜,抢购成功!
    客户端1号:2恭喜,抢购成功!
    客户端1号:3恭喜,抢购成功!
    客户端1号:4抢购失败!
    客户端1号:5抢购失败!
    客户端1号:6抢购失败!
    客户端1号:7抢购失败!
    客户端1号:8抢购失败!
    客户端1号:9抢购失败!
    
    客户端2
    F:\RedisC1\bin\Debug\netcoreapp3.1>dotnet RedisC1.dll --id=2 minute=42
    开始2
    在42分0秒正式开启秒杀!
    客户端2号:0恭喜,抢购成功!
    客户端2号:1恭喜,抢购成功!
    客户端2号:2恭喜,抢购成功!
    客户端2号:3抢购失败!
    客户端2号:4抢购失败!
    客户端2号:5抢购失败!
    客户端2号:6抢购失败!
    客户端2号:7抢购失败!
    客户端2号:8抢购失败!
    客户端2号:9抢购失败!
    
    F:\RedisC1\bin\Debug\netcoreapp3.1>dotnet RedisC1.dll --id=3 minute=42
    开始3
    在42分0秒正式开启秒杀!
    客户端3号:0恭喜,抢购成功!
    客户端3号:1恭喜,抢购成功!
    客户端3号:2恭喜,抢购成功!
    客户端3号:3抢购失败!
    客户端3号:4抢购失败!
    客户端3号:5抢购失败!
    客户端3号:6抢购失败!
    客户端3号:7抢购失败!
    客户端3号:8抢购失败!
    客户端3号:9抢购失败!
    

    从结果中可以看到10个商品被抢购成功,其余失败。

    为什么redis可以解决高并发情况下的秒杀,而且在代码中没有看到任何的锁?

    因为redis它是一个单线程的服务。
    很多人都有个误区:

    • 误区1:多线程(cpu上下文切换)一定比单线程快?。
    • 误区2:redis是单线程服务,那么redis服务里面是不是只有一个线程。

    其实单线程代表的是处理命令或者指令的时候,后台只有一个处理指令线程。
    我们发送给redis服务的指令,在同一时刻只会执行一条指令。

    redis 它是一个支持分布式的内存数据库,它有多种数据类型,为什么快?

    因为是它内存数据库,关系型数据库是操作硬盘的(好比人记的笔记),而内存数据库操作是内存(好比人的大脑记忆)。

    官网推荐优先使用hash

    向hashid集合中添加key/value

    #region  向hashid集合中添加key/value
    client.SetEntryInHash(hashid, "id", "001");
    Console.WriteLine(client.GetValuesFromHash(hashid, "id").FirstOrDefault());
    client.SetEntryInHash(hashid, "name", "路飞");
    Console.WriteLine(client.GetValuesFromHash(hashid, "name").FirstOrDefault());
    client.SetEntryInHash(hashid, "socre", "100");
    Console.WriteLine(client.GetValuesFromHash(hashid, "socre").FirstOrDefault());
    #endregion
    
    #region 如果hashid集合中存在key/value则不添加返回false,如果不存在在添加key/value,返回true
    Console.WriteLine(client.SetEntryInHashIfNotExists(hashid, "name", "123"));
    Console.WriteLine(client.SetEntryInHashIfNotExists(hashid, "name", "123 哈哈哈"));
    Console.WriteLine(client.GetValuesFromHash(hashid, "name").FirstOrDefault());
    #endregion
    

    批量新增key的值

    #region 批量新增key的值
    Dictionary<string, string> pairs = new Dictionary<string, string>();
    pairs.Add("id", "001");
    pairs.Add("name", "路飞");
    client.SetRangeInHash(hashid, pairs);
    //获取当前key的值
    Console.WriteLine(client.GetValueFromHash(hashid, "id"));
    Console.WriteLine(client.GetValueFromHash(hashid, "name"));
    //一次性的获取所有想要获取的小key(属性的)值  如果key不存在,则返回空,不抛出异常
    var list = client.GetValuesFromHash(hashid, "id", "name", "abc");
    Console.WriteLine("*********");
    foreach (var item in list)
    {
        Console.WriteLine(item);
    }
    #endregion
    

    存储对象到hash集合中

    #region 存储对象T t到hash集合中
    //urn: 类名: id的值
    client.StoreAsHash<UserInfo>(new UserInfo() { Id = 2, Name = "路飞", number = 0 });
    //如果id存在的话,则覆盖之前相同的id
    
    client.StoreAsHash<UserInfo>(new UserInfo() { Id = 1, Name = "路飞2" });
    //获取对象T中ID为id的数据。 必须要有属性id,不区分大小写
    
    Console.WriteLine(client.GetFromHash<UserInfo>(1).Name);
    var olduserinfo = client.GetFromHash<UserInfo>(1);
    olduserinfo.number = 4;
    client.StoreAsHash(olduserinfo);
    Console.WriteLine("最后的结果" + client.GetFromHash<UserInfo>(1).number);
    client.StoreAsHash(new UserInfoTwo() { Id = "001", Name = "路飞2" });
    Console.WriteLine(client.GetFromHash<UserInfoTwo>("001").Name);
    client.StoreAsHash(new UserInfoTwo() { Id = "002", Name = "路飞" });
    Console.WriteLine(client.GetFromHash<UserInfoTwo>("002").Name);
    
    UserInfo lisi = new UserInfo() { Id = 1, Name = "李四", number = 0 };
    client.StoreAsHash(lisi);
    Console.WriteLine(client.GetFromHash<UserInfo>(1).number);
    //做个自增
    var oldzhang = client.GetFromHash<UserInfo>(1);
    oldzhang.number++;
    client.StoreAsHash(oldzhang);
    #endregion
    

    其他方法

    // 获取所有hashid数据集的key/value数据集合
    var dics = client.GetAllEntriesFromHash(hashid);
    
    // 获取hashid数据集中的数据总数
    client.GetHashCount(hashid);
    
    // 获取hashid数据集中所有key的集合
    var keys = client.GetHashKeys(hashid);
    
    // 获取hashid数据集中的所有value集合
    var values = client.GetHashValues(hashid);
    
    // 删除hashid数据集中的key数据
    client.RemoveEntryFromHash(hashid, "id");
    
    // 判断hashid数据集中是否存在key的数据
    client.HashContainsEntry(hashid, "id")); //T  F
    
    // 给hashid数据集key的value加countby,返回相加后的数据
    // 注意,存的值必须是数字类型,否则抛出异常
    client.IncrementValueInHash(hashid, "number", 2)); 
    
  • 相关阅读:
    打开虚拟机导致电脑蓝屏
    jmeter访问接口后返回的数据乱码
    使用jmeter批量对新增账号初始化操作
    使用jmeter参数化时在数据中匹配11位的手机号并分组操作
    vsphere vdp备份情况导出
    grep的完全匹配(不是-w)
    win10右键新建md文件,亲测有效
    信步漫谈之Wiki知识库——搭建dokuwiki
    vim操作学习
    writev函数
  • 原文地址:https://www.cnblogs.com/nullcodeworld/p/16571895.html
Copyright © 2020-2023  润新知