redis提供了5种数据结构,理解每次数据结构的特点在开发和运维中是很重要的。使用redis版本:3.0.7。
一、全局命令
redis有5种数据结构,它们是键值对中的值,对于键来说是一些通用命令。
查看所有的键
keys pattern
例:
键的总数
dbsize
dbsize命令会返回当前数据库中所有键的总数。
例:
检查键是否存在
exists key
检查key是否存在,存在返回1,不存在0。
删除键
del key [...]
del是通用命令,无论任何数据结构类型,del都可以将其删除。
例:
返回结果为删除key的个数,如果删除一个不存在的键,会返回0。
键过期
expire key seconds
redis支持对键添加过期时间,当超过时间后键会被自动删除。
ttl命令会返回键剩余的过期时间,它有三个返回值:
- 大于或等于0的整数,键的剩余时间。
- -1:键未设置过期时间。
- -2:键不存在。
键的数据类型
type key
例如:key的数据类型为字符串类型,返回结果为string,键set为集合类型返回结果为set。
二、数据结构和内部编码
type命令时间返回的是当前键的数据结构类型,他们分别是string(字符串),hash(哈希),list(列表),set(集合),zset(有序集合)。
这些只是redis对外的数据类型。
每种数据类型都有自己的内部编码实现,而且是多种编码,这样redis会在适合的场景选择适合的内部编码。
可以通过object encoding命令查询内部编码:
redis的5种数据结构:
redis数据结构与内部编码:
字符串
字符串类型是redis最基础的数据类型,其他几种数据结构都是在字符串基础上建立的。字符串的值可以是字符串、数字、二进制,值最大不可以超过512mb。
命令
设置值
set命令:为键设置值
set key value
setex命令:为键设置值,并设置秒级的过期时间。
setex key value seconds
setpx命令:为键设置值,并设置毫秒秒级的过期时间。
setpx key value millisecond
setnx命令:向键设置值前判断键是否存在,不存在则设置值存在则不设置。
setnx key value
setxx命令:与setnx相反,向键设置值前判断键是否存在,存在则设置值存在则不设置。
setxx key value
setnx和setxx的使用场景:由于redis是单线程命令处理机制,如果多个客户端同时向redis服务使用setnx命令也只会有一个客户端执行成功。setnx可以用于分布式锁的一个解决方案。
计数
incr命令:用于对键的值进行自增操作,返回结果为三种:
- 值不是整数,返回错误。
- 值是整数,返回自增后的结果。
- 值不存在,按照值为0自增,返回1。
例:
exists key
(integer) 0
incr key
(integer) 1
incrby命令:用于对键的值进行指定自增量的自增操作,返回结果与incr相同。
例:
exists key
(integer) 0
incrby key 10
(integer) 10
incrbyfloat命令:用于对键的值进行指定浮点型自增量的自增操作。
decr命令:用于对键的值进行自减操作。
decrby命令:用于对键的值进行指定值进行减值操作。
append命令:用于对键的值进行追加操作,返回追加后的值的得长度。
例:
set hello w
OK
append hello orld
(integer) 5
get hello
"world"
strlen命令:用于查看键的值得长度。
例:
set hello world
OK
strlen hello
(integer) 5
setrange命令:用于键的值内指定索引字符的替换,如果参数是多个字符则从指定位置开始逐个替换,返回替换后值的长度。
例:
set hello world
OK
setrange hello 0 helloworld
(integer) 10
getrange命令:用于获取键的值指定索引的内容。
getrange key start end
例:
set hello world
OK
getrange hello 0 2
"wor"
内部编码
字符串类型的内部编码有3种:
- int:8个字节长度的长整形。
- embstr:小于等于39个字节的字符串。
- raw:大于39个字节的字符串。
整数类型示例:
set key 6379
OK
object encoding key
"int"
短字符串示例:
set key hello,word
OK
object encoding key
"embstr"
长字符串示例:
set key "one string greater than 39 char............."
OK
object encoding key
"raw"
strlen key
(integer) 44
哈希
哈希类型是指键值本身又是一个键值对结构,例如:value={{field1,value1},....,{fieldN,valueN}}。哈希类型中的映射关系叫做field-value,这里的value是指field对应的值,不是键对应的值。
命令
设置值
hset命令
hset key field value
例:
hset user:1 name username
(integer) 1
hset user:1 name codger
(integer) 0
如果设置不存在的field则响应1,存在则响应0。此外还提供了hsetnx命令,它们的关系就像set命令与setnx命令一样,只不过作用域转移到field。
获取值
hget命令
hget key field
例:
hget user:2 name
(nil)
hget user:1 name
"codger"
获取user:1的name属性对应的值,不存在则返回nil。
删除field
hdel key field [field1...]
例:
hdel user:1 name
(integer) 1
hdel命令可以删除一个或多个field,返回结果为删除成功的field数量。
计算field数量
hlen key
例:
hlen user:1
(integer) 1
hlen命令,可以获取键对应的hash结构值的field数量。
批量设置或获取field-value
hmget命令:批量获取键对应的hash结构值的多个field的值。
hmget key field [field...]
hmset命令:批量设置键对应的hash结构值的多个field。
例:
判断field是否存在
hexists key field
例:
hexists user:1 gender
(integer) 1
user:1是否包含gender域,包含则返回1不包含则返回。
获取所有field
hkeys key
hkeys命令返回指定哈希键所有的field。
获取所有value
hvals key
hvals命令返回指定哈希键所有的value。
例:
获取所有的field-value
hgetall key
例:
hstrlen命令:获取value字符串的长度(需要redis3.2以上)
内部编码
内部编码 | 编码描述 |
---|---|
ziplist(压缩列表) | 当哈希类型元素个数小于hash-max-ziplist-entries配置(默认为512个)、同时所有值都小于hash-max-ziplist-value配置(默认为64字节)时,redis会使用ziplist作为哈希的内部实现,ziplist使用更加紧凑的结构实现多个元素的连续存储,所以在节省内存方面比hashtable更优秀。 |
hashtable(哈希表) | 当哈希类型无法满足ziplist的条件时,redis会使用hashtable作为哈希的内部实现,因为此时ziplist无法保证读写效率。 |
列表
列表(list)类型是用来存储多个字符串,如下图A,B,C,D四个元素从左到右组成一个有序的集合.列表中的每个字符串被称为元素(element),一个列表最多可以存储(2的32次方)-1个元素.在redis中,可以对列表两端插入(push)和弹出(pop),还可以获取指定范围的元素列表、获取指定所有下标的元素等。
列表类型有两个特点:
- 列表中的元素是有序的,这就意味着可以通过索引下标获取某个元素或者某个范围内的元素列表。
- 列表中的元素可以是重复的。
命令
(1) 插入命令
① 从右边插入元素
rpush key value [value...]
例:向列表插入a,b,c三个元素。
lrange key 0 -1命令可以获取列表中所有的元素。
② 从左边插入元素
使用方法与rpush一样,从左侧插入。
lpush key value [value....]
例:
(2) 查询命令
① 查询指定范围内的元素列表
lrange key start end
lrange操作会获取列表指定索引范围所有的元素.索引下标有两个特点:第一,索引下标从左到右分别是0到N-1,但是从右到左分别是-1到-N。第二,lrange中的end选项包含了自身。
例:
② 获取列表指定索引下的元素
lindex key index
例:
③ 获取列表长度
llen key
例:
(3) 删除命令
① 从列表左侧或右侧弹出元素。
如下操作是将列表最左侧与右侧的元素弹出来。
② 删除指定元素
lrem key count value
lrem命令会从列表中找到等于value的元素进行删除,根据count的不同分为三种:
count>0,从列表中删除指定数量(count)的元素。
count<0,从列表中删除count绝对值数量的元素。
count=0,删除所有。
例:
指定元素数量
所有元素
③ 按照索引范围修剪列表
ltrim key start end
例如,下面操作会保留列表第二个到第六个元素:
(4) 修改命令
修改指定索引下标的元素:
lset key index value
例:
修改第一个元素的值为c。
(5) 阻塞操作
阻塞弹出
blpop key [key...] timeout
brpop key [key...] timeout
blpop与brpop命令是lpop和rpop命令的阻塞版本,他除了弹出方向不同,使用方法基本相同,所以下面以brpop命令进行说明,brpop命令包含两个参数:
- key [key...]:多个列表的键。
- timeout:阻塞时间(单位为秒)。
① 列表为空:如果timeout等于3,那么客户端等到三秒后返回,如果timeout=0,那么客户端将一直阻塞,直到弹出成功。
② 列表不为空:客户端会立刻返回。
在使用阻塞弹出命令时,有两点需要注意:
- 如果是多个键,那么会从左到右遍历键,一旦有一个键能弹出元素客户端就会立刻返回。
- 如果多个客户端同时对一个键进行操作,那么最先执行命令的客户端可以获取到值。
内部编码
列表类型的内部编码有两种:
编码名 | 编码描述 |
---|---|
ziplist(压缩列表) | 当列表的元素个数大于list-max-ziplist-entries配置(默认为512个),同时列表中每个元素的长度小于list-max-ziplist-value配置(默认为64字节)。 |
linkedlist(链表) | 当列表的长度或值得大小不满足ziplist的要求,redis会采用linkedlist为列表的内部实现编码。 |
使用场景
① 消息队列:redis的lpush-brpop命令组合即可实现阻塞队列,生产者客户端使用lpush命令向列表插入元素.消费者客户端使用brpop命令阻塞式的"抢"列表中的尾部元素.多个客户端保证消息的负载均衡与可用性。
② 文章列表:每个用户都有属于自己的文章列表.此时可以考虑使用列表,因为列表不但是有序的,同时支持使用lrange按照索引范围获取多个元素。
伪代码:
var articles = redis.lrange('user:1:acticles',0,9);
articles.forEach(function(){
// 遍历操作
});
开发提示::列表的使用场景有很多如: lpush+lpop=Stack(栈)、lpush+rpop=queue(队列)、lpush+brpop=message queue(消息队列)、lpush+ltrim=Capped Collection(有限集合)
集合
集合(set)类型也是用来保存多个的字符串元素,但和列表不同的是:它的元素是无序且不可重复的,不能通过索引获取元素.如下图,集合user:1:follows中包含着"his"、"it"、"sports"、"music"四个元素,一个集合最多可以存储(2的32次方-1)个元素。
命令
(1) 集合内操作
① 添加元素
sadd key value [value...]
返回结果为添加成功的元素数量。
例:
② 删除元素
srem key value [value...]
返回结果为删除成功的元素数量。
例:
③ 获取元素个数
scard key
例:
④ 判断元素是否在集合中
sismember key value
如果元素存在于集合内则返回1,反之返回0。
例:
⑤ 随机从集合中返回指定个数元素
srandmember key [count]
[count]是可选参数,如果不写默认为1。
例:
⑥ 从集合中随机弹出元素
spop key
pop操作可以从集合中随机弹出一个元素。
例:
使用spop命令后,集合元素变为"1","3".
⑦ 获取集合的所有元素
smembers key
获取集合所有元素,且返回结果是无序的。
(2) 集合间操作
① 求多个集合的交集
sinter key [key...]
例:
② 求多个集合的并集
sunion key [key...]
例:
③ 求多个集合的差集
sdiff key [key...]
例:
④ 将交集、并集、差集的结果保存
sinterstore storeKey key [key...]
sunionstore storeKey key [key...]
sdiffstore storeKey key [key...]
集合间的运算在元素比较多的情况下会比较耗时,所以redis提供了上面三个命令(原命令+store)将集合间交集、并集、差集的结果保存到storeKey中,例如将user:1:follows和user:2:follows两个集合之间的交集结果保存到user:1_2:follows中。
内部编码
集合类型的内部编码有两种:
编码名 | 编码描述 |
---|---|
intset(整数集合) | 当集合中的元素全是整数,且长度不超过set-max-intset-entries(默认为512个)时,redis会选用intset作为内部编码。 |
hashtable(哈希表) | 当集合无法满足intset的条件时,redis会使用hashtable作为内部编码。 |
使用场景
集合类型比较典型的使用场景是标签(tag)。例如一个用户可能对音乐感兴趣,另一个用户对新闻感兴趣,这些的区别就是标签.有了这些数据就可以获得喜欢同一个标签的人,以及用户的共同喜好的标签,这些数据对于用户体验来说比较重要。
下面使用集合类型实现标签功能的若干功能。例如:
(1) 给用户添加标签
(2) 给标签添加用户
(3) 删除用户的标签
(4) 删除标签的用户
第(3)和第(4)最好在同一个事务下进行处理。
(5) 计算用户共同拥有的标签
有序集合
有序集合相对于哈希、列表、集合来说会有一点陌生,但既然叫有序集合。那么它和集合必然是有着联系,它保留了集合不能重复元素的特性.但不同的是,有序集合是可排序的.但是他和列表使用索引下标进行排序依据不同的是,它给每个元素设置一个分数(score)作为排序的依据。
列表、集合、有序集合的异同点
数据结构 | 是否允许重复元素 | 是否有序 | 有序实现方式 | 应用场景 |
---|---|---|---|---|
集合 | 否 | 否 | 无 | 标签、社交等. |
有序集合 | 否 | 是 | 分值 | 排行榜、社交等. |
列表 | 是 | 是 | 索引下标 | 时间轴、消息队列等 |
命令
(1) 集合内
① 添加成员
zadd key score member [score member ...]
下面操作向有序集合user:ranking增加用户Rico和他的分数60分:
有关zadd命令有两点需要注意:
Redis 3.2为zadd命令添加了nx、xx、ch、incr四个选项:
- nx:member必须不存在,才可以设置成功,用于添加。
- xx:member必须存在,才可以设置成功,用于添加。
- ch:返回此次操作后,有序结合元素和分数发生变化的个数。
- incr: 对score进行添加操作,相当于后面介绍的zincrby。
有序集合相比集合提供了排序字段,但是也产生了代价,zadd的时间复杂度是O(log(n)),sadd的时间复杂度为O(1)。
② 获取成员个数
zcard key
例:
③ 获取某个成员的分数
zscore key member
例:
④ 获取成员排名
zrank key member
zrevrank key member
zrank命令是从分数从低到高返回排名,zrevrank反之,排名从0开始。例如下面:
⑤ 删除成员
zrem key member [member...]
下面操作成员ann从有序集合user:ranking中删除。
⑥ 增加成员分数
zincrby key score member
下面操作给Rico成员添加9分,分数变为69分。
⑦ 获取指定范围的元素
zrange key start end [withscores]
zrevrange key start end [withscores]
有序集合是按照分值排名的,zrange是由低到高返回,zrevrange反之,查询全部:zrange user:ranking 0 -1,加上withscores参数显示分数。
例:获取前三名
127.0.0.1:6379> zrange user:ranking 0 2
1) "codger"
2) "hank"
3) "ann"
127.0.0.1:6379> zrevrange user:ranking 0 2
1) "tom"
2) "Rico"
3) "ann"
⑧ 返回指定分数范围的成员
zrangebyscore key min max [withscores] [limit offset count]
zrevrangebyscore key min max [withscores] [limit offset count]
例:返回分数在0到100的成员。
127.0.0.1:6379> zrangebyscore user:ranking 0 100
1) "codger"
2) "hank"
3) "ann"
4) "Rico"
5) "tom"
返回分数在0到无限大的成员。
127.0.0.1:6379> zrangebyscore user:ranking 0 +inf
1) "codger"
2) "hank"
3) "ann"
4) "Rico"
5) "tom"
同时min和max还支持开区间(小括号)和闭区间(中括号),-inf和+inf分别代表无限小和无限大:
127.0.0.1:6379> zrangebyscore user:ranking (10 +inf withscores
1) "hank"
2) "15"
3) "ann"
4) "30"
5) "Rico"
6) "69"
7) "tom"
8) "80"
⑨ 返回指定分数范围成员个数
zcount key min max
下面返回分数为10到15的成员:
127.0.0.1:6379> zcount user:ranking (10 15
(integer) 1
⑩ 删除指定排名内的升序元素
zremrangebyrank key start end
删除第start到第end名的成员:
127.0.0.1:6379> zremrangebyrank user:ranking 0 1
(integer) 2
⑪ 删除指定分数范围的成员
zremrangebyscore key min max
删除分数从min到max的成员:
127.0.0.1:6379> zremrangebyscore user:ranking 0 30
(integer) 1
(2) 集合间的操作
① 交集
zinterstore storeKey keyNum key [key ...] [weights weight [weight...]] [aggregate sum|min|max]
参数说明:
- storeKey:交集计算结果保存到这个键下。
- keyNum:需要做交集的键的个数。
- key[key ...]:需要做交集的键。
- weights weight [weight...]:每个键的权重,在做交集计算时,每个键中的每个member的分值会和这个权重相乘,每个键的权重默认为1。
- aggregate sum|min|sum:计算成员交集后,分值可以按照sum(和)、min(最小值)、max(最大值)做汇总.默认值为sum。
127.0.0.1:6379> zrange user:ranking:2 0 -1 withscores
1) "Rico"
2) "138"
3) "tom"
4) "160"
127.0.0.1:6379> zinterstore user:ranking:1_2 2 user:ranking user:ranking:2 aggregate min
(integer) 2
127.0.0.1:6379> zrange user:ranking:1_2 0 -1
1) "Rico"
2) "tom"
127.0.0.1:6379> zrange user:ranking:1_2 0 -1 withscores
1) "Rico"
2) "69"
3) "tom"
4) "80"
② 并集
zunionstore storeKey keyNum key [key...] [weights weight [weight...]] [aggregate sum|min|max]
该命令的所有参数和zinterstore是一致的,只不过做的是并集计算。
例:
127.0.0.1:6379> zunionstore user:ranking:1_2 2 user:ranking user:ranking:2 aggregate min
(integer) 3
127.0.0.1:6379> zrange user:ranking:1_2 0 -1 withscores
1) "Rico"
2) "69"
3) "codger"
4) "90"
5) "tom"
6) "160"
内部编码
编码名称 | 编码描述 |
---|---|
ziplist(压缩列表) | 当有序集合的元素小于zset-max-ziplist-entries配置(默认是128个),同时每个元素的值都小于zset-max-ziplist-value(默认是64字节)时,Redis会用ziplist来作为有序集合的内部编码实现,ziplist可以有效的减少内存的使用 |
skiplist(跳跃表) | 当ziplist的条件不满足时,有序集合将使用skiplist作为内部编码的实现,来解决此时ziplist造成的读写效率下降的问题. |