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 的值设为 value
rds.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中没有数据就执行委托(执行数据库),否则就执行rds
T 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));