文章目录
Redis - 五种数据类型以及消息订阅
想要使用
Redis
,那对它的数据类型必须了如指掌,它支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。这里我们就来看看这些数据类型是怎样的并且是如何使用的。这里列举的命令指示部分常用命令,完整的大家参考官方文档以及其他资料,这里推荐一个网站Redis 命令参考。
1.String(字符串)
String
是最常用的一种数据类型,普通的key/value存储都可以归为此类。最大能存储 512MB。并且String
是二进制安全的。可包含任何数据。比如jpg图片或者序列化的对象。
1.1 set/get
set:设置key对应的值为String类型的value
get:获取key对应的值
127.0.0.1:6379> set name Tracy
OK
127.0.0.1:6379> get name
"Tracy"
1.2 mget
批量获取多个key的值,如果不存在则返回nil
127.0.0.1:6379> mget name age
1) "Tracy"
2) "20"
127.0.0.1:6379> mget name sex
1) "Tracy"
2) (nil)
1.3 incr && incrby
incr:对key对应的值进行加加操作(+1),并返回新的值;
incrby:加指定值
127.0.0.1:6379> set score 10
OK
127.0.0.1:6379> incr score
(integer) 11
127.0.0.1:6379> incrby score 10
(integer) 21
127.0.0.1:6379> incr name
(error) ERR value is not an integer or out of range
1.4 decr && decrby
decr:对key对应的值进行减减操作(-1),并返回新的值;
decrby:减指定值
127.0.0.1:6379> decr score
(integer) 20
127.0.0.1:6379> decrby score 5
(integer) 15
1.5 setnx
设置key对应的值为String类型的value,如果key已经存在则返回0
127.0.0.1:6379> setnx name Kobe
(integer) 0
127.0.0.1:6379> setnx name1 Kobe
(integer) 1
1.6 setex
设置key对应的值为String类型的value,并设定有效期
127.0.0.1:6379> setex name 60 Tracy
OK
127.0.0.1:6379> ttl name
(integer) 57
127.0.0.1:6379> get name
"Tracy"
1.6 getrange
获取key对应value的子字符串
127.0.0.1:6379> getrange name 0 -1
"TracyMcgrady"
127.0.0.1:6379> getrange name 0 4
"Tracy"
1.7 mset
批量设置多个key的值,如果成功表示所有值都被设置,否则返回0表示没有任何值被设置
127.0.0.1:6379> mset name Kobe age 20
OK
127.0.0.1:6379> get name
"Kobe"
127.0.0.1:6379> get age
"20"
1.8 getset
设置key的值,并返回key旧的值
127.0.0.1:6379> getset name Tracy
"Kobe"
127.0.0.1:6379> get name
"Tracy"
1.9 append
给指定key的value追加字符串,并返回新字符串的长度
127.0.0.1:6379> append name McGrady
(integer) 12
127.0.0.1:6379> get name
"TracyMcGrady"
1.10 应用场景
除此之外还有支持一些其他的命令,大家可以查看官网文档。
String
是最常用的一种数据类型,普通的key/value存储都可以归为此类,value不仅可以存储String类型,也可以存储数字,比如有时我们需要实现某些计数功能,incr命令可以很简单就实现,通过原子递增保持计数。
2.Hash(哈希)
Hash
是一个String类型的field和value(键值对)之间的映射表,Redis
的Hash数据类型的key(hash表名称)对应的value实际的内部存储结构为一个HashMap,特别适合用于存储对象.相对于把一个对象的每个属性存储为String类型,将整个对象存储在Hash类型中会占用更少内存。所存储的成员较少时数据存储为ZipMap
,当成员数量增大时会自动转成真正的HashMap
,此时encoding为ht。
2.1 hset
设置key对应的HashMap中的field的value
127.0.0.1:6379> hset user name Tracy
(integer) 1
127.0.0.1:6379> hset user age 20
(integer) 1
2.2 hget
获取key对应的HashMap中的field的value
127.0.0.1:6379> hget user name
"Tracy"
2.3 hgetall
获取key对应的HashMap中的所有field的value
127.0.0.1:6379> hgetall user
1) "name"
2) "Tracy"
3) "age"
4) "20"
2.4 hgetall
返回key对应的HashMap中的field的数量
127.0.0.1:6379> hlen user
(integer) 2
2.5 应用场景
当我们需要存储一些结构化的数据时,Hash的作用就体现出来了,我们这里用一个用户的信息来举个例子,假设我们需要去存储一个用户的信息,那么我们有几种做法。
第一种就是将整个对象序列化之后存储,当我们在正式业务中对象结构复杂并且设计修改时,需要将Value取出反序列化然后修改之后再序列化放回Redis
,从各方面增加了开销,并且涉及并发时更加容易造成不必要的麻烦。
第二种就是我们通过用户标识以及属性去拼接,一个对象存储N个属性就会产生多少个Key-Value键值对,虽然省去了序列化开销而且避免了并发问题出现,但是用户标识重复存储,当对象属性多或者数据量大时就会造成很多不必要的内存浪费。
这里我们的Hash就登场了,在Hash数据结构中,Key依然是我们的用户标识,但是Value对应的是一个Map而不单纯是一个值。
通过Hash这种数据结构,我们若需要修改某一个用户的某一个属性,那就十分轻松了,也避免了上面出现的问题。我们知道了Hash结构对应Value内部实际就是一个HashMap
,实际Redis
做了两种不同实现,当Hash的成员属性较少时为了节省内存会采用类似一维数组的方式来紧凑存储,并未采用真正的HashMap
结构,对应Value的redisObject此时encoding为ZipMap
,而当成员数量增大时会自动转成真正的HashMap,此时encoding为ht。
3.List(列表)
List
是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部或尾部。
3.1 lpush
在key对应的list的头部添加一个元素
127.0.0.1:6379> lpush names Tracy Kobe Kevin
(integer) 3
3.2 lrange
获取key对应的list的指定下标范围的元素,-1表示获取所有元素
127.0.0.1:6379> lrange names 0 -1
1) "Kevin"
2) "Kobe"
3) "Tracy"
127.0.0.1:6379> lrange names 1 1
1) "Kobe"
3.3 lpop
从key对应的list的尾部删除一个元素,并返回该元素
127.0.0.1:6379> lpop names
"Kevin"
3.4 rpop
从key对应的list的尾部删除一个元素,并返回该元素
127.0.0.1:6379> rpop names
"Tracy"
3.5 rpush
在key对应的list的尾部添加一个元素
127.0.0.1:6379> lrange names 0 -1
1) "Kobe"
127.0.0.1:6379> rpush names Paul
(integer) 2
127.0.0.1:6379> lrange names 0 -1
1) "Kobe"
2) "Paul"
3.6 应用场景
List
应用场景非常多,也是Redis最重要的数据结构之一,比如关注列表,粉丝列表等都可以用Redis的list结构来实现。List就是链表,相信我们基本都接触过,使用List我们可以轻松地实现最新消息排行等功能。List的另一个常用场景就是消息队列,可以利用List的push
操作将任务存在List中,然后再用pop
操作将任务取出执行。List
的实现为一个双向链表,即支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销,Redis内部的很多实现,包括发送缓冲队列等也是使用List
结构。
4.Set(集合)
Set
是String类型的无序集合。集合成员是唯一的,集合中不能出现重复的数据。它是通过哈希表实现的,所以查找、添加、删除的复杂度都是 O(1)。
4.1 sadd
在key对应的set中添加一个元素
127.0.0.1:6379> sadd nameset Kobe
(integer) 1
127.0.0.1:6379> sadd nameset Tracy
(integer) 1
4.2 smembers
获取key对应的set的所有元素
127.0.0.1:6379> smembers nameset
1) "Kobe"
2) "Tracy"
4.3 suion
获取给定key对应的set并集
127.0.0.1:6379> sadd nameset2 Paul
(integer) 1
127.0.0.1:6379> sunion nameset nameset2
1) "Kobe"
2) "Paul"
3) "Tracy"
4.4 sinter
获取给定key对应的set交集
127.0.0.1:6379> sadd nameset3 Kobe
(integer) 1
127.0.0.1:6379> sinter nameset nameset3
1) "Kobe"
4.5 spop
随机返回并删除key对应的set中的一个元素
127.0.0.1:6379> spop nameset
"Tracy"
4.6 scard
返回集合中元素的数量(Key不存在返回0)
127.0.0.1:6379> scard names
(integer) 3
127.0.0.1:6379> scard users
(integer) 0
4.7 应用场景
Set
与List
类似是一个列表的功能,特殊之处在于Set
可以自动排重。当我们需要存储一个不重复的列表数据时,Set
是一个很好的选择,并且Set
还提供了验证某个成员是否在Set集合内的接口,这个也是List
所不能提供的。
Set
集合概念就是一些不重复值的组合。利用该数据结构,可以存储一些集合性的数据,比如各社交应用中,可以将一个用户关注的所有人都存放在一个集合中,或者将其所有粉丝存放在一个集合中。利用集合提供的求交集、并集、差集等操作,可以非常方便地实现如共同关注、可能感兴趣等功能。Set
内部实际上是一个Value全部为null的HashMap,实际就是通过计算hash值的方式来快速排重的。
5.SortedSet(有序集合)
有序集合(SortedSet)
和集合(Set)
一样也是String类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。通过分数来为集合中的成员进行从小到大的排序。Set
是通过HashMap
存储,key对应set的元素,value是空对象。在HashMap
存储为基准上,还加了一层跳跃表:相当于双向链表,在其基础上添加前往比当前元素大的跳转链接,用这种设计也可以更有效率地进行排序和跳转,关于跳表会写一篇博客介绍。
5.1 zadd
在key对应的zset中添加一个元素
127.0.0.1:6379> zadd names 90.0 Kobe
(integer) 1
127.0.0.1:6379> zadd names 88.0 Tracy
(integer) 1
127.0.0.1:6379> zadd names 100 Gosling
(integer) 1
5.2 zrange/zrevrange
zrange:获取key对应的zset中指定范围的元素,-1表示获取所有元素(从小到大)
zrevrange:获取key对应的zset中指定范围的元素,-1表示获取所有元素(从大到小)
127.0.0.1:6379> zrange names 0 -1
1) "Tracy"
2) "Kobe"
3) "Gosling"
127.0.0.1:6379> zrevrange names 0 -1
1) "Gosling"
2) "Kobe"
3) "Tracy"
5.3 zrangebyscore
zrange:返回有序集key中,指定分数范围的元素列表
127.0.0.1:6379> zrangebyscore names 80 90
1) "Tracy"
2) "Kobe"
5.4 zrank/zrevrank
zrank:获取key对应的zset中指定成员(member)的排名。其中member按score值递增(从小到大),排名从0开始
zrevrank:获取key对应的zset中指定成员(member)的排名。其中member按score值递增(从大到小),排名从0开始
127.0.0.1:6379> zrank names Tracy
(integer) 0
127.0.0.1:6379> zrevrank names Tracy
(integer) 2
5.5 zrem
zrange:删除key对应的zset中的一个元素
127.0.0.1:6379> zrem names Gosling
(integer) 1
127.0.0.1:6379> zrange names 0 -1
1) "Tracy"
2) "Kobe"
5.6 应用场景
Sorted Set
的使用场景与set类似,区别是set不是有序的,而Sorted Set可以根据用户提供一个优先级(score)
来为成员排序,并且是插入有序的,即自动排序。当我们需要一个有序并且不重复的集合列表时间,那么选择Sorted Set
这种数据结构就十分有效。Sorted Set
应用场景也十分广泛,例如全班同学成绩排名,value存储同学的学号,score
设置为其考试分数,这样数据插入集合时就已经进行了天然的排序,排行榜类似当日活跃度和用户积分也是这个设计理念。另外还可以用来做带权重的队列,例如普通消息的score
为1,重要消息的score
为2,工作线程便可以根据score
来选择优先执行重要任务。我们还可以score
设置为过期时间的时间戳,这样就可以简单地通过过期时间排序,定时清除过期数据了。
6.Pub/Sub(消息发布订阅)
发布订阅类似于消息管道,用来进行系统之间消息解耦,我们可以设定对某个key值
(channel频道)
进行消息发布及消息订阅,当一个channel
上进行了消息发布后,所有订阅它的客户端都会收到相应的消息。这就类似于rabbitmq、activemq、rocketmq、kafka
等。
6.1 subscribe
订阅指定频道的信息
127.0.0.1:6379> subscribe channel_task
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel_task"
3) (integer) 1
6.2 unsubscribe
取消订阅指定的频道。若不指定则取消订阅所有的频道。
127.0.0.1:6379> unsubscribe channel_task
1) "unsubscribe"
2) "channel_task"
3) (integer) 0
6.3 publish
将信息message发送到指定的频道channel。返回收到消息的客户端数量。
127.0.0.1:6379> publish channel_task add_user_score
(integer) 1 (此时有一个订阅该频道的客户端收到信息)
6.4 应用场景
消息发布订阅的模式可以有很多应用场景,例如订单交易成功后会员系统加积分、货物系统出货等操作都可以用该思路去完成,还有平时普通的即时聊天功能也可以通过该功能实现。但是
Redis
的发布订阅和其他MQ比起来功能更加薄弱,但是也更加轻量。同时由于Redis
消息持久化可能会导致数据可靠性比较差,并且没有相比其他MQ提供的丰富后台功能,所以在平时使用消息队列时更多的会选择其他产品。