Redis 简介
Redis 是一个开源的,使用 C 语言编写,面向“键 / 值”对类型数据的分布式 NoSQL 数据库系统,特点是高性能,持久存储,适应高并发的应用场景。 Redis 纯粹为应用而产生,它是一个高性能的 key-value 数据库 , 并且提供了多种语言的 API
性能测试结果表示 SET 操作每秒钟可达 110000 次, GET 操作每秒 81000 次(当然不同的服务器配置性能不同)。
redis 目前提供五种数据类型: string( 字符串 ),list (链表) , Hash (哈希) ,set (集合)及 zset(sorted set) (有序集合)
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 安装
文章的最后我提供了下载包,当然你也可以去官网下载最新版本的Redis https://github.com/dmajkic/redis/downloads
将服务程序拷贝到一个磁盘上的目录,如下图:
文件说明:
redis-server.exe :服务程序
redis-check-dump.exe :本地数据库检查
redis-check-aof.exe :更新日志检查
redis-benchmark.exe :性能测试,用以模拟同时由 N 个客户端发送 M 个 SETs/GETs 查询 .
redis-cli.exe : 服务端开启后,我们的客户端就可以输入各种命令测试了
1 、打开一个 cmd 窗口,使用 cd 命令切换到指定目录( F:\Redis )运行 redis-server.exe redis.conf
2 、重新打开一个 cmd 窗口,使用 cd 命令切换到指定目录( F:\Redis )运行 redis-cli.exe -h 127.0.0.1 -p 6379 ,其中 127.0.0.1 是本地 ip , 6379 是 redis 服务端的默认端口 (这样可以开启一个客户端程序进行特殊指令的测试) .
可以将此服务设置为 windows 系统服务,下载 Redis 服务安装软件,安装即可。( https://github.com/rgl/redis/downloads
)
安装完成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 style= "font-family:宋体;"> //</span>(底层使用json序列化 ) client.Set<UserInfo>( "userInfo", userInfo); 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 string[] { "a4"}); 返回存在于第一个集合,但是不存在于其他集合的数据。差集
Sorted Set 类型
sorted set 是 set 的一个升级版本,它在 set 的基础上增加了一个顺序的属性,这一属性在添加修改 . 元素的时候可以指定,每次指定后, zset( 表示有序集合 ) 会自动重新按新的值调整顺序。可以理解为有列的表,一列存 value, 一列存顺序。操作中 key 理解为 zset 的名字 .
Redis sorted set 的使用场景与 set 类似,区别是 set 不是自动有序的,而 sorted set 可以通过用户额外提供一个优先级 (score) 的参数来为成员排序,并且是插入有序的,即自动排序。当你需要一个有序的并且不重复的集合列表,那么可以选择 sorted set 数据结构,
client.AddItemToSortedSet( "a5", "ffff"); client.AddItemToSortedSet( "a5", "bbbb"); client.AddItemToSortedSet( "a5", "gggg"); client.AddItemToSortedSet( "a5", "cccc"); client.AddItemToSortedSet( "a5", "waaa"); System.Collections.Generic.List< string> list =client.GetAllItemsFromSortedSet( "a5"); foreach ( string str in list) { Console.WriteLine(str); }
Redis 工具和所需资料代码全下载,地址: http://pan.baidu.com/s/1c0ChL5m