在C#中使用Redis
NoSql
NoSql概念
NoSQL,泛指非关系型的数据库。随着互联网web2.0网站的兴起, 历史中—中国的网站----马云--- 中国黄页,只能展示;用户只能看到 传统的关系数据库在处理web2.0网站(可以看,也可以做到写),特 别是超大规模和高并 发的SNS类型的web2.0纯动态网站已经显得力 不从心,出现了 很多难以克服的问题。 而非关系型的数据库则由于其本身的特点得到了非常迅速的发展。 NoSQL数据库的产生就是为了解决大规模数据集合多重数据种 类带来的挑战,特别是大数据应用难题。
非关系----基本上单纯的保存数据----不支持多种数据之间的关系关系;
NoSql特点
早期: 只能存储数据在内存,性能搞,读取快,成本高; 不能固化存储;
现在:基本上也都可以固化到硬盘---也可以做到持久化存储;
Redis
什么是Redis
Remot Dictionary Server---远程字典服务器 字典:key-value
官方地址:https://redis.io/
Redis 是一种开源(BSD 许可)、内存中数据结构存储,用作数据库、缓 存和消息代理。 Redis 提供了数据结构,例如字符串、散列、列表、集合、带有范围查询的 排序集合、 位图、超级日志、地理空间索引和流。Redis 内置复制、Lua 脚本、LRU 驱 逐、事务和 不同级别的磁盘持久化,并通过Redis Sentinel 和Redis Cluster 自动分区提 供高可用性
Redis环境搭建
可以在我资料里面找Redis文件:--仅限Windows
.NET程序对接Redis
.NET 对接Redis的有三大组件 NuGet程序下载:
- StackExchange.Redis库(不支持哨兵,主从和集群)
- ServiceStack.Redis (不支持哨兵,主从和集群)
- CsRedis (支持哨兵,主从和集群)-首选
- 第一步打开Redis服务:
出现以下就是正确的:
- 第二步:打开Redis 客户端查看是否有效
路径:
验证:
能使用和输出就证明成功。
- 安装Redis可视化工具:
- 创建连接
测试连接Success
默认是有15个db库
刚才写的数据,就在db0里面:
Redis数据结构
五大数据结构:string(字符串),hash(哈希),list(列表),set(无序集合)及zset(有序集合)。
String类型
Key-Value存储
字符串类型是Redis中最基本的数据存储类型,它是一个由字节组成的序列,在Rediss 中是二进制安全的。这意味着该类型可以接受任何格式数据,如JPEG图像数据和Json 对象说明信息。它是标准的key-value,通常用于存储字符串、整数和浮点。Value可容 纳高达512MB的数据。 由于所有数据都在单个对象中,Redis 中的字符串操作速度非常快。基本的 Redis 命令 (如 SET、GET 和 DEL)允许您对字符串值执行基本操作。
SET 键值 – 设置指定键的值。
GET 键 – 检索指定键的值。
DEL 键 – 删除给定键的值。
在C#中使用: 安装NuGet :Caching.CSRedis 是必不可少的
"localhost,defaultDatabase=3,poolsize=3,tryit=0":连接字符串
localhost:Redis的Ip
defaultDatabase:默认是第几个库
poolsize:最大连接数量
tryit:尝试次数
string redisConnectionString = "localhost,defaultDatabase=3,poolsize=3,tryit=0";string key = "key";
using (CSRedisClient cSRedisClient = new CSRedisClient(redisConnectionString))
{cSRedisClient.Set(key, "小海study~~");string sResult = cSRedisClient.Get(key);
};
写一个父类,包含一些常用的方法和数据库连接:
public class RedisBase{protected CSRedisClient rds = new CSRedisClient("127.0.0.1,defaultDatabase=0,poolsize=3,tryit=0");public RedisBase(){//rds.NodesServerManager.FlushAll();}/// <summary>/// 配置操作哪个节点/// </summary>/// <param name="nodeIndex"></param>public void ConfigNode(string nodeIndex){}/// <summary>/// 删除所有节点信息/// </summary>public void FlushAll(){rds.NodesServerManager.FlushAll();}}
应用程序场景:非常常见的场景用于计算站点访问量、当前在线人数等
秒杀案例:
超卖:订单数超过商品
秒杀:10件商品,大用户量的来参与秒杀,同时来抢这个商品; 肯定是多个线程同时来操作
如果商品保存在数据库中:
程序设计:a.获取商品数量 b.判断是否还有库存 c.如果有库存---提示秒杀成功--减库存 d.库存再设置上去
注意:防止超卖---10商品参与秒杀,如果下了20个订单~~
public class OversellTest : RedisBase
{private static object Object_locker = new object();public void Show(){int count = 10; //初始化有10件商品//应为是秒杀,并发很高~~ 多线程~~ //如果秒杀成功---必然要减库存~~List<Task> tasklist = new List<Task>();for (int i = 0; i < 5000; i++){tasklist.Add(Task.Run(() =>{int k = i;//判断仓库数据量和减库存必须是原子性操作;原子的:不能拆分; lock (Object_locker){if (count > 0) //获取库存,判断是否还有库存{//Thread.Sleep(new Random().Next(10, 30)); //随机休息 count = count - 1; //减库存Console.WriteLine($"用户{k}参与秒杀,秒杀成功了。。。");}else{Console.WriteLine($"秒杀结束了...count值:{count}");}}}));}Task.WaitAll(tasklist.ToArray());Console.WriteLine($"所有秒杀结束后,库存应该为0 ,这里的Count:{count}");}private static bool IsGoOn = true;//秒杀活动是否结束public void ShowRedis(){FlushAll();rds.Set("Stock", 10); //初始化商品的库存量for (int i = 0; i < 5000; i++){int k = i;Task.Run(() =>//每个线程就是一个用户请求{if (IsGoOn){//long index = rds.IncrBy("Stock", -1); //自减1并且返回 ---这个是原则性操作,不会出现中间值; 不会有超卖问题if (index >= 0){Console.WriteLine($"{k.ToString("000")}秒杀成功,秒杀商品索引为{index}");}else{if (IsGoOn){IsGoOn = false;}Console.WriteLine($"{k.ToString("000")}秒杀失败,秒杀商品索引为{index}");}}else{Console.WriteLine($"{k.ToString("000")}秒杀停止......");}});}Console.Read();}
}
Hash类型
Key-Value存储
Redis hash 是一个键值(key=>value)对集合。Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。Redis的Hash结构可以使你像在数据库 中Update一个属性一样只修改某一项属性值。和String略像,但value中存放的是一张表, 一般用于多个个体的详细事项排列,String也可以做到,但要比hash麻烦许多。 哈希命令允许您独立访问和更改单个或多个字段。
HSET – 将值映射到哈希中的键。
HGET – 检索与哈希中的键关联的各个值。
HGETALL – 显示整个哈希内容。
HDEL – 从哈希中删除现有的键值对。
Hash二次封装:
public class RedisHashService : RedisBase{#region Hash/// <summary>/// [redis-server 3.2.0] 返回hash指定field的value的字符串长度,如果hash或者field不存在,返回0./// </summary>/// <param name="key">不含prefix前辍</param>/// <param name="field">字段</param>/// <returns></returns>public long HStrLen(string key, string field) => rds.HStrLen(key, field);/// <summary>/// 删除一个或多个哈希表字段/// </summary>/// <param name="key">不含prefix前辍</param>/// <param name="fields">字段</param>/// <returns></returns>public long HDel(string key, params string[] fields) => rds.HDel(key, fields);/// <summary>/// 查看哈希表 key 中,指定的字段是否存在/// </summary>/// <param name="key">不含prefix前辍</param>/// <param name="field">字段</param>/// <returns></returns>public bool HExists(string key, string field) => rds.HExists(key, field);/// <summary>/// 获取存储在哈希表中指定字段的值/// </summary>/// <param name="key">不含prefix前辍</param>/// <param name="field">字段</param>/// <returns></returns>public string HGet(string key, string field) => rds.HGet(key, field);/// <summary>/// 获取存储在哈希表中指定字段的值/// </summary>/// <typeparam name="T">byte[] 或其他类型</typeparam>/// <param name="key">不含prefix前辍</param>/// <param name="field">字段</param>/// <returns></returns>public T HGet<T>(string key, string field) => rds.HGet<T>(key, field);/// <summary>/// 获取在哈希表中指定 key 的所有字段和值/// </summary>/// <param name="key">不含prefix前辍</param>/// <returns></returns>public Dictionary<string, string> HGetAll(string key) => rds.HGetAll(key);/// <summary>/// 获取在哈希表中指定 key 的所有字段和值/// </summary>/// <typeparam name="T">byte[] 或其他类型</typeparam>/// <param name="key">不含prefix前辍</param>/// <returns></returns>public Dictionary<string, T> HGetAll<T>(string key) => rds.HGetAll<T>(key);/// <summary>/// 为哈希表 key 中的指定字段的整数值加上增量 increment/// </summary>/// <param name="key">不含prefix前辍</param>/// <param name="field">字段</param>/// <param name="value">增量值(默认=1)</param>/// <returns></returns>public long HIncrBy(string key, string field, long value = 1) => rds.HIncrBy(key, field, value);/// <summary>/// 为哈希表 key 中的指定字段的整数值加上增量 increment/// </summary>/// <param name="key">不含prefix前辍</param>/// <param name="field">字段</param>/// <param name="value">增量值(默认=1)</param>/// <returns></returns>public decimal HIncrByFloat(string key, string field, decimal value) => rds.HIncrByFloat(key, field, value);/// <summary>/// 获取所有哈希表中的字段/// </summary>/// <param name="key">不含prefix前辍</param>/// <returns></returns>public string[] HKeys(string key) => rds.HKeys(key);/// <summary>/// 获取哈希表中字段的数量/// </summary>/// <param name="key">不含prefix前辍</param>/// <returns></returns>public long HLen(string key) => rds.HLen(key);/// <summary>/// 获取存储在哈希表中多个字段的值/// </summary>/// <param name="key">不含prefix前辍</param>/// <param name="fields">字段</param>/// <returns></returns>public string[] HMGet(string key, params string[] fields) => rds.HMGet(key, fields);/// <summary>/// 获取存储在哈希表中多个字段的值/// </summary>/// <typeparam name="T">byte[] 或其他类型</typeparam>/// <param name="key">不含prefix前辍</param>/// <param name="fields">一个或多个字段</param>/// <returns></returns>public T[] HMGet<T>(string key, params string[] fields) => rds.HMGet<T>(key, fields);/// <summary>/// 同时将多个 field-value (域-值)对设置到哈希表 key 中/// </summary>/// <param name="key">不含prefix前辍</param>/// <param name="keyValues">key1 value1 [key2 value2]</param>/// <returns></returns>public bool HMSet(string key, params object[] keyValues){return rds.HMSet(key, keyValues);}/// <summary>/// 将哈希表 key 中的字段 field 的值设为 value/// </summary>/// <param name="key">不含prefix前辍</param>/// <param name="field">字段</param>/// <param name="value">值</param>/// <returns>如果字段是哈希表中的一个新建字段,并且值设置成功,返回true。如果哈希表中域字段已经存在且旧值已被新值覆盖,返回false。</returns>public bool HSet(string key, string field, object value){return rds.HSet(key, field, value);}/// <summary>/// 只有在字段 field 不存在时,设置哈希表字段的值/// </summary>/// <param name="key">不含prefix前辍</param>/// <param name="field">字段</param>/// <param name="value">值(string 或 byte[])</param>/// <returns></returns>public bool HSetNx(string key, string field, object value){return rds.HSetNx(key, field, value);}/// <summary>/// 获取哈希表中所有值/// </summary>/// <param name="key">不含prefix前辍</param>/// <returns></returns>public string[] HVals(string key) => rds.HVals(key);/// <summary>/// 获取哈希表中所有值/// </summary>/// <typeparam name="T">byte[] 或其他类型</typeparam>/// <param name="key">不含prefix前辍</param>/// <returns></returns>public T[] HVals<T>(string key) => rds.HVals<T>(key);/// <summary>/// 迭代哈希表中的键值对/// </summary>/// <param name="key">不含prefix前辍</param>/// <param name="cursor">位置</param>/// <param name="pattern">模式</param>/// <param name="count">数量</param>/// <returns></returns>public RedisScan<(string field, string value)> HScan(string key, long cursor, string pattern = null, long? count = null){return rds.HScan(key, cursor, pattern, count);}/// <summary>/// 迭代哈希表中的键值对/// </summary>/// <typeparam name="T">byte[] 或其他类型</typeparam>/// <param name="key">不含prefix前辍</param>/// <param name="cursor">位置</param>/// <param name="pattern">模式</param>/// <param name="count">数量</param>/// <returns></returns>public RedisScan<(string field, T value)> HScan<T>(string key, long cursor, string pattern = null, long? count = null){return rds.HScan<T>(key, cursor, pattern, count);}#endregion}
常见的使用:
RedisHashService rds = new RedisHashService();{rds.FlushAll();{// 同时将多个 field-value (域-值)对设置到哈希表 key 中rds.HMSet("TestHDel", "string1", "name", "bytes1", "25", "class1", new UserInfo() { Name = "XiaoHai", Id = 123 });//删除一个或多个哈希表字段rds.HDel("TestHDel", "string1", "bytes1", "class1");// 查看哈希表 key 中,指定的字段是否存在bool exists = rds.HExists("TestHExists", "null1");Console.WriteLine(exists);//将哈希表 key 中的字段 field 的值设为 valuerds.HSet("TestHExists", "null1", 1);//查看哈希表 key 中,指定的字段是否存在exists = rds.HExists("TestHExists", "null1");Console.WriteLine(exists);//删除一个或多个哈希表字段rds.HDel("TestHExists", "null1");//查看哈希表 key 中,指定的字段是否存在exists = rds.HExists("TestHExists", "null1");}{string result1 = rds.HGet("TestHGet", "null1");}}
应用程序场景:存储部分更改数据,如用户信息、会话共享。
因为在使用Redis的时候,尽可能的去完成原子性操作,对于一个业务处理,尽量不要去搞多个操作;
Redis在WPF中的应用
缓存数据
在需要的ViewModel中集成RedisBase
在RedisBase中封装为一个通用的方法:
/// <summary>
/// 优先获取redis中的数据,如果没有想要获取的数据的数据,就执行委托
/// </summary>
/// <typeparam name="T">数据类型</typeparam>
/// <param name="key">redis的key</param>
/// <param name="func">包装查询数据库的逻辑</param>
/// <returns></returns>
protected T GetCacheData<T>(string key,Func<T> func) where T : class
{// 如果rds中没有数据就执行委托(执行数据库),否则就执行rdsT t = rds.Get<T>(key);if (t == null) // redis里面没有数据{t = func.Invoke();rds.Set(key, t);}return t;
}
然后再ViewModel中调用这个方法:
List<ScoreInfo> scoreList = new List<ScoreInfo>();
string key = "scoreList";
scoreList = GetCacheData(key, () =>
{return _scoreInfoService.Set<ScoreInfo>().ToList();
});ScoreList.Clear();
//赋值
scoreList.ForEach(x => ScoreList.Add(x));