ASP.NETc#Redis开发
Redis简介
Redis是⼀个开源的,使⽤C语⾔编写,⾯向“键/值”对类型数据的分布式NoSQL数据库系统,特点是⾼性能,持久存储,适应⾼并发的应⽤场景。Redis纯粹为应⽤⽽产⽣,它是⼀个⾼性能的key-value数据库,并且提供了多种语⾔的API
性能测试结果表⽰SET操作每秒钟可达110000次,GET操作每秒81000次(当然不同的服务器配置性能不同)。
Redis⽬前提供五种数据类型:string(字符串),list(链表), Hash(哈希),set(集合)及zset(sorted set) (有序集合)
Redis开发维护很活跃,虽然它是⼀个Key-Value数据库存储系统,但它本⾝⽀持MQ功能,所以完全可以当做⼀个轻量级的队列服务来使⽤。对于RabbitMQ和Redis的⼊队和出队操作,各执⾏100万次,每10万次记录⼀次执⾏时间。测试数据分为128Bytes、512Bytes、1K和10K四个不同⼤⼩的数据。实验表明:⼊队时,当数据⽐较⼩时Redis的性能要⾼于RabbitMQ,⽽如果数据⼤⼩超过了10K,Redis 则慢的⽆法忍受;出队时,⽆论数据⼤⼩,Redis都表现出⾮常好的性能,⽽RabbitMQ的出队性能则远低于Redis。
Redis与Memcached的⽐较
1.Memcached是多线程,⽽Redis使⽤单线程.
2.Memcached使⽤预分配的内存池的⽅式,Redis使⽤现场申请内存的⽅式来存储数据,并且可以配置虚拟内存。
3.Redis可以实现持久化,主从复制,实现故障恢复。
4.Memcached只是简单的key与value,但是Redis⽀持数据类型⽐较多。
Redis的存储分为内存存储、磁盘存储 .从这⼀点,也说明了Redis与Memcached是有区别的。Redis 与Memcached⼀样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写⼊磁盘或者把修改 操作写⼊追加的记录⽂件,并且在此基础上实现了master-slave(主从)同步。
Redis有两种存储⽅式,默认是snapshot⽅式,实现⽅法是定时将内存的快照(snapshot)持久化到硬盘,这种⽅法缺点是持久化之后如果出现crash则会丢失⼀段数据。因此在完美主义者的推动下作者增加了aof⽅式。aof即append only mode,在写⼊内存数据的同时将操作命令保存到⽇志⽂件,在⼀个并发更改上万的系统中,命令⽇志是⼀个⾮常庞⼤的数据,管理维护成本⾮常⾼,恢复重建时间会⾮常长,这样导致失去aof⾼可⽤性本意。另外更重要的是Redis是⼀个内存数据结构模型,所有的优势
都是建⽴在对内存复杂数据结构⾼效的原⼦操作上,这样就看出aof是⼀个⾮常不协调的部分。
其实aof⽬的主要是数据可靠性及⾼可⽤性.
Redis安装
将服务程序拷贝到⼀个磁盘上的⽬录,如下图:
⽂件说明:
<:服务程序
<:本地数据库检查
<:更新⽇志检查
<:性能测试,⽤以模拟同时由N个客户端发送M个 SETs/GETs 查询.
<: 服务端开启后,我们的客户端就可以输⼊各种命令测试了
1、打开⼀个cmd窗⼝,使⽤cd命令切换到指定⽬录(F:\Redis)运⾏ f
2、重新打开⼀个cmd窗⼝,使⽤cd命令切换到指定⽬录(F:\Redis)运⾏ -h 127.0.0.1 -p 6379,其中 127.0.0.1是本地ip,6379是redis服务端的默认端⼝ (这样可以开启⼀个客户端程序进⾏特殊指令的测试).
如果你的电脑是64bit系统,可以下载
安装完成Redis服务后,我们会在计算机的服务⾥⾯看到
然后启动此服务。
接下来在使⽤Redis时,还需要下载C#驱动(也就是C#开发库),如下图:
Redis常⽤数据类型
使⽤Redis,我们不⽤在⾯对功能单调的数据库时,把精⼒放在如何把⼤象放进冰箱这样的问题上,⽽是利⽤Redis灵活多变的数据结构和数据操作,为不同的⼤象构建不同的冰箱。
Redis最为常⽤的数据类型主要有以下五种:
String
Hash
List
Set
Sorted set
String类型
String是最常⽤的⼀种数据类型,普通的key/value存储都可以归为此类 。⼀个Key对应⼀个Value,string类型是⼆进制安全的。Redis的string可以包含任何数据,⽐如jpg图⽚(⽣成⼆进制)或者序列化的对象。基本操作如下:
var client = new RedisClient("127.0.0.1", 6379);
client.Set<int>("pwd", 1111);
int pwd=client.Get<int>("pwd");
Console.WriteLine(pwd);
UserInfo userInfo = new UserInfo() { UserName = "zhangsan", UserPwd = "1111" };//</span>(底层使⽤json序列化 )
client.Set<UserInfo>("userInfo", userInfo);
redis支持的五种数据类型UserInfo user=client.Get<UserInfo>("userInfo");
Console.WriteLine(user.UserName);
List<UserInfo> list = new List<UserInfo>() { new UserInfo(){UserName="lisi",UserPwd="111"},new UserInfo(){UserName="wangwu",UserPwd="123"} }; client.Set<List<UserInfo>>("list",list);
List<UserInfo>userInfoList=client.Get<List<UserInfo>>("list");
foreach (UserInfo userInfo in userInfoList)
{
Console.WriteLine(userInfo.UserName);
}
Hash类型
Hash是⼀个string 类型的field和value的映射表。hash特别适合存储对象。相对于将对象的每个字段存成单个string 类型。⼀个对象存储在hash类型中会占⽤更少的内存,并且可以更⽅便的存取整个对象。
作为⼀个key value存在,很多开发者⾃然的使⽤set/get⽅式来使⽤Redis,实际上这并不是最优化的使⽤⽅法。尤其在未启⽤VM情况下,Redis全部数据需要放⼊内存,节约内存尤其重要.
增加了序列化/反序列化的开销,并且在需要修改其中⼀项信息时,需要把整个对象取回
Redis为单进程单线程模式,采⽤队列模式将并发访问变为串⾏访问。Redis本⾝没有锁的概念,Redis对于多个客户端连接并不存在竞
争 .. redis是个单线程的程序,为什么会这么快呢 ?
1、⼤量线程导致的线程切换开销
2、锁、
3、⾮必要的内存拷贝。
4. Redis多样的数据结构,每种结构只做⾃⼰爱做的事.
Hash对应的Value内部实际就是⼀个HashMap,实际这⾥会有2种不同实现,这个HashMap的成员⽐
较少时,Redis为了节省内存会采⽤类似⼀维数组的⽅式来紧凑存储,⽽不会采⽤真正的HashMap结构,当成员量增⼤时会⾃动转成真正的HashMap.
Key仍然是⽤户ID, value是⼀个Map,这个Map的key是成员的属性名,value是属性值,这样对数据的修改和存取都可以直接通过其内部Map的Key(Redis⾥称内部Map的key为field), 也就是通过 key(⽤户ID) + field(属性标签) 就可以操作对应属性数据了,既不需要重复存储数据,也不会带来序列化和反序列化
client.SetEntryInHash("user", "userInfo", "aaaaaaaaaa");
List<string> list = client.GetHashKeys("user");
List<string> list = client.GetHashValues("userInfo");//获取值
List<string> list = client.GetAllKeys();//获取所有的key。
Redis为不同数据类型分别提供了⼀组参数来控制内存使⽤,我们在前⾯提到过的Redis Hash的value内部是⼀个
HashMap,如果该Map的成员⽐较少,则会采⽤⼀维数组的⽅式来紧凑存储该MAP,省去了⼤量指针的内存开销,这个参数在redis,conf 配置⽂件中下⾯2项。
Hash-max-zipmap-entries 64
Hash-max-zipmap-value 512.
含义是当value这个Map内部不超过多少个成员时会采⽤线性紧凑格式存储,默认是64,即value内部有64个以下的成员就是使⽤线性紧凑存储,超过该值⾃动转成真正的HashMap.
Hash-max-zipmap-value含义是当value这个MAP内部的每个成员值长度不超过多少字节就会采⽤线性紧凑存储来节省空间。以上两个条件任意⼀个条件超过设置值都会转成真正的HashMap,也就不会再节省内存了,这个值设置多少需要权衡,HashMap的优势就是查和操作时间短。
⼀个key可对应多个field,⼀个field对应⼀个value
这⾥同时需要注意,Redis提供了接⼝(hgetall)可以直接取到全部的属性数据,但是如果内部Map的成
员很多,那么涉及到遍历整个内部Map 的操作,由于Redis单线程模型的缘故,这个遍历操作可能会⽐较耗时,⽽另其它客户端的请求完全不响应,这点需要格外注意
建议使⽤对象类别和ID构成键名,使⽤字段表⽰对象属性,字
段值存储属性值,例如:car:2 price 500
List类型
list是⼀个链表结构,主要功能是push,pop,获取⼀个范围的所有的值等,操作中key理解为链表名字。 Redis的list类型其实就是⼀个每个⼦元素都是string类型的双向链表。我们可以通过push,pop操作从链表的头部或者尾部添加删除元素,这样list既可以作为栈,⼜可以作为队列。Redis list的实现为⼀个双向链表,即可以⽀持反向查和遍历,更⽅便操作,不过带来了部分额外的内存开销,Redis内部的很多实现,包括发送缓冲队列等也都是⽤的这个数据结构
//队列使⽤
client.EnqueueItemOnList("name", "zhangsan");
client.EnqueueItemOnList("name", "lisi");
int count= client.GetListCount("name");
for (int i = 0; i < count; i++)
{
Console.WriteLine(client.DequeueItemFromList("name"));
}
//栈使⽤
client.PushItemToList("name2", "wangwu");
client.PushItemToList("name2", "maliu");
int count = client.GetListCount("name2");
for (int i = 0; i < count; i++)
{
Console.WriteLine(client.PopItemFromList("name2"));
}
Set类型
它是string类型的⽆序集合。set是通过hash table实现的,添加,删除和查,对集合我们可以取并集,交集,差集.
//对Set类型进⾏操作
client.AddItemToSet("a3", "ddd");
client.AddItemToSet("a3", "ccc");
client.AddItemToSet("a3", "tttt");
client.AddItemToSet("a3", "sssh");
client.AddItemToSet("a3", "hhhh");
System.Collections.Generic.HashSet<string>hashset=client.GetAllItemsFromSet("a3");
foreach (string str in hashset)
{
Console.WriteLine(str);
}
//求并集
client.AddItemToSet("a3", "ddd");
client.AddItemToSet("a3", "ccc");
client.AddItemToSet("a3", "tttt");
client.AddItemToSet("a3", "sssh");
client.AddItemToSet("a3", "hhhh");
client.AddItemToSet("a4", "hhhh");
client.AddItemToSet("a4", "h777");
System.Collections.Generic.HashSet<string>hashset= client.GetUnionFromSets(new string[] { "a3","a4"});
foreach (string str in hashset)
{
Console.WriteLine(str);
}
//求交集
System.Collections.Generic.HashSet<string> hashset = client.GetIntersectFromSets(new string[] { “a3”, “a4” });
//求差集.
System.Collections.Generic.HashSet<string> hashset = client.GetDifferencesFromSet("a3",new strin
g[] { "a4"});
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论