redis是一种高级的key:value存储系统,其中value支持五种数据类型:
1.字符串(String)
2.字符串列表(List)
3.字符串集合(Set)
4.有序字符串集合(Zset)
5.哈希(Hash)
而关于key,有几个点要提醒大家:
1.key不要太长,尽量不要超过1024字节,这不仅消耗内存,而且会降低查找的效率;
2.key也不要太短,太短的话,key的可读性会降低;
3.在一个项目中,key最好使用统一的命名模式,例如user:10000:passwd。
一、String
1、key/ value 存储
2、既可以完全实现目前 Memcached 的功能,并且效率更高;
3、可以享受Redis的定时持久化,操作日志及 Replication等功能。除了提供与 Memcached 一样的get、set、incr、decr 等操作外,Redis还提供了下面一些操作:
获取字符串长度
往字符串append内容
设置和获取字符串的某一段内容
设置及获取字符串的某一位(bit)
批量设置一系列字符串的内容
4、应用场景:
统计网站访问数量、当前在线人数、微博数、粉丝数等,incr命令(++操作)
5、实现方式:
String在redis内部存储默认就是一个字符串,被redisObject所引用,当遇到incr,decr等操作时会转成数值型进行计算,此时redisObject的encoding字段为int。因为是二进制安全的,所以可以用来存储图片。
二、List
1、双向链表 有序 可重复
也就是说对于一个具有上百万个元素的lists来说,在头部和尾部插入一个新元素,其时间复杂度是常数级别的,比如用LPUSH在10个元素的lists头部插入新元素,和在上千万元素的lists头部插入新元素的速度应该是相同的。所以,list相比数组的优点是:定点 删除/插入元素 ,有序,最多影响给定元素的左右两个节点,但在查找时需要一个个查找;而数组以索引的形式进行存储,在查找上更快,但存储时,会移动后面数据的顺序。
2、应用:
2.1.我们可以利用lists来实现一个消息队列,而且可以确保先后顺序,不必像MySQL那样还需要通过ORDER BY来进行排序。
2.2.利用LRANGE还可以很方便的实现分页的功能。
2.3.在博客系统中,每个博文的评论也可以存入一个单独的list中。
3、实现方式:
Redis list的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销,Redis内部的很多实现,包括发送缓冲队列等也都是用的这个数据结构。
三、Set
1、String 无序 不重复 集合
2、应用场景:
QQ有一个社交功能叫做“好友标签”,大家可以给你的好友贴标签,比如“大美女”、“土豪”、“欧巴”等等,这时就可以使用redis的集合来实现,把每一个用户的标签都存储在一个集合之中。
Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。
3、实现方式:
set 的内部实现是一个 value永远为null的HashMap,实际就是通过计算hash的方式来快速排重的,这也是set能提供判断一个成员是否在集合内的原因。
四、Zset
1、String 有序 不重复 集合
在sort的基础上,使用double类型的score来确定顺序
2、应用场景:
2.1当你需要一个有序的并且不重复的集合列表,那么可以选择Zset数据结构;
2.2自定义有序集合
比如twitter 的public timeline可以 以发表时间作为score来存储,这样获取时就是自动按时间排好序的。
3、实现方式:
Redis Zset的内部使用HashMap和跳跃表(SkipList)来保证数据的存储和有序,HashMap里放的是成员到score的映射,而跳跃表里存放的是所有的成员,排序依据是HashMap里存的score,使用跳跃表的结构可以获得比较高的查找效率,并且在实现上比较简单。
五、Hash
1、string field/value
2、应用场景:
适合用于存储对象,例如存储、读取、修改用户属性(name,age,pwd等)
比如,我们要存储一个用户信息对象数据,包含以下信息:
用户ID为查找的key,存储的value用户对象包含姓名,年龄,生日等信息,如果用普通的key/value结构来存储,主要有以下2种存储方式:
第一种方式将用户ID作为查找key,把其他信息封装成一个对象以序列化的方式存储,这种方式的缺点是,增加了序列化/反序列化的开销,并且在需要修改其中一项信息时,需要把整个对象取回,并且修改操作需要对并发进行保护,引入CAS等复杂问题。
第二种方法是这个用户信息对象有多少成员就存成多少个key-value对儿,用用户ID+对应属性的名称作为唯一标识来取得对应属性的值,虽然省去了序列化开销和并发问题,但是用户ID为重复存储,如果存在大量这样的数据,内存浪费还是非常可观的。
那么Redis提供的Hash很好的解决了这个问题,Redis的Hash实际是内部存储的Value为一个HashMap,并提供了直接存取这个Map成员的接口,
也就是说,Key仍然是用户ID, value是一个Map,这个Map的key是成员的属性名,value是属性值,这样对数据的修改和存取都可以直接通过其内部Map的Key(Redis里称内部Map的key为field), 也就是通过 key(用户ID) + field(属性标签) 就可以操作对应属性数据了,既不需要重复存储数据,也不会带来序列化和并发修改控制的问题。很好的解决了问题。
这里同时需要注意,Redis提供了接口(hgetall)可以直接取到全部的属性数据,但是如果内部Map的成员很多,那么涉及到遍历整个内部Map的操作,由于Redis单线程模型的缘故,这个遍历操作可能会比较耗时,而另其它客户端的请求完全不响应,这点需要格外注意。
3、实现方式
redis的Hash数据类型的value内部是一个HashMap,如果该Map的成员比较少,则会采用一维数组的方式来紧凑存储该Map,省去了大量指针的内存开销,这个参数在redis.conf配置文件中下面2项。
Hash-max-zipmap-entries 64 Hash-max-zipmap-value 512
1
2
Hash-max-zipmap-entries含义:是当value这个Map内部不超过64个成员时会采用线性紧凑格式存储(默认是64),即value内部有64个以下的成员就是使用线性紧凑存储,超过该值自动转成真正的HashMap。
Hash-max-zipmap-value含义:是当value这个Map内部的每个成员值长度不超过多少字节就会采用线性紧凑存储来节省空间。
以上两个条件任意一个条件超过设置值都会转成真正的HashMap,也就不会再节省内存了,这个值设置多少需要权衡,HashMap的优势就是查找和操作效率高。