Redis储存类型
在以上的图上可以看出Redis是使用redisObject的对象来表示所有的key和value的,数据类型包括:String,Hash,List,Set,Sort Set编码的方式有Row,int,ht,zipmap,linkedlist,ziplist,intset方式,只有打开了 Redis 的虚拟内存功能,此字段才会真正的分配内存,该功能默认是关闭状态的。
在设计时要注意一下几点:
1、key不要太长,尽量不要超过1024字节,这样不仅消耗内存,也会降低查找的效率
2、Key也不要太短,太短的话,key的可读性会降低
3、在项目中key的设计尽量使用规范的命名规则,如:userId:name:sex等
1-1)、String
A)、常用命令
set:设置key-value 的值
get:获取键为key的值
incr:递加键一次的整数值
decr:递减键一次的整数值
mget:得到所有的给定键的值
B)、实例
redis 127.0.0.1:6379> set baidu www.baidu.com
OK
redis 127.0.0.1:6379> get baidu
"www.baidu.com"
redis 127.0.0.1:6379> append baidu .link
(integer) 18
redis 127.0.0.1:6379> get baidu
"www.baidu.com.link"
redis 127.0.0.1:6379> set version 0
OK
redis 127.0.0.1:6379> incr version
(integer) 1
redis 127.0.0.1:6379> incr version
(integer) 2
redis 127.0.0.1:6379> get version
"2"
redis 127.0.0.1:6379> incrby versions 100
(integer) 100
redis 127.0.0.1:6379> get versions
"100"
redis 127.0.0.1:6379> type baidu
string
redis 127.0.0.1:6379> type version
string
redis 127.0.0.1:6379> rename baidu re-baidu
OK
redis 127.0.0.1:6379> get baidu
(nil)
redis 127.0.0.1:6379> get re-baidu
"www.baidu.com.link"
redis 127.0.0.1:6379> mget baidu
1) (nil)
redis 127.0.0.1:6379>
C)、使用场景
String是最常用的诗句的一种类型,普通的key/value的储存都可以归于此类。
D)、实现方式
String在redis储存的默认的是一个字符串,被reidsobject所引用,当与incr,decr时,就会转化为数值进行计算,此时的redisObject的encoding字段为int
1-2)、Hash
A)、常用命令
hset:HSET key field value 设置对象指定字段的值
hget:HGET key field 获取对象中该field属性域的值
hmset:HMSET key field value [field value ...] 同时设置对象中一个或多个字段的值
hmget:HMGET key field[field...] 获取对象的一个或多个指定字段的值
hgetall:HGETALL key 获取对象的所有属性域和值
hvals:HVALS key 获取对象的所有属性值
hlen:HLEN key 获取对象的所有属性字段的总数
hexists:HEXISTS key field 查看对象是否存在该属性域
hdel:HDEL key field[field...] 删除对象的一个或几个属性域,不存在的属性将被忽略
B)、实例
redis 127.0.0.1:6379> HSET person name jack
(integer) 1
redis 127.0.0.1:6379> HSET peson age 10
(integer) 1
redis 127.0.0.1:6379> HSET person sex famale
(integer) 1
redis 127.0.0.1:6379> HGETALL person
1) "name"
2) "jack"
3) "sex"
4) "famale"
redis 127.0.0.1:6379> HKEYS person
1) "name"
2) "sex"
redis 127.0.0.1:6379> HVALS person
1) "jack"
2) "famale"
redis 127.0.0.1:6379> HDEL person name
(integer) 1
redis 127.0.0.1:6379> HGETALL person
1) "sex"
2) "famale"
redis 127.0.0.1:6379> HMGET person name
1) "jack"
redis 127.0.0.1:6379> HLEN person
(integer) 2
C)、使用场景
Hash一般的储存用户的对象的信息,例如用户的姓名,性别,生日等信息,使用普通的key/value来储存数据。
1-1)、以Key作为储存
方式一的是将ID作为key,其他的作为value封装成对象一序列化的方式来储存,这种方式的缺点是,增加序列化/反序列化的开销,并且在需要时修改其中的一项信息,需要把整个对象取回,并且修改操作需要对并发进行保护,引入CAS等复杂问题。
1-2)、储存key-value匹配的方式
第二种是把用户信息对象有多少成员就存成多少个 key-value 对的形式,用用户 ID 对应属性的名称作为唯一标识来取得对应属性的值,虽然省去了序列化反序列化的开销和并发问题,但是用户 ID 为重复存储,如果存在大量这样的数据,内存浪费还是非常可观的。
1-3)、Redis 储存Hash的方式
Key依然是ID,value是一个map,这个map的key是成员的属性,value是属性值,这样对数据的修改和储存都可以直接通过其内部Map的key(redis内部的key成为field),也就是通过key(id) field(属性的标签)就可以查询出对应的数据了,既不需要重复储存,也不需要序列化与反序列化带来的性能问题,很好的解决了这个问题。
这里需要注意的是,redis提供了接口hgetall会把全部的属性查询出来,这样数据多了会去遍历整个Map,由于redis是单线程的,遍历Map会比较慢,则会影响其他的线程的操作,索引查询时需要注意。
D)、实现方式
Redis Hash 对应的value的内部就是一个HashMap,HashMap的数据量的大小可以分为不用的方式,分为数据量少时会采用类似一维数组的方式来紧凑储存,而不会采用hashMap目的是节省redis的内存的空间,对应的value redisobject的encoding为zipmap,当数据量多时则会自动转化为hashmap此时的encoding为ht。
1-3)、List
A)、常用命令
lst:LSET key index value 在列表中的索引设置一个元素的值
lrange:LRANGE key start stop 从一个列表获取各种元素
rpush:RPUSH key value1 [value2] 添加一个或多个值到列表
rpushx:RPUSHX key value 添加一个值列表,仅当列表中存在
lindex:LINDEX key index 从一个列表其索引获取对应的元素
linsert:LINSERT key BEFORE|AFTER pivot value 在列表中的其他元素之后或之前插入一个元素
llen:LLEN key 获取列表的长度
lpop:LPOP key 获取并取出列表中的第一个元素
lrem:LREM key count value 从列表中删除元素
ltrim:LTRIM key start stop 修剪列表到指定的范围内
B)、实例
redis 127.0.0.1:6379> LPUSH list redis
(integer) 1
redis 127.0.0.1:6379> LPUSH list redis1
(integer) 2
redis 127.0.0.1:6379> LPUSH list hello
(integer) 3
redis 127.0.0.1:6379> LPUSH list word
(integer) 4
redis 127.0.0.1:6379> LLEN list
(integer) 4
redis 127.0.0.1:6379> LRANGE list 0 3
1) "word"
2) "hello"
3) "redis"
4) "redis"
redis 127.0.0.1:6379> LRANGE list 0 5
1) "word"
2) "hello"
3) "redis"
4) "redis"
redis 127.0.0.1:6379> LPOP list
"word"
redis 127.0.0.1:6379> RPOP list
"redis"
redis 127.0.0.1:6379> LTRIM list 0 3
OK
redis 127.0.0.1:6379> LINDEX list 1
"redis"
redis 127.0.0.1:6379>
C)、使用场景
Redis list使用的比较多,由于他是一个消息队列,可以确保先后顺序,不必用mysql那样order by 来排序,利用LRANGE可以很方便的实现分页的功能,也可以实现关注的列表,粉丝列表等都可以用redis的list结构来实现,
D)、实现方式
Redis list 是以双向链表的方式来实现的,既可以支持反向查找和遍历,更方便操作,不过是给内存增加开销,redis内部的很多现实,包括发送缓冲队列等也是用的这个数据结构。
1-4)、Set
A)、常用命令
sunion:SUNION key [key ...] 添加多个set元素
srem:SREM key member [member ...] 从集合里删除一个或多个元素,不存在的元素会被忽略
spop:SPOP key [count] 获取并删除一个集合里面的元素
smove:SMOVE source destination member 移动集合里面的一个key到另一个集合
sinter:SINTER key [key ...] 获得两个集合的交集
sdiff:SDIFF key [key ...] 获得队列不存在的元素
sacrd:SCARD key 获取集合里面的元素数量
sadd:SADD key member [member ...] 添加一个或者多个元素到集合(set)里
sscan:SSCAN key cursor [MATCH pattern] [COUNT count] 迭代set里面的元素
smembers:SMEMBERS key 获取集合里面的所有key
sismember:SISMEMBER key member 确定一个给定的值是一个集合的成员
sdiffstore:SDIFFSTORE destination key [key ...] 获得队列不存在的元素,并存储在一个关键的结果集
B)、实例
redis 127.0.0.1:6379> SADD myset "hello"
(integer) 1
redis 127.0.0.1:6379> SADD myset "word"
(integer) 1
redis 127.0.0.1:6379> SMEMBERS myset
1) "word"
2) "hello"
redis 127.0.0.1:6379> SADD myset "one"
(integer) 1
redis 127.0.0.1:6379> SISMEMBER myset "one"
(integer) 1
redis 127.0.0.1:6379> SISMEMBER myset "two"
(integer) 0
redis 127.0.0.1:6379> sadd friends:leto ghanima paul chani jessica
(integer) 4
redis 127.0.0.1:6379> sadd friends:duncan paul jessica alia
(integer) 3
redis 127.0.0.1:6379> sismember friends:leto jessica
(integer) 1
redis 127.0.0.1:6379> sismember friends:leto vladimir
(integer) 0
redis 127.0.0.1:6379> sinter friends:leto friends:duncan
1) "jessica"
2) "paul"
redis 127.0.0.1:6379> sinterstore friends:leto_duncan friends:leto friends:duncan
(integer) 2
redis 127.0.0.1:6379>
C)、使用场景
Redis set 对外提供的功能与 list 类似是一个列表的功能,特殊之处在于 set 是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set 是一个很好的选择,并且 set 提供了判断某个成员是否在一个 set 集合内的重要接口,这个也是 list 所不能提供的。基本的操作包括添加,删除,交并集等等操作。也可以实现文章的标签,群聊中的成员等。
D)、实现方式
set 的内部实现是一个 value 永远为 null 的 HashMap,实际就是通过计算 hash 的方式来快速排重的,这也是 set 能提供判断一个成员是否在集合内的原因。
1-5)、Sorted Set
A)、常用命令
zadd:ZADD key score1 member1 [score2 member2] 添加一个或多个成员到有序集合,或者如果它已经存在更新其分数
zcard:ZCARD key 得到的有序集合成员的数量
zincrby:ZINCRBY key increment member 在有序集合增加成员的分数
zrange:ZRANGE key start stop [WITHSCORES] 由索引返回一个成员范围的有序集合(从低到高)
zrangebylex:ZRANGEBYLEX key min max [LIMIT offset count]返回一个成员范围的有序集合(由字典范围)
zrangebyscore:ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT] 返回有序集key中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员,有序集成员按 score 值递增(从小到大)次序排列
zrank:ZRANK key member 确定成员的索引中有序集合
zrem:ZREM key member [member ...] 从有序集合中删除一个或多个成员,不存在的成员将被忽略
zscore:ZSCORE key member 获取给定成员相关联的分数在一个有序集合
zscan:ZSCAN key cursor [MATCH pattern] [COUNT count] 增量迭代排序元素集和相关的分数
B)、实例
redis 127.0.0.1:6379> ZADD dbs 100 redis
(integer) 1
redis 127.0.0.1:6379> ZADD dbs 98 mencache
(integer) 1
redis 127.0.0.1:6379> ZADD dbs 99 mongndb
(integer) 1
redis 127.0.0.1:6379> ZADD dbs 99 java
(integer) 1
redis 127.0.0.1:6379> ZCARD dbs
(integer) 4
redis 127.0.0.1:6379> ZCOUNT dbs 10 99
(integer) 3
redis 127.0.0.1:6379> ZRANK dbs java
(integer) 1
redis 127.0.0.1:6379> ZRANK dbs other
(nil)
redis 127.0.0.1:6379> ZRANGEBYSCORE dbs 98 100
1) "mencache"
2) "java"
3) "mongndb"
4) "redis"
C)、使用场景
Redis sorted set 的使用场景与 set 类似,区别是 set 不是自动有序的,而 sorted set 可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。当你需要一个有序的并且不重复的集合列表,那么可以选择 sorted set 数据结构,比如 twitter 的 public timeline 可以以发表时间作为 score 来存储,这样获取时就是自动按时间排好序的。
D)、使用场景
Redis sorted set 的内部使用 HashMap 和跳跃表(SkipList)来保证数据的存储和有序,HashMap 里放的是成员到 score 的映射,而跳跃表里存放的是所有的成员,排序依据是 HashMap 里存的 score,使用跳跃表的结构可以获得比较高的查找效率,并且在实现上比较简单。
Skiplist 详解:http://blog.csdn.net/acceptedxukai/article/details/17333673