Redis数据类型

Posted 王思聪6

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis数据类型相关的知识,希望对你有一定的参考价值。

今天来学习redis的基础数据类型,redis中一共有五种数据类型,分别是:string,hash,list,set,zset。下面分别进行介绍。

一、string(字符串)

字符串类型是redis最基础的数据类型,它能存储任何形式的字符串,包括二进制数据。一个字符串类型允许存储的数据最大容量是512M。字符串是其他4种数据类型的基础。

1 字符串相关命令

1)GET/SET命令

SET key value    #给key赋值
GET key          #获取key的值
127.0.0.1:6379> SET str \'hello,world\'
OK
127.0.0.1:6379> GET str
"hello,world"

当键不存在时返回空值。

2)INCR递增命令

字符串类型可以存储任何形式的字符串,当存储的字符串是整数形式时,可以用incr命令让它自动递增。

INCR key
127.0.0.1:6379> INCR bar
(integer) 2
127.0.0.1:6379> INCR bar
(integer) 3

当键不存在时,会自动创建该键并从0开始递增。

2 字符串实践

1)文章访问量统计

博客常见的一个功能就是统计文章的访问量,这里我们可以为每篇文章使用名为post:id:page.view的键来记录文章的访问量,每次有人访问的时候用incr命令为该键递增。

键的命名规则:对象类型:对象id:对象属性

如:user:1:friends 表示id为1的用户的好友列表,增加易读性和可维护性。

2)生成自增ID

怎么为每篇文章标示一个唯一ID呢?在关系型数据库中可以用auto_increment,但是在redis中可以通过另一种模式实现:对每一类对象使用名为对象类型(复数形式):count的键来存储当前对象类型的数量,没增加一个新对象就对该值进行递增。

3)存储文章数据

文章数据包括标题、内容、发布时间等字段,而我们知道一个字符串类型只能存储一个字符串,那么如何存储文章数据呢?因为字符串类型可以存储二进制数据,可以采用MessagePack进行序列化,速度更快,占用空间更小。下面是伪代码:

 

复制代码
#获得新文章ID
$postid = INCR posts:count 
#将博客文章诸多字段序列化成字符串
$serialpost = serialize($posttitle,$content,$author,$time)
#将序列化后的文章存储到字符串中
SET post:$postid:data = $serialpost

#从redis中读取id=42的文章数据
$serialpost = GET post:42:data
#将文章反序列化成各个字段
$posttitle,$content,$author,$time=unserialize($serialpost)
#获取文章的访问数量
$count = INCR post:42:page.view
复制代码

3 字符串相关扩展命令

1)增加指定整数

INCRBY key increment

127.0.0.1:6379> GET goo
"2"
127.0.0.1:6379> INCRBY goo 3
(integer) 5

2)减少指定整数

复制代码
DECR key
DECRBY key value
127.0.0.1:6379> GET goo
"5"
127.0.0.1:6379> DECR goo
(integer) 4
127.0.0.1:6379> DECRBY goo 2
(integer) 2
复制代码

3)向尾部追加值

APPEND key value,如果该键不存在则将该value设为键的值,返回值是追加后的长度。

127.0.0.1:6379> GET str
"hello,world"
127.0.0.1:6379> APPEND str \',zhao\'
(integer) 16
127.0.0.1:6379> GET str
"hello,world,zhao"

4)获取字符串长度

STRLEN key

127.0.0.1:6379> STRLEN str
(integer) 16

5)同时设置/获取多个键值

MGET/MSET key1 key2 ....

127.0.0.1:6379> MGET str goo bar
1) "hello,world,zhao"
2) "2"
3) "3"

二、hash(散列类型)

 1、简介

hash类型的键值是字典结构,其存储了字段和字段值的映射,但是字段值只能是字符串,不支持其他类型,也就是说,hash类型不支持类型的嵌套,一个hash类型键最多包括2的32次方-1个字段(除了hash,其他类型也不支持类型嵌套)。

hash类型最适合存储对象了,我们一般使用对象类别:ID表示键名,使用字段表示对象的属性,而字段值存储属性值,下面具体例子:要存储电脑ID为5的对象,可以分别使用名为color,name和price的3个字段来存储该电脑的颜色、名称和价格,存储结构如下图所示:

我们回想下如果用关系型数据库来存储该数据,存储结构应该这样:

这种以二维表的形式存储关系型数据,所有的对象都要有相同的属性,试想,如果我想为ID为1的电脑添加一个保修期限,是不是所有的都需要添加这个属性,那么对于其他的就属于冗余了。而采用redis的hash类型可以自由地为不同对象添加不同的字段和属性。

2、相关命令

 1)赋值与取值

HSET key field value
HGET key field
HMSET key field value [field value ...]
HMGET key field [field...]
HGETALL key

HSET用来给字段赋值,它不区分是更新还是插入操作,这意味如果着修改数据时无需考虑该字段是否存在,插入操作返回1,更新返回0;HGET获取字段的值,

复制代码
127.0.0.1:6379> HSET car:1 color red
(integer) 1
127.0.0.1:6379> HSET car:1 name BMW
(integer) 1
127.0.0.1:6379> HSET car:1 price 1200000
(integer) 1
127.0.0.1:6379> HGET car:1 color
"red"
127.0.0.1:6379> HGET car:1 name
"BMW"
127.0.0.1:6379> HGET car:1 price 
"1200000"
127.0.0.1:6379> HGETALL car:1
1) "color"
2) "red"
3) "name"
4) "BMW"
5) "price"
6) "1200000"
127.0.0.1:6379> HSET car:1 color yellow   #该字段已存在,故返回状态0
(integer) 0
复制代码

2)判断字段是否存在

复制代码
HEXISTS key field  #判断字段是否存在,存在返回1,否则返回0,键不存在也返回0
127.0.0.1:6379> hexists car color #键car不存在返回0
(integer) 0
127.0.0.1:6379> hexists car:1 color
(integer) 1
127.0.0.1:6379> hexists car:1 color1 #字段color1不存在返回0
(integer) 0
复制代码

3)当字段不存在时赋值

复制代码
HSETNX key field value#当字段不存在赋值,返回1;如存在不执行任何操作,返回0;属于原子操作,不会发生竞态条件
127.0.0.1:6379> hsetnx car:1 price 150000
(integer) 0
127.0.0.1:6379> hget car:1 price
"1200000"
127.0.0.1:6379> hsetnx car:1 date \'2015\'
(integer) 1
127.0.0.1:6379> hget car:1 date
"2015"
复制代码

4)增加数字

HINCRBY key field increment  #与字符串的incrby相似
127.0.0.1:6379> hincrby car:1 price 1000000
(integer) 2200000

5)删除字段

复制代码
HDEL key field
127.0.0.1:6379> hgetall car:1
1) "color"
2) "yellow"
3) "name"
4) "BMW"
5) "price"
6) "2200000"
7) "date"
8) "2015"
127.0.0.1:6379> hdel car:1 date
(integer) 1
127.0.0.1:6379> hgetall car:1
1) "color"
2) "yellow"
3) "name"
4) "BMW"
5) "price"
6) "2200000"
复制代码

6)只获取字段名或字段值

复制代码
hkeys key
hvals key
127.0.0.1:6379> hkeys car:1  #获取字段名
1) "color"
2) "name"
3) "price"
127.0.0.1:6379> hvals car:1   #获取字段值
1) "yellow"
2) "BMW"
3) "2200000"
复制代码

7)获得字段的数量

hlen key
127.0.0.1:6379> hlen car:1
(integer) 3

三、List(列表类型)

1、介绍

列表可以存储一个有序的字符串列表,常用的操作是向列表两端添加元素,或者获取列表的一个片段。

redis的列表的内部是用双向链表实现的,故向列表两端添加元素的时间复杂度是O(1),获取越靠近两端的数据速度越快。这意味着即使有一个几千万的列表,获取它的头10条或者尾部10数据也是极快的。不过使用链表的代价是通过索引访问列表元素是很慢的,试想:有3000个人在排队买iphone 6s,这时苹果想找到编号为1699的人,工作人员是不是需要从头或者尾部一个一个数过去啊。但无论队伍有多长,新来的顾客直接加入到队尾就可以了,与队长多少没有任何关系。

这种的特性很好的应用在以下场景:如社交网站的新鲜事,我们只关心最新的内容,使用列表存储,即使它有几千万条,获取前100条也是很快的。同时列表也适合记录日志,可以保证加入新日志的速度不受列表的长度影响。

一个列表类型键最多有2的32次方-1个元素。

2、命令操作

1)向列表两端添加元素

复制代码
lpush key value [value ...]#向列表左边添加元素,返回列表个数
rpush key value [value...]#向列表右边添加元素,返回列表个数
127.0.0.1:6379> lpush numbers 0
(integer) 1
127.0.0.1:6379> lpush numbers -1 -2 -3
(integer) 4
127.0.0.1:6379> rpush numbers 1 2 3
(integer) 7
复制代码

2)获取列表片段

复制代码
lrange key start stop  #获取列表一个片段,列表索引是从0开始,包含两边
127.0.0.1:6379> lrange numbers 0 6
1) "-3"
2) "-2"
3) "-1"
4) "0"
5) "1"
6) "2"
7) "3"
#lrange支持负索引,0表示最左元素,-1表示从最右面第一个元素
127.0.0.1:6379> lrange numbers -2 -1 #从最右面开始,取两个
1) "2"
2) "3"
127.0.0.1:6379> lrange numbers 0 -1  #取所有元素
1) "-3"
2) "-2"
3) "-1"
4) "0"
5) "1"
6) "2"
7) "3"
复制代码

3)从列表两端弹出元素

lpop key  #从最左面移除元素,返回被移除的元素值
rpop key  #从最右面移除元素,返回被移除的元素值
127.0.0.1:6379> lpop numbers
"-3"
127.0.0.1:6379> rpop numbers
"3"

4)获取列表中元素的个数

llen key
127.0.0.1:6379> llen numbers
(integer) 5

5)删除列表中指定的值

lrem key count value

删除列表中前count个值为value的元素,返回值是删除的个数,根据count的不同,执行有所差异:

当count=0时,删除列表中所有值是value的元素

当count>0时,删除从最左边开始,前count个值为value的元素

当count<0时,删除从最右边开始,前|count|个值为value的元素

6)获取/设置指定索引的元素值

复制代码
lindex key index
lset key index value
127.0.0.1:6379> lindex numbers 0
"-2"
127.0.0.1:6379> lset numbers 0 1
OK
127.0.0.1:6379> lindex numbers 0
"1"
复制代码

7)只保留列表指定片段的元素(也就是删除指定片段之外所有元素)

复制代码
ltrim key start stop
127.0.0.1:6379> lrange numbers 0 -1
1) "1"
2) "-1"
3) "0"
4) "1"
5) "2"
127.0.0.1:6379> ltrim numbers 1 3
OK
127.0.0.1:6379> lrange numbers 0 -1
1) "-1"
2) "0"
3) "1"
复制代码

这个很有用,与lpush结合可以只显示最新的100个日志数量,如:

lpush logs $logs
ltrim logs 0 99

8)向列表中插入一个元素值

复制代码
linsert key before/after pvior value#从左到右查找值为pvior,然后根据第二个参数决定在前面还是后面插入,返回元素个数
127.0.0.1:6379> lrange numbers 0 -1
1) "-1"
2) "0"
3) "1"
127.0.0.1:6379> linsert numbers before 0 2
(integer) 4
127.0.0.1:6379> lrange numbers 0 -1
1) "-1"
2) "2"
3) "0"
4) "1"
复制代码

9)将元素从一个列表转到另一个列表

rpoplpush source  destinion #先执行source列表的rpop,然后向新列表中lpush,返回元素值,整个过程原子操作

四、set(集合类型)

 1、介绍

集合是一组不重复没有顺序限制的元素,每个集合最多存储2的32次方减1个元素。

集合的常用操作是向集合中加入或删除元素、判断某个元素是否存在。同时不同集合之间还可以进行并集、交集等操作。

2、命令操作

1)增加/删除元素:sadd key member [member...]  /srem key member [member...]

复制代码
127.0.0.1:6379> sadd s1 1 2 3
(integer) 3
127.0.0.1:6379> srem s1 3
(integer) 1
127.0.0.1:6379> smembers s1  #列出集合中所有元素
1) "1"
2) "2"
复制代码

2)判断元素是否在集合中

#判断元素是否在集合中,如果在返回1,否则返回0
sismember key member
127.0.0.1:6379> sismember s1 1
(integer) 1 
127.0.0.1:6379> sismember s1 0
(integer) 0

3)集合间运算

sdiff    key1 key2 [key...]  #差集
sinter   key1 key2 [key...]  #交集
sunion   key1 key2 [key...]  #并集

4)取得集合元素的个数:scard key

5)进行集合运算并将结果存储

sdiffstore    key1 key2 [key...]  #差集
sinterstore   key1 key2 [key...]  #交集
sunionstore   key1 key2 [key...]  #并集

一般用在需要进行多步运算时,可以先把部分结果存储起来再进行下一步运算。

6)随机取得集合元素

srandomember key [count]  #count表示多个元素,根据count值不同取得值也不一样
#当count为正数,取得count个不同的元素
#当count为负数,取得|count|个元素,有可能重复

7)弹出一个元素:spop key 随机选择一个元素弹出

五、zset(有序集合类型)

 1、介绍

有序集合是在集合基础上为每个元素添加一个关联分数,从而通过获得元素分数的高低进行排序,集合元素是不同的,但是关联的分数却可以相同。

有序集合在某些方面跟列表有些相似:

相同点:

  • 二者都是有序的
  • 二者都可以获得某个范围内元素

不同点:

  • 列表是用链表实现的,越靠近两端访问越快,随着元素的增加,访问中间元素变得很慢,这种比较适合如新鲜事、日志类的场景。
  • 有序集合是通过散列或跳跃表实现的,读取中间元素也很快
  • 有序集合更耗费内存
  • 列表不能简单的调整两个元素的位置,但是有序集合可以(通过调整关联的分数)

2、命令操作

1)增加元素

zadd key score member[score member]#如果元素已存在,则用新的分数更新它的分数,返回添加的元素个数
127.0.0.1:6379> zadd sort 50 tom 60 darren 70 helen
(integer) 3

分数不仅支持正数,还支持小数。

2)获得元素的分数

zscore key member
127.0.0.1:6379> zscore sort tom
"50"

3)获得/删除排名在某个范围内的元素

zrange key start stop [withscores]
zrerange key start stop [withscores]

zrange表示先按分数从小到大排序后,取得索引从start到stop之间所有的元素,包括两端。加上withscores表示把分数也取得。

4)获得指定分数范围内的元素

zrangebyscore key min max [withscores] [limit offset count]

表示先按分数从小到大排序后,取得分数从min到max之间所有的元素,包括两端。如果不希望加上两端的话,只需要在前面加上(,withscores表示把分数也取得。min和max还支持无穷大。后面的 [limit offset count]跟sql中功能一样。

5)增加某个元素的分数

zincrby key increment member #返回值是增加后的分数

6)获得有序集合元素个数:zcard key

7)获得指定分数范围内的元素个数:zcount key min max

8)删除有序集合中的元素:zrem key member [member...]

9)按照排名范围删除元素:zremrangebyrank key start stop

10)按照分数范围删除元素:zremrangebyscore key min max

11)获得元素的排名:zrank key member(从小到大) zrevrank key member(从大到小)

redis中五大数据类型到这里就都说完了,有些命令还是多敲敲才能记住。

 

以上是关于Redis数据类型的主要内容,如果未能解决你的问题,请参考以下文章

Redis的列表(List)类型

如何利用redis来进行分布式集群系统的限流设计

Redis的数据类型之String

在代码片段中包含类型转换

Redis五种基本数据类型的典型应用场景

Redis五大数据类型详解