Redis_全称(Remote Dictionary Server)远程字典服务
1.Redis的用处与使用场景?
Redis是NOSQL(Not Only SQL 非关系型数据库)中的一种常用的缓存(内存)数据库.
应对高并发时可以进行数据缓存,减轻我们服务器直接操作硬盘的压力.因为当Web获取数据时,是去直接操作数据库的,就等于去直接操作服务器硬盘进行IO操作,而IO操作非常耗费服务器性能,这时我们可以将需要Web需要读取的数据,放到Redis中,而Redis是将数据放到内存中,而内存的效率,比直接访问硬盘的效率高很多,以此来达到减轻服务器压力的目的.
PS: Redis的处理速度,可以达到一秒钟十万的速度.
2.NoSQL数据库的优点与适应场景以及不适应场景?
优点:
1.NoSQL数据库没有关联关系,数据结构简单,拓展表比较容易.
2.NOSQL读取速度快,对较大数据处理快.适用场景:
1. 数据高并发的读写
2. 海量数据的读写
3. 对扩展性要求高的数据不适应场景:
1. 需要事务支持(非关系型数据库)
2. 基于sql结构化查询储存,关系复杂
2.1 Redis优点:
1. 方便扩展
2. 大数据量高性能
3. 数据存储类型多样性
4. 自带分布式存储2.2 Redis 八种存储类型:
String(字符串)丶List(集合)丶Hash(哈希)丶Set(去重集合)丶Zset(有序的去重集合)丶BitMaps丶HyperLogLoss丶Streams
3. 使用.Net Core5的 控制台程序来了解Redis的存储类型
1.从Nuget管理包中 安装 ServiceStack.Redis 来帮助我们连接Redis.
3.1 控制台程序测试Redis类型的方法
using Microsoft.VisualBasic;
using Newtonsoft.Json;
using ServiceStack;
using ServiceStack.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Threading.Tasks;
using TechnicalSummary_Core.RedisTest.RedisModel;
namespace TechnicalSummary_Core.RedisTest
{
class RedisStringTest
{
static void Main(string[] args)
{
// 实例化Redis连接 参数:1.IP地址 2.端口号 参数 db可以指定 1-15 中的任意数据库
using (IRedisClient client = new RedisClient("127.0.0.1", 6379))
{
string DaKey = "总KEY"; // 与对象名概念相似
client.FlushDb(); // 清空Redis中所有数据
#region Redis_String
#region String_单个添加与读取
// 删除 当前数据库中的所有Key
///client.FlushDb();
// 新增 Key
///client.Set<string>("User", "wxh");
// 获取 指定名称的Key
// 推荐,因为默认帮我们做了反序列化
///client.Get<string>("User");
///Console.WriteLine(client.Get<string>("User"));
// 不建议使用GetValue,因为多了两个双引号,如果使用需要 DeserializeObject 反序列化
///client.GetValue("User");
///Console.WriteLine(JsonConvert.DeserializeObject<string>(client.GetValue("User")));
#endregion
#region String_批量操作
// 批量添加
///Dictionary<string, string> keyValues = new Dictionary<string, string>();
///keyValues.Add("Name", "晓亨");
///keyValues.Add("age", "18");
///client.SetAll(keyValues);
// 批量获取
///IDictionary<string,string> getKeys = client.GetAll<string>(new string[] { "Name", "age" });
///foreach (var item in getKeys)
///{
///Console.WriteLine(item.Value);
///}
#endregion
#region String_设置过期时间
// 10秒后过期
///client.Set<string>("User", "吴晓亨", TimeSpan.FromSeconds(10));
// 等待十秒,十秒后获取值
///Task.Delay(10000).Wait();
// 如果没有数据时,则返回 ""
///Console.WriteLine(client.Get<string>("User"));
// 指定日期过期 在明天的这时过期
client.Set<string>("User", "吴晓亨", DateTime.Now.AddDays(1));
#endregion
#region String_往某个指定 Key 后追加内容
// 往 name 后追加 I,如果找不到则等于新建了name
///client.AppendToValue("name", "I");
///Console.WriteLine(client.Get<string>("name"));
///client.AppendToValue("name", " Love ");
///client.AppendToValue("name", "U");
///Console.WriteLine(client.Get<string>("name"));
#endregion
#region String_获取 Key 原来的值(Value),并附上新值(Value) GetAndSetValue(),取出来的值需要反序列化,要不然是""
///client.Set<string>("Name", "吴晓亨");
// GetAndSetValue 需要 反序列化 JsonConvert.DeserializeObject
///Console.WriteLine(JsonConvert.DeserializeObject(client.GetAndSetValue("Name","新的吴晓亨")));
///Console.WriteLine(client.Get<string>("Name"));
#endregion、
#region Int_自增,自减
// 自增 未新增过Count时,默认从0开始自增
///client.Increment("Count", 1);
///Console.WriteLine(client.Get<int>("Count")); // 1
///client.Increment("Count", 1);
///client.IncrementValueBy("Count", 1); // 意思与 Increment 一致.
/// Console.WriteLine(client.Get<int>("Count")); // 3
// 自减 未新增过Count时,默认从0开始自减
///client.Decrement("Count2", 1);
///Console.WriteLine(client.Get<int>("Count2")); // -1
///client.Decrement("Count2", 1);
///client.Decrement("Count2", 1);
///Console.WriteLine(client.Get<int>("Count2")); // -3
#endregion
#region Add 与 Set 的区别.
// Add 返回 True 则为添加成功.如果数据库中有这个 Key 存在,则返回False,新增失败. 我这抛出异常了
///Console.WriteLine(client.Add<string>("name", "WXH"));
///Console.WriteLine(client.Add<string>("userInfo", "WXH2"));
// Set 返回 True,如果数据库中有这个 Key 存在则将之前的值给替换了.
///Console.WriteLine(client.Set<string>("userInfo", "WXH"));
///Console.WriteLine(client.Set<string>("userInfo", "WXH2"));
#endregion
#region 查看数据库中是否存在这个Key,返回boolean值
///Console.WriteLine(client.ContainsKey("name"));
///Console.WriteLine(client.Get<string>("name"));
#endregion
#region 传入Key 查看是Redis八种类型的那种类型.
/// Console.WriteLine(client.GetEntryType("name")); // string 类型
#endregion
#endregion
#region Redis_HashTable_哈希表
/*
* 注释: Hash表在Redis中存储方式类似于对象,首先有一个大 Key,可以往 大 Key下面, 添加N多个 小Key.
*/
#region Hash_新增(选择性新增)与 获取
// 新增 参数:1.就当成对象名(大Key) 2.小Key名称(属性名) 3.小Key值(属性值)
///client.SetEntryInHash(DaKey, "小key1", "小KeyValue1");
// 获取 参数:1.就当成对象名(大Key) 2.小Key名称(属性名)
///Console.WriteLine(client.GetValueFromHash(DaKey, "小key1"));
// 判断大key中是否存在相同的 K/V,如果存在则返回False并且 新增失败,不存在则返回True 并新增成功.
// SetEntryInHashIfNotExists 参数:1.大Key名称 2.小Key名 3.小Key值
///Console.WriteLine(client.SetEntryInHashIfNotExists(DaKey, "小key1", "小KeyValue1")); // 已经存在 False
///Console.WriteLine(client.SetEntryInHashIfNotExists(DaKey, "小Key2", "小KeyValue2")); // 不存在 true
///Console.WriteLine(client.SetEntryInHashIfNotExists(DaKey, "小Key2", "小KeyValue1")); // Key存在Values不存在 False
#endregion
#region Hash_批量操作
// 批量添加
Dictionary<string, string> dakeys = new Dictionary<string, string>();
dakeys.Add("Key1", "Value1");
dakeys.Add("Key2", "Value2");
dakeys.Add("number", "1");
client.SetRangeInHash(DaKey, dakeys);
// 获取 大Key 下的所有值
///Dictionary<string,string> dics = client.GetAllEntriesFromHash(DaKey);
///foreach (var item in dics)
///{
///Console.WriteLine(item.Key);
/// }
#endregion
#region Hash_操作对象,对象与大KEY平级
/*
模拟场景: 存储用户的信息时,如果使用 String 时,我们需要 首先将对象做一个Json序列化(将对象转为Json),然后存储到Redis的String里面.
但是如果我们需要改变这个对象其中的一个属性值时,我们需要先把这个对象 Json字符串读取出来进行反序列化(将Json转为对象),然后再修改属性值,
修改完成之后,再将用户信息序列化,然后存到Redis的String类型中. 所以我们可以使用 HashTable 直接来操作对象.
PS: 用HashTable储存对象时,对象必须要有ID属性.
*/
//Console.WriteLine("-------------------对象-----------------------");
// 将对象写入到Redis的Hash中
//client.StoreAsHash(new Redis_User_Model() { ID = "01", Age = "18", Name = "吴晓亨" });
// 获取对象 参数:1.属性ID的值
//Console.WriteLine(client.GetFromHash<Redis_User_Model>("01").Name);
//Console.WriteLine(client.GetFromHash<Redis_User_Model>("01").Age);
//Console.WriteLine(client.GetFromHash<Redis_User_Model>("01").ID);
//Console.WriteLine("---------------------------------------------");
#endregion
#region Hash_统计Key,总数量Count,对象不算
//Console.WriteLine(client.GetHashCount(DaKey));
#endregion
#region Hash_读取总Key中的所有小Key与Value
读取所有Key
//List<string> list = client.GetHashKeys(DaKey);
//foreach (var item in list)
//{
// Console.WriteLine(item);
//}
读取所有Values
//List<string> list2 = client.GetHashValues(DaKey);
//foreach (var item in list2)
//{
// Console.WriteLine(item);
//}
#endregion
#region Hash_判断 DaKey 中是否存在指定的Key,返回Boolean
Console.WriteLine(client.HashContainsEntry(DaKey, "Key1")); // True
Console.WriteLine(client.HashContainsEntry(DaKey, "Redis_User_Model")); // False
#endregion
#region Hash_自增_向大Key中的指定key自增Value的值.
Console.WriteLine(client.IncrementValueInHash(DaKey, "number", 5));
#endregion
#endregion
#region Redis_List_集合操作
#region List_三种添加方法
// 顺序添加,如果添加对象需要序列化一下,AddItemToList与PushItemToList 意思一致都是追加.
//Redis_User_Model xiaoLi = new Redis_User_Model() { ID = "01", Name = "李白" };
//client.AddItemToList("List1", JsonConvert.SerializeObject(xiaoLi)); // 参数:1集合名称 2序列化后的字符串
//Redis_User_Model daYe = new Redis_User_Model() { ID = "01", Name = "打野" };
//client.AddItemToList("List1", JsonConvert.SerializeObject(daYe));
//Redis_User_Model shangDan = new Redis_User_Model() { ID = "02", Name = "上单" };
//client.PushItemToList("List1", JsonConvert.SerializeObject(shangDan)); // 参数:1集合名称 2序列化后的字符串
// 插队,插到第一个,来添加
//Redis_User_Model sheShou = new Redis_User_Model() { ID = "02", Name = "射手" };
//client.PrependItemToList("List1", JsonConvert.SerializeObject(sheShou)); // 参数:1集合名称 2序列化后的字符串
#endregion
#region List_设置过期时间与 查看Count集合数量方法
// 设置集合过期时间 五秒过期
//client.ExpireEntryAt("List1", DateTime.Now.AddSeconds(5));
// 查看集合数量 参数:集合名称
//Console.WriteLine(client.GetListCount("List1"));
#endregion
#region List_批量操作
// 批量添加
//client.AddRangeToList("List1", new List<string>() { "01", "02", "03" });
// 从集合的第0个开始读取2个数据 02,03
//List<string> list1 = client.GetRangeFromList("List1", 0, 1);
//foreach (var item in list1)
//{
// Console.WriteLine(item);
//}
#endregion
#region List_栈操作,后进先出
// 移除尾部(最后一个),并返回结果
//Console.WriteLine(client.RemoveEndFromList("List1"));
// 移除头部(第一个),并返回结果
//Console.WriteLine(client.RemoveEndFromList("List1"));
#endregion
#region 扩展操作
//Redis_User_Model xiaoLi = new Redis_User_Model() { ID = "01", Name = "李白" };
//client.AddItemToList("List1", JsonConvert.SerializeObject(xiaoLi));
//Redis_User_Model daYe = new Redis_User_Model() { ID = "02", Name = "打野" };
//client.AddItemToList("List1", JsonConvert.SerializeObject(daYe));
//Redis_User_Model shangDan = new Redis_User_Model() { ID = "03", Name = "上单" };
//client.PushItemToList("List1", JsonConvert.SerializeObject(shangDan));
// 从一个List的尾部移除一个元素,然后将这个数据添加到另一个List的头部,并返回移动的值
//Console.WriteLine(client.PopAndPushItemBetweenLists("List1","List2"));
// 获取Key集合的过期时间
//Console.WriteLine(client.GetTimeToLive("List1"));
// 根据集合的下标来修改值
//client.SetItemInList("List1", 0, "李太白");
#endregion
#region 删除集合
//client.RemoveAllFromList("List1");
#endregion
#endregion
#region Redis_Set自动去重集合
/*
* PS : Set也是集合,只不过是自动去除重复的集合,使用场景例如:投票时,记录用户电脑的IP地址来进行判断用户是否投票,
* 如果使用平常的方式,我们会需要每次都判断,大大降低我们的程序性能,所以可以使用我们的去重集合.
*/
#region Set_添加
// 添加两次,实际只有一个
//client.AddItemToSet("SetList", JsonConvert.SerializeObject(new Redis_User_Model() { ID = "01", Age = "60", Name = "吴晓亨" }));
//client.AddItemToSet("SetList", JsonConvert.SerializeObject(new Redis_User_Model() { ID = "01", Age = "60", Name = "吴晓亨" }));
#endregion
#region Set_批量操作
//client.AddRangeToSet("SetLists", new List<string>() { "01", "02", "03", "01" });
#endregion
#region Set_获取
//foreach (var item in client.GetAllItemsFromSet("SetLists"))
//{
// Console.WriteLine(item);
//}
#endregion
#region Set_删除
// 根据 Set集合 的Value,来删除这个元素.
//client.RemoveItemFromSet("SetLists", "01");
// 将 SetLists 集合的 02 值移除,并将移除的值添加到 ToSetList 集合中
//client.MoveBetweenSets("SetLists","ToSetList", "02");
#endregion
#region Set_交叉并补,查看两个集合里值一样的元素
//client.AddRangeToSet("SetListA", new List<string>() { "01", "02", "03", "04", "05" });
//client.AddRangeToSet("SetListB", new List<string>() { "08", "07", "06", "04", "05" });
// 获取 SetListA 与 SetListB 集合的交集,就是在两个集合中都出现过的元素,并且值一样.
//foreach (var item in client.GetIntersectFromSets("SetListA", "SetListB"))
//{
// Console.WriteLine(item); // 04 05
//}
// 获取 SetListA 与 SetListB 集合的并集,就是这两个集合所组合的一个集合,元素不重复.
//foreach (var item in client.GetUnionFromSets("SetListA", "SetListB"))
//{
// Console.WriteLine(item); // 01 02 03 04 05 06 07 08
//}
#endregion
#region Set_扩展操作
// 统计 SetList 的数量
//Console.WriteLine(client.GetSetCount("SetList"));
// 模拟随机抽奖,随机获取集合里的任意元素的值
//Console.WriteLine(client.GetRandomItemFromSet("SetLists"));
// 可以转为集合来进行一系列操作
//client.GetAllItemsFromSet("SetLists").ToList().FirstOrDefault(a => a == "01");
// 随机删除集合里的元素,并返回被删除的值
//Console.WriteLine(client.PopItemFromSet("SetLists"));
#endregion
#endregion
#region Redis_ZSet有序的去重集合
/*
* ZSet 自动去重,而且多了一个字段 Score 分数的字段,并且从小到大自动排序.
*/
#region ZSet_添加
当 ZSet的Score分数字段列不写时,在排序时则默认为最大
//client.AddItemToSortedSet("ZSetList", "01");
//client.AddItemToSortedSet("ZSetList", "02", 10);
//client.AddItemToSortedSet("ZSetList", "03", 90);
//client.AddItemToSortedSet("ZSetList2", "01");
//client.AddItemToSortedSet("ZSetList2", "02", 10);
//client.AddItemToSortedSet("ZSetList2", "03", 90);
#endregion
#region ZSet_批量操作
// 批量添加
//client.AddRangeToSortedSet("ZSetList",new List<string>() { "11","22"},0);
#endregion
#region ZSet_获取数据
//1. 获取全部操作
client.GetAllItemsFromSortedSet("ZSetList") 从小到大 升序 顺序查询,不返回 Score 分数列
//foreach (var item in client.GetAllItemsFromSortedSet("ZSetList"))
//{
// Console.WriteLine(item);
//}
client.GetAllItemsFromSortedSet("ZSetList") 从大到小 降序 倒序查询,不返回 Score 分数列
//foreach (var item in client.GetAllItemsFromSortedSetDesc("ZSetList"))
//{
// Console.WriteLine(item);
//}
//2. 根据下标获取 顺序 从小到大 , 不返回 Score 分数列
//foreach (var item in client.GetRangeFromSortedSet("ZSetList", 0, 1))
//{
// Console.WriteLine(item);
//}
// 根据下标获取 倒叙 从大到小 , 不返回 Score 分数列
//foreach (var item in client.GetRangeFromSortedSetDesc("ZSetList", 0, 1))
//{
// Console.WriteLine(item);
//}
//3. 根据下标获取,顺序 从小到大 ,返回 Score 分数列
//foreach (var item in client.GetRangeWithScoresFromSortedSet("ZSetList", 0, 1))
//{
// Console.WriteLine(item.Key+"-"+item.Value);
//}
// 根据下标获取 倒叙 从大到小 , 返回 Score 分数列
//foreach (var item in client.GetRangeWithScoresFromSortedSetDesc("ZSetList", 0, 1))
//{
// Console.WriteLine(item.Key+ "-" + item.Value);
//}
#endregion
#region ZSet_求交集_两个ZSet集合中的共同的数据,返回共有的数量,并创建新的ZSet
// 当 ZSet的Score分数字段列不写时,在排序时则默认为最大
client.AddItemToSortedSet("ZSetList", "01");
client.AddItemToSortedSet("ZSetList", "02", 10);
client.AddItemToSortedSet("ZSetList", "03", 90);
client.AddItemToSortedSet("ZSetList2", "01");
client.AddItemToSortedSet("ZSetList2", "02", 10);
client.AddItemToSortedSet("ZSetList2", "03", 90);
Console.WriteLine(
// 求交集,两个集合中都有的数据,来创建第三个集合并将数据存入到新集合里,SCore列会自动将两个集合的SCore值相加.
// 参数:1.新集合名称 2.被求交集的集合们的名称,是个数组
client.StoreIntersectFromSortedSets("ZSetList3", "ZSetList", "ZSetList2")
);
#endregion
#endregion
}
// 等待十六秒
//Task.Delay(16000).Wait();
Console.WriteLine("OK");
Console.ReadLine();
}
}
}
4. 你应该知道的Redis底层数据结构存储方式.
Redis_String在存储数据时的操作概念:
在存储数据时的keyValue如果不超过41字节,则使用RAW编码,超过则使用Embstr编码.
RAW编码:会开辟两次空间,性能较差,使用场景 存储值的时候,值不超过41字节,Redis会默认使用RAW编码
Embstr编码:开辟一次空间,性能较好,使用场景 存储值的时候,值超过41字节,Redis会默认使用Embstr编码,
Embstr编码在开辟空间时,会默认预留一些空间,浪费我们的数据空间与内存,所以比较推荐Hash.Redis_Hash在存储数据时的操作概念:
当大Key(对象)中的小Key(属性)与Value(值)的长度大于64个字节时 或 大Key(对象)下的小Key(属性)的数量超过512个时会使用 HashTable来存储.
HashTable通过Hash算法来计算出下标,查找速度比数组方式快多了.
当大Key(对象)中的小Key(属性)与Value(值)的长度小于64个字节时 或 大Key(对象)下的小Key(属性)的数量未超过512个时会使用 ZipList来存储.
这个限制,我们可以在 Redis.conf 文件中进行修改,如下两列:
hash-max-zipmap-entries 512 hash-max-zipmap-value 64
但不建议更改,因为默认已经一个性能的瓶颈了.
PS:后续持续更新,Redis差的还多呢,这只是基本语法,后续会持续更新,一起加油!