Redis系列-第三篇Redis数据类型

1.全局命令

1.1查看所有键

127.0.0.1:6379> set k1 111
OK
127.0.0.1:6379> set k2 222
OK
127.0.0.1:6379> set k3 333
OK
127.0.0.1:6379> keys *
1) "k2"
2) "k1"
3) "k3"

keys *命令会将所有的键输出,如上所示。

1.2 键总数

dbsize命令会返回当前数据库中键的总数 .

127.0.0.1:6379> dbsize
(integer) 3

dbsize命令在计算键总数时不会遍历所有键, 而是直接获取Redis内置的键总数变量, 所以dbsize命令的时间复杂度是O(1) 。 而keys命令会遍历所有键, 所以它的时间复杂度是O(n) , 当Redis保存了大量键时, 线上环境禁止使用

1.3.检查键是否存在

EXISTS key [key …]

127.0.0.1:6379> EXISTS k1
(integer) 1
127.0.0.1:6379> EXISTS k4
(integer) 0

如上结果所示:键存在会返回1,不存在会返回0

1.4 删除键

DEL key [key …]

del是一个通用命令, 无论值是什么数据结构类型, del命令都可以将其删除 。

如下演示,删除k1,然后执行检测k1结果是0不存在。

127.0.0.1:6379> DEL k1
(integer) 1
127.0.0.1:6379> EXISTS k1
(integer) 0

1.5 键过期

EXPIRE key seconds

ttl命令会返回键的剩余过期时间, 它有3种返回值:

  • 大于等于0的整数: 键剩余的过期时间。
  • -1: 键没设置过期时间。
  • -2: 键不存在
127.0.0.1:6379> set k1 11111
OK
#k1 设置10s过期时间
127.0.0.1:6379> EXPIRE k1 10
(integer) 1
127.0.0.1:6379> ttl k1
(integer) 6
127.0.0.1:6379> ttl k1
(integer) 4
127.0.0.1:6379> ttl k1
(integer) 2
127.0.0.1:6379> ttl k1
(integer) -2

1.6 键类型

TYPE key

127.0.0.1:6379> set k1 111
OK
127.0.0.1:6379> type k1
string
127.0.0.1:6379> LPUSH l1 1 2 3 4
(integer) 4
127.0.0.1:6379> type l1
list

如果键不存在, 则返回none:

127.0.0.1:6379> TYPE k2
none

2.String类型

字符串类型是Redis最基础的数据结构。 首先键都是字符串类型, 而且其他几种数据结构都是在字符串类型基础上构建的, 所以字符串类型能为其他四种数据结构的学习奠定基础。 字符串类型的值实际可以是字符串(简单的字符串、 复杂的字符串(例如JSON、 XML) ) 、 数字(整数、 浮点数) , 甚至是二进制(图片、 音频、 视频) , 但是值最大不能超过512MB

2.1命令

设置值

SET key value [EX seconds] [PX milliseconds] [NX|XX]

参数选项:

ex seconds: 为键设置秒级过期时间。
px milliseconds: 为键设置毫秒级过期时间。
nx: 键必须不存在, 才可以设置成功, 用于添加。
xx: 与nx相反, 键必须存在, 才可以设置成功, 用于更新。

如:

127.0.0.1:6379> SET h1 1111
OK

简化set操作,设置值并设置过期时间,SETEX key seconds value (三个参数一个)

127.0.0.1:6379> SETEX h2 2222
(error) ERR wrong number of arguments for 'setex' command
127.0.0.1:6379> SETEX h2 10 2222
OK

扩展:基于set命令设置的复杂性,redis提供了setxx 和setnx两个命令用于简化操作。

setnx和setxx在实际使用中有什么应用场景吗? 以setnx命令为例子, 由于Redis的单线程命令处理机制, 如果有多个客户端同时执行setnx key value,根据setnx的特性只有一个客户端能设置成功, setnx可以作为分布式锁的一种实现方案, Redis官方给出了使用setnx实现分布式锁的方法: http://redis.io/topics/distlock。

SETNX key value

设置key,只有key不存在才会设置成功;如下演示k1已经存在setnx通过设置不成功,k2不存在则可以设置

127.0.0.1:6379> KEYS *
1) "h1"
2) "l1"
3) "k1"
127.0.0.1:6379> SETNX k1 4444
(integer) 0
127.0.0.1:6379> SETNX k2 5555
(integer) 1

获取键值

GET key

127.0.0.1:6379> GET h1
"1111"

如果要获取的键不存在, 则返回nil(空)

127.0.0.1:6379> GET K4
(nil)

批量设置键

MSET key value [key value …]

127.0.0.1:6379> MSET k3 333 k4 444 k5 555
OK
127.0.0.1:6379> keys *
1) "h1"
2) "k4"
3) "l1"
4) "k5"
5) "k3"
6) "k1"
7) "k2"

批量获取键

MGET key [key …]

127.0.0.1:6379> MGET k3 k4 k5
1) "333"
2) "444"
3) "555"

批量操作命令可以有效提高开发效率, 假如没有mget这样的命令, 要执行n次get命令 具体耗时如下:

n次get时间 = n次网络时间 + n次命令时间

使用mget命令后, 要执行n次get命令操 具体耗时如下:

n次get时间 = 1次网络时间 + n次命令时间

使用批量操作, 有助于提高业务处理效率, 但是要注意的是每次批量操作所发送的命令数不是无节制的, 如果数量过多可能造成Redis阻塞或者网络拥塞。

计数

INCR key

incr命令用于对值做自增操作, 返回结果分为三种情况:

  • 值不是整数, 返回错误。
  • 值是整数, 返回自增后的结果。
  • 键不存在, 按照值为0自增, 返回结果为1。

除了incr命令, Redis提供了decr(自减) 、 incrby(自增指定数字) 、decrby(自减指定数字) 、 incrbyfloat(自增浮点数)

其他不常用命令

向尾部追加值

APPEND key value

APPEND命令,向键值的末尾追加value。如果键不存在则将该键的值设置为value,即相当于 SET key value。返回值是追加后字符串的总长度。

127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> set k1 hello
OK
127.0.0.1:6379> APPEND k1 world
(integer) 10
127.0.0.1:6379> APPEND k1 redis
(integer) 15
127.0.0.1:6379> get k1
"helloworldredis"
获取字符串长度

STRLEN key

STRLEN命令,返回键值的长度,如果键不存在则返回0。

127.0.0.1:6379> get k1
"helloworldredis"
127.0.0.1:6379> STRLEN k1
(integer) 15

2.2 应用场景

缓存

计数器(自增主键)

3.Hash类型

3.1使用String类型的问题

​ 假设存储User对象,它有id,username、password、age、name等属性,需要转成JSON格式存储到Redis中,存储的过程如下:

User对象->json(string)->redis

如果在业务上只是更新age属性,其他的属性并不做更新我应该怎么做呢?如果仍然采用上边的方法在传输、处理时会造成资源浪费。

3.2Hash介绍

hash叫散列类型,它提供了字段和字段值的映射。字段值只能是字符串类型,不支持散列类型、集合类型等其它类型。如下:

在这里插入图片描述

哈希类型中的映射关系叫作field-value, 注意这里的value是指field对应的值, 不是键对应的值.

3.3命令

设置值

HSET key field value

127.0.0.1:6379> HSET  user name  zhangsan
(integer) 1
127.0.0.1:6379> HSET  user age  28
(integer) 1
127.0.0.1:6379> HSET  user sex  man
(integer) 1

取值

HGET key field

127.0.0.1:6379> HGET user  name
"zhangsan"
127.0.0.1:6379> HGET user  age
"28"
127.0.0.1:6379> HGET user  sex
"man"

如果键或field不存在, 会返回nil

127.0.0.1:6379> HGET user  phone
(nil)

删除Field

HDEL key field [field …]

hdel会删除一个或多个field, 返回结果为成功删除field的个数

127.0.0.1:6379> HDEL user sex  
(integer) 1
127.0.0.1:6379> HDEL user name age
(integer) 2

计算Field个数

HLEN key

127.0.0.1:6379> HLEN user
(integer) 0
127.0.0.1:6379> HSET user name  1
(integer) 1
127.0.0.1:6379> HSET user age  2
(integer) 1
127.0.0.1:6379> HSET user phone 1306666666
(integer) 1
127.0.0.1:6379> HLEN user
(integer) 3

批量获取值

HMGET key field [field …]

127.0.0.1:6379> HMGET user name age phone
1) "1"
2) "2"
3) "1306666666"
127.0.0.1:6379> HMGET user name age sex
1) "1"
2) "2"
3) (nil)

如果某个field不存在,则会返回nil

批量设置值

HMSET key field value [field value …]

127.0.0.1:6379> HMSET user1  name lisi age 28 phone 12345
OK

判断field是否存在

HEXISTS key field

127.0.0.1:6379> HEXISTS user1 name
(integer) 1
127.0.0.1:6379> HEXISTS user1 sex
(integer) 0

存在返回1,不存在返回0

获取所有field

HKEYS key

127.0.0.1:6379> HKEYS user1
1) "name"
2) "age"
3) "phone"

获取所有field对应的value

HVALS key

127.0.0.1:6379> HVALS user1
1) "lisi"
2) "28"
3) "12345"

获取所有的field-value

HGETALL key

127.0.0.1:6379> HGETALL user1
1) "name"
2) "lisi"
3) "age"
4) "28"
5) "phone"
6) "12345"

在使用hgetall时, 如果哈希元素个数比较多, 会存在阻塞Redis的可能。如果开发人员只需要获取部分field, 可以使用hmget, 如果一定要获取全部field-value, 可以使用hscan命令, 该命令会渐进式遍历哈希类型.

3.4使用场景

存储用户信息或商品信息

4.List类型

4.1ArrayList与LinkedList的区别

​ ArrayList使用数组方式存储数据,所以根据索引查询数据速度快,而新增或者删除元素时需要设计到位移操作,所以比较慢。

​ LinkedList使用双向链表方式存储数据,每个元素都记录前后元素的指针,所以插入、删除数据时只是更改前后元素的指针指向即可,速度非常快。然后通过下标查询元素时需要从头开始索引,所以比较慢,但是如果查询前几个元素或后几个元素速度比较快。

在这里插入图片描述

在这里插入图片描述

4.2List介绍

​ Redis的列表类型(list)可以存储一个有序的字符串列表,常用的操作是向列表两端添加元素(push)和弹出(pop),或者获得列表的某一个片段

​ 列表类型内部是使用双向链表(double linked list)实现的,所以向列表两端添加元素的时间复杂度为0(1),获取越接近两端的元素速度就越快。这意味着即使是一个有几千万个元素的列表,获取头部或尾部的10条记录也是极快的。

​ 列表是一种比较灵活的数据结构, 它可以充当栈和队列的角色, 在实际开发上有很多应用场景

​ 列表类型有两个特点: 第一、 列表中的元素是有序的, 这就意味着可以通过索引下标获取某个元素或者某个范围内的元素列表.第二、 列表中的元素可以是重复的。

4.3命令

操作类型 命令
添加 rpush lpush linsert
lrange lindex llen
删除 lpop rpop lrem ltrim
修改 lset
阻塞 blpop brpop

添加

  • 从左边插入元素

LPUSH key value [value …]

127.0.0.1:6379> LPUSH list1 1 2 3 4 5 6
(integer) 6

lrange 0-1 命令可以从右到左获取列表的所有元素

127.0.0.1:6379> LRANGE list1 0 -1
1) "6"
2) "5"
3) "4"
4) "3"
5) "2"
6) "1"
  • 从右边插入元素

RPUSH key value [value …]

127.0.0.1:6379> RPUSH list2 1 2 3 4 5 6
(integer) 6
127.0.0.1:6379> LRANGE list2 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"

查看

  • 获取指定范围内的元素列表

LRANGE key start stop

lrange操作会获取列表指定索引范围所有的元素。 索引下标有两个特 第一, 索引下标从左到右分别是0到N-1, 但是从右到左分别是-1到-N。第二, lrange中的end选项包含了自身

  • 获取列表指定索引下标的元素

LINDEX key index

127.0.0.1:6379> LINDEX list2 1
"2"
127.0.0.1:6379> LINDEX list2 0
"1"
  • 获取列表长度

LLEN key

127.0.0.1:6379> LLEN list2
(integer) 6

删除

  • 从列表左侧弹出元素

LPOP key

127.0.0.1:6379> LRANGE list1 0 -1
1) "6"
2) "5"
3) "4"
4) "3"
5) "2"
6) "1"
127.0.0.1:6379> LPOP list1
"6"
127.0.0.1:6379> LRANGE list1 0 -1
1) "5"
2) "4"
3) "3"
4) "2"
5) "1"
  • 从列表右侧弹出

RPOP key

127.0.0.1:6379> LRANGE list1 0 -1
1) "6"
2) "5"
3) "4"
4) "3"
5) "2"
6) "1"
127.0.0.1:6379> LPOP list1
"6"
127.0.0.1:6379> LRANGE list1 0 -1
1) "5"
2) "4"
3) "3"
4) "2"
5) "1"
127.0.0.1:6379> RPOP list1
"1"
127.0.0.1:6379> LRANGE list1 0 -1
1) "5"
2) "4"
3) "3"
4) "2"
  • 删除指定元素

LREM key count value

lrem命令会从列表中找到等于value的元素进行删除, 根据count的不同
分为三种情况:

count>0, 从左到右, 删除最多count个元素。

count<0, 从右到左, 删除最多count绝对值个元素。

count=0, 删除所有。

  • 按照索引范围修剪列表

LTRIM key start stop

start 和 stop都是下标索引

127.0.0.1:6379> LRANGE list2 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
127.0.0.1:6379> LTRIM list2 1 3
OK
127.0.0.1:6379> LRANGE list2 0 -1
1) "2"
2) "3"
3) "4"

修改

LSET key index value

127.0.0.1:6379> LRANGE list2 0 -1
1) "2"
2) "3"
3) "4"
127.0.0.1:6379> LSET list2 0 222
OK
127.0.0.1:6379> LRANGE list2 0 -1
1) "222"
2) "3"
3) "4"

4.4使用场景

  • 消息队列
  • 文章列表,商品列表

5.Set类型

5.1set集合介绍

set类型即集合类型,其中的数据是不重复且没有顺序

集合类型和列表类型的对比:
在这里插入图片描述

​ 集合类型的常用操作是向集合中加入或删除元素、判断某个元素是否存在等,由于集合类型的Redis内部是使用值为空的散列表实现,所有这些操作的时间复杂度都为0(1)。

Redis还提供了多个集合之间的交集、并集、差集的运算

5.2命令

添加元素

SADD key member [member …]

127.0.0.1:6379> EXISTS s1
(integer) 0
127.0.0.1:6379> SADD s1 a b c d
(integer) 4
127.0.0.1:6379> SADD s1 a b   #因为上一条命令已经添加了 a b,所以第二次执行时,返回0表示没有添加成功
(integer) 0

删除元素

SREM key member [member …]

127.0.0.1:6379> SREM s1 a b
(integer) 2

计算元素个数

SCARD key

127.0.0.1:6379> SCARD s1
(integer) 2

scard的时间复杂度为O(1) , 它不会遍历集合所有元素, 而是直接用Redis内部的变量 .

判断元素是否在集合中

SISMEMBER key member

127.0.0.1:6379> SISMEMBER s1 a
(integer) 0
127.0.0.1:6379> SISMEMBER s1 c
(integer) 1

如果给定元素element在集合内返回1, 反之返回0

获取所有元素

SMEMBERS key

127.0.0.1:6379> SMEMBERS s1
1) "c"
2) "d"

smembers和lrange、 hgetall都属于比较重的命令, 如果元素过多存在阻塞Redis的可能性, 这时候可以使用sscan来完成.

求多个集合的交集

SINTER key [key …]

127.0.0.1:6379> SMEMBERS s1
1) "c"
2) "d"
127.0.0.1:6379> SMEMBERS s2
1) "d"
2) "r"
3) "c"
4) "y"
5) "e"
127.0.0.1:6379> SINTER s1 s2
1) "c"
2) "d"

求多个集合的并集

SUNION key [key …]

127.0.0.1:6379> SMEMBERS s1
1) "c"
2) "d"
127.0.0.1:6379> SMEMBERS s2
1) "c"
2) "y"
3) "e"
4) "d"
5) "r"
127.0.0.1:6379> SUNION s1 s2
1) "e"
2) "d"
3) "c"
4) "y"
5) "r"

求多个集合的差集

SDIFF key [key …]

127.0.0.1:6379> SMEMBERS s1
1) "c"
2) "d"
127.0.0.1:6379> SMEMBERS s2
1) "c"
2) "y"
3) "e"
4) "d"
5) "r"
127.0.0.1:6379> SDIFF s1 s2
(empty list or set)
127.0.0.1:6379> SDIFF s2 s1
1) "y"
2) "e"
3) "r"

注意以上s1和s2的操作顺序,顺序不同结果也会不同

集合间的运算在元素较多的情况下会比较耗时, 所以Redis提供了上面 ,集合间的运算在元素较多的情况下会比较耗时, 所以Redis提供了上面 destination key中 。

sinterstore destination key [key …]
suionstore destination key [key …]
sdiffstore destination key [key …]

5.3使用场景

集合类型比较典型的使用场景是标签(tag)

6.Zset集合(有序集合)

6.1Zset介绍

​ 在集合类型的基础上,有序集合类型为集合中的每个元素都关联一个分数,这使得我们不仅可以完成插入、删除和判断元素是否存在在集合中,还能够获得分数最高或最低的前N个元素、获取指定分数范围内的元素等与分数有关的操作。

在某些方面有序集合和列表类型有些相似。

1、二者都是有序的。

2、二者都可以获得某一范围的元素。

但是,二者有着很大区别:

1、列表类型是通过链表实现的,获取靠近两端的数据速度极快,而当元素增多后,访问中间数据的速度会变慢。

2、有序集合类型使用散列表实现,所有即使读取位于中间部分的数据也很快。

3、列表中不能简单的调整某个元素的位置,但是有序集合可以(通过更改分数实现)

4、有序集合要比列表类型更耗内存。

数据结构 是够允许重复元素 是否有序 有序实现方式 应用场景
列表(list) 索引下标 消息队列
集合(set) 标签
有序集合(zset) 分值 排行榜系统

6.2命令

添加

​ 向有序集合中加入一个元素和该元素的分数,如果该元素已经存在则会用新的分数替换原有的分数。返回值是新加入到集合中的元素个数,不包含之前已经存在的元素。

ZADD key [NX|XX] [CH] [INCR] score member [score member …]

Redis3.2为zadd命令添加了nx、 xx、 ch、 incr四个选项:
nx: member必须不存在, 才可以设置成功, 用于添加。
xx: member必须存在, 才可以设置成功, 用于更新。
ch: 返回此次操作后, 有序集合元素和分数发生变化的个数
incr: 对score做增加, 相当于后面介绍的zincrby。

返回添加成员个数

127.0.0.1:6379> ZADD z1  80 zhangsan 81 lisi 82 wangwu
(integer) 3

计算成员个数

ZCARD key

127.0.0.1:6379> ZCARD z1
(integer) 3

计算某个成员的分数

ZSCORE key member

127.0.0.1:6379> ZSCORE z1 zhangsan
"80"

计算成员的排名

ZRANK key member

127.0.0.1:6379> ZRANK z1 zhangsan
(integer) 0
127.0.0.1:6379> ZRANK z1 wangwu
(integer) 2
127.0.0.1:6379> ZRANK z1 lisi
(integer) 1

删除成员

ZREM key member [member …]

127.0.0.1:6379> ZREM z1 zhangsan
(integer) 1

返回指定排名范围的成员

ZRANGE key start stop [WITHSCORES]

ZREVRANGE key start stop [WITHSCORES]

zrange是从低到高返回, zrevrange反之

127.0.0.1:6379> ZRANGE z1 0 -1
1) "lisi"
2) "wangwu"
3) "zhangsan"
127.0.0.1:6379> ZREVRANGE z1 0 -1
1) "zhangsan"
2) "wangwu"
3) "lisi"

获得指定分数范围的元素

ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]

ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]

127.0.0.1:6379> ZRANGEBYSCORE scoreboard 90 97 WITHSCORES
1) "wangwu"
2) "94"
3) "lisi"
4) "97"
127.0.0.1:6379> ZRANGEBYSCORE scoreboard 70 100 limit 1 2
1) "wangwu"
2) "lisi"

增加某个元素的分数

ZINCRBY key increment member

127.0.0.1:6379> ZINCRBY z1  10  zhangsan
"109"

返回指定分数范围成员个数

ZCOUNT key min max

127.0.0.1:6379> ZCOUNT z1 90 100
(integer) 0
127.0.0.1:6379> ZCOUNT z1 90 110
(integer) 1
127.0.0.1:6379> ZCOUNT z1 80 110
(integer) 3

删除指定分数范围的成员

ZREMRANGEBYSCORE key min max

删除分数为80-90之间的成员

127.0.0.1:6379> ZREMRANGEBYSCORE z1 80  90
(integer) 2
127.0.0.1:6379> ZRANGE z1 0 -1
1) "zhangsan"

等等

6.3应用场景

排行榜系统

7.渐进式遍历

Redis从2.8版本后, 提供了一个新的命令scan, 它能有效的解决keys命令存在的问题。 和keys命令执行时会遍历所有键不同, scan采用渐进式遍历的方式来解决keys命令可能带来的阻塞问题, 每次scan命令的时间复杂度是。

SCAN cursor [MATCH pattern] [COUNT count]

  • cursor是必需参数, 实际上cursor是一个游标, 第一次遍历从0开始, 每
    次scan遍历完都会返回当前游标的值, 直到游标值为0, 表示遍历结束。

  • match pattern是可选参数, 它的作用的是做模式的匹配, 这点和keys的
    模式匹配很像

  • count number是可选参数, 它的作用是表明每次要遍历的键个数, 默认
    值是10, 此参数可以适当增大

如以下演示

127.0.0.1:6379> MSET a a b b c c d d e e f f g g h h i i j j k k l l m m n n o o p p q q r r s s t t u u v v w w x x y y z z
OK
127.0.0.1:6379> keys *
 1) "r"
 2) "o"
 3) "t"
 4) "h"
 5) "l"
 6) "p"
 7) "u"
 8) "s"
 9) "q"
10) "d"
11) "j"
12) "b"
13) "n"
14) "a"
15) "y"
16) "x"
17) "f"
18) "v"
19) "c"
20) "z"
21) "w"
22) "e"
23) "k"
24) "i"
25) "m"
26) "g"
127.0.0.1:6379> DBSIZE
(integer) 26

scan演示

127.0.0.1:6379> SCAN 0
1) "30"
2)  1) "r"
    2) "y"
    3) "p"
    4) "k"
    5) "o"
    6) "f"
    7) "s"
    8) "i"
    9) "m"
   10) "j"
   11) "b"
   12) "n"
127.0.0.1:6379> SCAN 30
1) "27"
2)  1) "x"
    2) "u"
    3) "v"
    4) "c"
    5) "d"
    6) "g"
    7) "t"
    8) "h"
    9) "l"
   10) "q"
127.0.0.1:6379> SCAN 27
1) "0"
2) 1) "z"
   2) "w"
   3) "e"
   4) "a"

除了scan以外, Redis提供了面向哈希类型、 集合类型、 有序集合的扫描遍历命令, 解决诸如hgetall、 smembers、 zrange可能产生的阻塞问题, 对应的命令分别是hscan、 sscan、 zscan, 它们的用法和scan基本类似.

渐进式遍历可以有效的解决keys命令可能产生的阻塞问题, 但是scan并非完美无瑕, 如果在scan的过程中如果有键的变化(增加、 删除、 修改) , 那么遍历效果可能会碰到如下问题: 新增的键可能没有遍历到, 遍历出了重
复的键等情况, 也就是说scan并不能保证完整的遍历出来所有的键, 这些是我们在开发时需要考虑的。

发布了234 篇原创文章 · 获赞 157 · 访问量 41万+

猜你喜欢

转载自blog.csdn.net/niugang0920/article/details/96427251