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)); 
posted @ 2022-08-10 12:00  一纸年华  阅读(49)  评论(0编辑  收藏  举报