安装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));