redis是使用内存存储(in-memory)的非关系型数据库,可以存储key和五种不同类型的value之间的映射。
特点:数据类型多存取速度快,支持持久化,高可用分布式, 主从复制(master/slave replication), 脚本(存储过程,stored procedure)
1. String
定义:是二进制的。可以存储序列化对象,图片,字符串,数值等
方法名称 | 说明 |
---|---|
set/get/del | 设置值 set key value, 取值 get key,删值del key |
mset/mget | 设置多个键值 mset key value key2 value2, 取多个键值 mget key key2 |
getset | 将给定 key 的值设为 value ,并返回 key 的旧值 getset key value |
setnx | key 不存在进行设置,存在不设置返回 0 setnx key value |
msetnx | 设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在,存在不设置返回 0 |
setex | 设置key有效期为 10 秒,10 秒后返回nil setex key 10 value |
setrange | 用 value 参数覆写给定 key 所储存的字符串值,从偏移量 offset 开始。 setrange key offset value |
incr/decr | 对value数字值进行增1/减1 incr key |
incrby/decrby | 对value数字值进行指定长度的递增/递减 incrby key 10 |
append | key 经存在并且是一个字符串,命令将指定的 value 追加到该 key 原来值value的末尾 |
strlen | 返回 key 所储存的字符串值的长度。 |
比如想知道什么时候封锁一个IP地址(访问超过几次)。INCRBY命令让这些变得很容易,通过原子递增保持计数。
实现方式:
2. Hash
定义:是String类型的field和value的映射表,或者说一个String集合。它的特别适合存储对象,相比较而言,将一个对象类型存储在Hash类型里要比存储在String类型里占用更少的内存空间,并方便存取整个对象。
方法名称 | 说明 |
---|---|
hset/hget/hdel | 存值 hset key field value,取值 hget key field,删值 hdel key field |
hmset/hmget | 存多个 hmset key field1 value1 field2 value2,取多个 hmget key field1 field2 |
hsetnx | 设置值当 field 不存在时 hsenx key field value |
hincrby | 字段指定的数字值加N hincrby key field 10 |
hexists | 查看哈希表 key 中,指定的字段是否存在。 hexists key field |
hlen | 返回hash里的字段的数量 |
hkeys | 返回hash里所有的字段 |
hvals | 返回hash的所有value |
hgetall | 返回hash里所有field和value |
如果用普通的key/value结构来存储,主要有以下2种存储方式:
第一种方式将用户ID作为查找key,把其他信息封装成一个对象以序列化的方式存储,
如:set u001 “李三,18,20010101”
这种方式的缺点是,增加了序列化/反序列化的开销,并且在需要修改其中一项信息时,需要把整个对象取回,并且修改操作需要对并发进行保护,引入CAS等复杂问题。
第二种方法是这个用户信息对象有多少成员就存成多少个key-value对儿,用用户ID+对应属性的名称作为唯一标识来取得对应属性的值,
如:mset user:001:name “李三 “user:001:age18 user:001:birthday “20010101”
虽然省去了序列化开销和并发问题,但是用户ID为重复存储,如果存在大量这样的数据,内存浪费还是非常可观的。
key(用户ID) + field(属性标签) 操作对应属性数据了,既不需要重复存储数据,也不会带来序列化和并发修改控制的问题。很好的解决了问题。
同时需要注意,Redis提供了接口(hgetall)可以直接取到全部的属性数据,但是如果内部Map的成员很多,那么涉及到遍历整个内部Map的操作,由于Redis单线程模型的缘故,这个遍历操作可能会比较耗时,而另其它客户端的请求完全不响应,这点需要格外注意。
实现方式:
3. List
定义:是一个双向链表的结构,我们可以通过相关操作进行集合的头部或者尾部添加删除元素,list的设计非常简单精巧,即可以做为栈,又可以作为队列
方法名称 | 说明 |
---|---|
lpush/rpush/lpop/rpop/lindex | 先进后出/先进先出/ 移出并获取列表的第一个元素/ 移除并获取列表最后一个元素 /通过索引获取列表元素 |
lrem | 移除值为 value 的元素 count > 0: 从头往尾移除值为 value 的元素,数量为 count; count < 0: 从尾往头移除值为 value 的元素,数量为 count; count = 0: 移除所有值为 value 的元素 |
blpop | 阻塞等待 移出并获取列表的第一个元素 blpop key timeout |
lset | 设置 index 位置值 LSET key index value |
linsert | 在列表的元素前或者后插入元素 LINSERT key BEFORE|AFTER pivot value |
lrange | 获取列表指定范围内的元素 |
ltrim | 让列表只保留指定区间内的元素 LTRIM key start stop |
llen | 获取列表的长度 |
rpoppush | 移除列表的最后一个元素,并将该元素添加到另一个列表并返回 RPOPLPUSH source destination |
我们可以轻松地实现最新消息排行等功能。
Lists的另一个应用就是消息队列,可以利用Lists的PUSH操作,将任务存在Lists中,然后工作线程再用POP操作将任务取出进行执行。
RPOPLPUSH source destination 将列表 source 中的最后一个元素(尾元素)弹出,并返回给客户端。
将 source 弹出的元素插入到列表 destination ,作为 destination 列表的的头元素。
为什么要阻塞版本的blpop呢,主要是为了避免轮询。举个简单的例子如果我们用list来实现一个工作队列。执行任务的thread可以调用阻塞版本的pop去获取任务这样就可以避免轮询去检查是否有任务存在。当任务来时候工作线程可以立即返回,也可以避免轮询带来的延迟。
4. Set
定义:是string类型的无序集合,对集合我们可以取交集、并集、差集;通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。
命令 | 说明 |
---|---|
sadd/srem | 添加 sadd key mems;删除 srem key mems |
spop | 随机返回删除的元素 |
sdiff | 返回一个集合与给定集合的差集的元素 SDIFF [key …] |
sdiffstore | 类似sdiff,但无返回值而是放入目标集合中 SDIFFSTORE destination key [key …] |
sinter | 返回指定所有的集合的成员的交集 SINTER key [key …] |
sinterstore | 类似sinter,但无返回值而是放入目标集合中 SINTERSTORE destination key [key …] |
sunion | 返回给定的多个集合的并集中的所有成员 SUNION key [key …] |
sunionstore | 类似sunion,但不返回结果集,而是存储在目标集合中 SUNIONSTORE destination key [key …] |
smove | 将 mem 元素从 source 集合移动到 destination 集合 smove source destination member |
scard | 获取集合元素数量 SCARD key |
sismember | 返回成员 member 是否是集合 key 的成员 SISMEMBER key member |
smembers | 返回所有成员 smembers key |
set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。
set 的内部实现是一个 value永远为null的HashMap,实际就是通过计算hash的方式来快速排重的,这也是set能提供判断一个成员是否在集合内的原因。
5. Zset
定义: 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。有序集合的成员是唯一的,但分数(score)却可以重复。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
命令 | 说明 |
---|---|
zadd | 向有序集合添加一个或多个成员,存在则更新已存在成员的分数 |
zrem | 删除名称为key的zset中的元素member |
zincrby | 为有序集key的成员member的score值加上增量increment |
zrangebyscore | 返回key的有序集合中的分数在min和max之间的所有元素. 按score升序 |
zremrangebyrank | 移除有序集key中,指定排名(rank)区间start和stop内的所有成员 |
zrank | 返回有序集key中成员member的排名,从小到大 |
zrevrank | 返回有序集key中成员member的排名,score从大到小 |
count | 返回有序集key中,score值在min和max之间(默认包括score值等于min或max)的成员个数 |
smove | 将 mem 元素从 source 集合移动到 destination 集合 smove source destination member |
scard | 获取集合元素数量 SCARD key |
sismember | 返回成员 member 是否是集合 key 的成员 SISMEMBER key member |
smembers | 返回所有成员 smembers key |
ZADD key [NX|XX][CH] [INCR] score member [score member …]
XX: 仅仅更新存在的成员,不添加新成员。
NX: 不更新存在的成员。只添加新成员
CH: 修改返回值为发生变化的成员总数,原始是返回新添加成员的总数
INCR: 当ZADD指定这个选项时,成员的操作就等同ZINCRBY命令,对成员的分数进行递增操作
以某个条件为权重,比如按顶的次数排序. 需要精准设定过期时间的应用
实现方式 Redis sorted set的内部使用HashMap和跳跃表(SkipList)来保证数据的存储和有序,HashMap里放的是成员到score的映射,而跳跃表里存放的是所有的成员,排序依据是HashMap里存的score,使用跳跃表的结构可以获得比较高的查找效率,并且在实现上比较简单。