redis开发与运维-redis02-redis数据类型与命令总结

发布于:2024-12-22 ⋅ 阅读:(14) ⋅ 点赞:(0)

文章目录

【README】

本文总结自《redis开发与运维》,作者付磊,张益军,墙裂推荐;

本文使用的redis版本是 7.0.15 ; 又《redis开发与运维》中使用的redis版本是3.0.7,两个版本的redis部分数据结构的内部编码有差异(但不影响整体知识结构);



【1】redis通用命令与数据结构

【1.1】通用命令

1)keys * 查看所有键

keys事件复杂度为O(n),因为它会遍历所有键 ;

192.168.163.211:6379> keys *
 1) "name3"
 2) "name"
 3) "name2"
 4) "tomhome:order:2"
 5) "set2"
 6) "yuwen_score_set"
 7) "tomhome:user:1"
 8) "orders"
 9) "tomhome:order:1"
10) "tomhome:product:2"
11) "set1"
12) "tomhome:user:2"
13) "tomhome:product:1"

2)dbsize 键总数 : 返回当前数据库中键的总数;

dbsize时间复杂度为O(1),它是直接获取redis内置的键总数变量;

192.168.163.211:6379> dbsize
(integer) 13

3)exists key 检查键是否存在

192.168.163.211:6379> exists name3
(integer) 1
192.168.163.211:6379> 
192.168.163.211:6379> exists hello
(integer) 0

4)del key 删除键

192.168.163.211:6379> del name3
(integer) 1

5)expire key seconds 键过期 (过期时间单位是秒)

redis支持对键添加过期时间,当超过过期时间后,会自动删除键;

192.168.163.211:6379> expire name 10
(integer) 1

# ttl 查看剩余的存活时间(单位秒) 
192.168.163.211:6379> ttl name
(integer) 8
192.168.163.211:6379> 
192.168.163.211:6379> ttl name
(integer) 7
192.168.163.211:6379> ttl name
(integer) 6
192.168.163.211:6379> ttl name
(integer) 5
192.168.163.211:6379> ttl name
(integer) -2 # -2表示该键name被删除了 

【ttl命令】:返回键的剩余过期时间,有3种值;

  • 大于等于0的整数: 键剩余的过期时间;
  • -1 : 键没有设置过期时间,即永久存在;
  • -2: 键不存在;

6)type key 键的数据结构类型

若键不存在,返回none

192.168.163.211:6379> keys *
 1) "name2"
192.168.163.211:6379> type name2
string

# 若键不存在,返回none 
192.168.163.211:6379> type hello
none


【1.2】数据结构与内部编码

1)type key命令实际返回键的数据结构类型,包括string字符串, hash哈希, list列表, set集合, zset有序集合;如下图所示。
在这里插入图片描述在这里插入图片描述

2)可以通过object encoding key命令查询内部编码;

192.168.163.211:6379> get name2
"zhangsan2"
192.168.163.211:6379> object encoding name2
"embstr"


【1.3】redis单线程架构

1)为什么单线程还那么快?

  • 纯内存访问:redis将所有数据放在内存中,内存的响应时长大约为100纳秒,这是redis达到每秒万级别访问的基础;
  • 非阻塞IO:redis使用epoll作为io多路复用技术的实现,再加上redis自身的事件处理模型将epoll中的连接,读写,关闭都转换为事件,不在网络io上浪费过多的时间;
  • 单线程避免了线程切换和竞态产生的消耗;

在这里插入图片描述

【1.3.1】redis单线程优缺点

1)redis单线程的好处:

  • 单线程可以简化数据结构和算法实现;
  • 单线程避免线程切换和竞态产生的消耗,对于服务端来说,锁和线程切换通常是性能杀手

2)redis单线程缺点:

  • 对于每个命令的执行时间有要求;如果某个命令执行时间过长,会造成其他命令的阻塞,对于redis这种高性能的服务来说是致命的,所以redis是面向快速执行场景的数据库;


【2】字符串(值的类型为字符串)

1)字符串定义: 字符串类型是redis最基础的数据结构。键都是字符串类型,而其他几种数据结构都是在字符串类型基础上构建的;

2)字符串类型的值:实际可以是字符串(简单字符串,复杂字符串如json),数字(整数,浮点数),二进制(图片,音视频),值最大不超过512M;

在这里插入图片描述



【2.1】常用命令

【2.1.1】设置值

1)set key value 设置键key的值为value

192.168.163.211:6379> get name
"zhangsan"
192.168.163.211:6379> set name zhangsan2
OK
192.168.163.211:6379> get name
"zhangsan2"

set命令有几个选项:如 set key value [NX|XX] [GET] [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|KEEPTTL]

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

2)setex key seconds value 设置带有过期时间的key-value

等价于set key value ex seconds

192.168.163.211:6379> set name zhangsan3 ex 10
OK
192.168.163.211:6379> ttl name
(integer) 6
192.168.163.211:6379> ttl name
(integer) -2
192.168.163.211:6379> get name
(nil)
192.168.163.211:6379> exists name
(integer) 0 #0表示不存在 

3)setnx key vlaue 当键不存在才新增

等价于 set key value nx

192.168.163.211:6379> exists name
(integer) 0

# setnx 不存在才新增
192.168.163.211:6379> setnx name zhangsan4
(integer) 1

# exists name是否存在; 1-存在,0-不存在 
192.168.163.211:6379> exists name
(integer) 1
192.168.163.211:6379> get name
"zhangsan4"

# set key value nx 不存在才新增
192.168.163.211:6379> set name zhangsan5 nx
(nil)
192.168.163.211:6379> get name
"zhangsan4" # 还是zhangsan4 

4)set key value xx 键key存在才新增,否则不新增

192.168.163.211:6379> keys *
1) "name2"
2) "set1"
3) "set2"
4) "name"
192.168.163.211:6379> set name3 zhangsan30 xx
(nil)
192.168.163.211:6379> get name
"zhangsan4"
192.168.163.211:6379> set name zhangsan30
OK
192.168.163.211:6379> get name
"zhangsan30"


【2.1.2】获取值

1)get key 获取值,若键不存在,则返回空nil

192.168.163.211:6379> keys *
1) "name2"
2) "set1"
3) "set2"
4) "name"
192.168.163.211:6379> get name
"zhangsan30"
192.168.163.211:6379> get name3
(nil)


【2.1.3】批量设置值

1)mset key value [key value …] 批量设置值

192.168.163.211:6379> mset k1 1 k2 2 k3 3
OK


【2.1.4】批量获取值

1)mget key [key…] 批量获取值

192.168.163.211:6379> mset k1 1 k2 2 k3 3
OK

# mget 批量获取值 ( 若key不存在,则返回空nil )
192.168.163.211:6379> mget k1 k2 k3 k4
1) "1"
2) "2"
3) "3"
4) (nil)

补充:批量操作mset,mget可以有效提高系统性能,因为减少了网络io;

注意: 每次批量操作发送的命令数不是无节制的,如果数量过多可能导致redis阻塞或网络阻塞;



【2.1.5】计数

1) incr key 自增

incr命令可以对值自增1,返回结果分三种情况:

  • 值不是整数, 返回错误;
  • 值是整数, 返回自增后的结果;
  • 键不存在, 按照值为0自增, 返回结果为1;
192.168.163.211:6379> exists name3
(integer) 0
192.168.163.211:6379> incr name3
(integer) 1
192.168.163.211:6379> incr name3
(integer) 2

# 查找键name的值
192.168.163.211:6379> get name
"zhangsan30"
# 值不是整数,则返回错误 
192.168.163.211:6379> incr name
(error) ERR value is not an integer or out of range

补充:除开 incr命令,redis还提供了 decr自减,incrby自增指定数字,decrby自减指定数字,incrbyfloat自增浮点数;

  • 很多存储系统和编程语言内部使用CAS实现计数功能,会有一定的cpu开销; redis完全不存在这个问题, 因为redis是单线程架构,命令是顺序执行;


【2.2】不常用命令

【2.2.1】追加值

1)append key value 向字符串尾部追加值

192.168.163.211:6379> get name
"zhangsan30" 
192.168.163.211:6379> append name hello
(integer) 15
192.168.163.211:6379> get name
"zhangsan30hello"


【2.2.2】字符串长度

1)strlen key 返回键=key的值的长度

192.168.163.211:6379> get name
"zhangsan30hello"
192.168.163.211:6379> strlen name
(integer) 15


【2.2.3】设置并返回原值

1)getset key new_value 设置key的值为new_value,并返回旧值

192.168.163.211:6379> get name
"zhangsan30hello"
192.168.163.211:6379> getset name hello
"zhangsan30hello"
192.168.163.211:6379> get name
"hello"


【2.2.4】设置指定位置的字符

1)setrange key offset new_value 把key对应值的offset位置(从0计数)上的值设置为new_value

192.168.163.211:6379> get tom
"pest"
192.168.163.211:6379> setrange tom 0 b
(integer) 4
192.168.163.211:6379> get tom
"best"


【2.2.5】获取部分字符串

1)getrange key start end 截取key对应值的[start,end]位置上的字符串, start与end从0开始计数

192.168.163.211:6379> get tom
"best"
192.168.163.211:6379> getrange tom 1 2
"es"


【2.2.6】值为字符串类型的命令总结

命令 命令描述 时间复杂度
set key value 设置键与值 O(1)
get key 获取键的值 O(1)
del key [key …] 删除键 O(k),k为键的个数
mset key value [key value …] 批量设置多个键值对 O(k),k为键的个数
mget key [key …] 批量获取多个键的值 O(k),k为键的个数
incr key Key的值自增 O(1)
decr key Key的值自减 O(1)
incrby key increment Key的值增加increment O(1)
decrby key increment Key的值减少increment O(1)
incrbyfloat key increment Key的值增加increment O(1)
append key value 向key的值的尾部追加value O(1)
strlen key 计算key的值的长度 O(1)
setrange key offset value 设置指定位置的字符串,从0计数 O(1)
getrange key start end 获取[start,end]位置范围上的字符串,从0计数 O(n),n为字符串长度


【2.3】内部编码

1)字符串类型的内部有3种编码:

  • int: 8个字节的长整型;
  • embstr:小于等于39个字节的字符串;
  • raw:大于39个字节的字符串;

redis 会根据当前值的类型与长度决定使用哪种内部编码;

192.168.163.211:6379> get num1
"1234"
192.168.163.211:6379> object encoding num1
"int"


【2.4】值为字符串类型的典型使用场景

1)缓存;

  • redis作为缓存层,mysql作为存储层,绝大部分请求的数据都从redis获取;
  • 键名格式: 业务名: 对象名 : id : [属性]

2)计数;

  • 如视频观看数,点赞数;

3)共享Session;

  • 每次用户更新或者查询登录信息都直接从redis中获取;

在这里插入图片描述

4)限速;

  • 很多应用出于安全考虑,会在每次登录时,让用户输入手机验证码;


【3】哈希(值的类型为哈希)

1)哈希类型定义:指键的值本身又是一个键值对结构。 如value={{field1, value1}, {field2, value2}}

2)哈希类型的键值对举例,如下。

在这里插入图片描述



【3.1】命令

【3.1.1】设置值

1)hset key field value 为key添加一对 field-value 域-值对

设置成功返回1,否则返回0;

192.168.163.211:6379> hset user:1 name tom
(integer) 1
192.168.163.211:6379> hset user:1 age 18
(integer) 1
192.168.163.211:6379> hget user:1 name
"tom"

2)hsetnx key field value 当field不存在时,为key添加一对 field-value 域-值对

192.168.163.211:6379> hsetnx user:1 name tom2
(integer) 0
192.168.163.211:6379> hget user:1 name
"tom"

192.168.163.211:6379> hsetnx user:1 addr cd
(integer) 1

192.168.163.211:6379> hget user:1 addr
"cd"


【3.1.2】获取值

1)hget key field 获取key的field域的值

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

192.168.163.211:6379> hget user:1 addr
"cd"

192.168.163.211:6379> hget user:2 name
(nil)

192.168.163.211:6379> hget user:1 name
"tom"
192.168.163.211:6379> hget user:1 account
(nil)


【3.1.3】删除field

1)hdel key field [field …]

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

192.168.163.211:6379> hget user:1 name
"tom"
192.168.163.211:6379> hget user:1 age
"18"

192.168.163.211:6379> hdel user:1 name age
(integer) 2

192.168.163.211:6379> hget user:1 age
(nil)
192.168.163.211:6379> hget user:1 name
(nil)


【3.1.4】计算field个数

1) hlen key 计算field个数

# 获取所有域
192.168.163.211:6379> hkeys user:1
1) "addr"
2) "name"
3) "age"

# 计算域个数 
192.168.163.211:6379> hlen user:1
(integer) 3


【3.1.5】 批量设置或获取域值对

1)hmset key field [field …] 批量设置域值对

2)hmget key field [field …] 批量获取域值对

192.168.163.211:6379> hmset user:2 name tom2 age 22 addr cd2
OK

192.168.163.211:6379> hmget user:2 name age addr
1) "tom2"
2) "22"
3) "cd2"


【3.1.6】判断field域是否存在

1)hexists key field 判断key对应值的域field是否存在

若key对应值包含域field,则返回1,否则返回0

192.168.163.211:6379> hexists user:2 name
(integer) 1

192.168.163.211:6379> hexists user:2 phonenum
(integer) 0


【3.1.7】获取所有域和值

1)hkeys key 获取key的所有域field

2)hvals key 获取key的所有域的值

192.168.163.211:6379> hkeys user:2
1) "name"
2) "age"
3) "addr"

192.168.163.211:6379> hvals user:2
1) "tom2"
2) "22"
3) "cd2"

3)hgetall key 获取key的所有域值对

192.168.163.211:6379> hgetall user:2
1) "name"
2) "tom2"
3) "age"
4) "22"
5) "addr"
6) "cd2"


【3.1.8】自增操作

1)hincrby key field given_value 把key的field域自增给定值given_value

  • hincrby 与 incrby 类似;只不过hincrby 操作key的域,而incrby操作key;

2)hincrbyfloat key field given_value 把key的field域(浮点型)自增给定值given_value

  • hincrby 与 incrby 类似;只不过hincrby 操作key的域,而incrby操作key;
192.168.163.211:6379> hget user:2 age
"22"

# 
192.168.163.211:6379> hincrby user:2 age 3
(integer) 25
192.168.163.211:6379> hget user:2 age
"25"

# 
192.168.163.211:6379> hincrbyfloat user:2 age 4
"29"
192.168.163.211:6379> hget user:2 age
"29"


【3.1.9】计算value的字符串长度

1)hstrlen key field 获取key的域field对应值的字符串长度

192.168.163.211:6379> hget user:2 addr
"cd2"
192.168.163.211:6379> hstrlen user:2 addr
(integer) 3


【3.1.10】值为哈希类型的命令总结

命令 命令描述 时间复杂度
hset key field value 设置key的field域的值为value O(n),n为field的个数
hget key field 获取key的field域的值 O(n),n为field的个数
hdel key field [field …] 删除key的field域 O(1)
hlen key 获取key的field域的个数 O(n),n为field的个数
hgetall key 获取key的所有域值对(域+值) O(n),n为field的个数
hmget field [field…] 批量获取key的field域的值 O(1)
hmset field value [field value…] 批量设置key的field域的值 O(1)
hexists key field 判断key的field域是否存在 O(1)
hkeys key 获取key的所有域 O(1)
hvals key 获取key的所有域的值 O(1)
hsetnx key field value 当field域不存在才为key设置域值对 O(1)
hincrby key field increment key的field域自增给定值increment(整型) O(1)
hincrbyfloat key field increment key的field域自增给定值increment(浮点型) O(1)
hstrlen key field 获取key的field域的值的字符串长度 O(1)


【3.2】内部编码

1)哈希类型的内部编码有2种:

  • ziplist-压缩列表:当域个数小于hash-max-ziplist-entries(默认512),且所有域的值的字节数小于hash-max-ziplist-value(默认64字节),redis使用ziplist编码格式存储该哈希类型的值; (补充: ziplist使用更加紧凑的结构实现多个元素的连续存储,比hashtable节省内存)
  • hashtable-哈希表:当不满足ziplist条件时,redis使用hashtable编码格式存储该哈希类型的值; (因为ziplist的读写效率会下降,而hashtable的读写时间复杂度为O(1) )

2)object encoding key 查看key的值的编码

# object encoding key 查看key的值的编码 
192.168.163.211:6379> object encoding user:1
"listpack"
192.168.163.211:6379> hgetall user:1
1) "addr"
2) "cd"
3) "name"
4) "tom"
5) "age"
6) "18"


【3.3】使用场景

【3.3.1】 哈希类型与关系型数据库的不同

1)哈希类型与关系型数据库的不同

  • 哈希类型是稀疏的,关系型数据库是完全结构化的; 如哈希类型每个键可以有不同field,而关系型数据库一旦添加新的列,所有行都要为其设置值;
  • 关系型数据库可以做复杂关系查询, redis做复杂查询比较困难;


【3.3.2】缓存用户信息的方法

1)缓存用户信息的3种方法:

  • 方法1:原生字符串类型,每个属性1个键;【不推荐
    • 优点:简单直观,每个属性都支持更新操作;
    • 缺点:占用过多键,内存占用量较大;
  • 方法2:序列化字符串类型,把用户信息序列化后用一个键保存;
    • 优点:提供内存使用率;
    • 缺点:序列化与反序列化有一定开销;
  • 方法3:哈希类型,每个用户属性使用一个域值对存储,但只用一个键保存;
    • 优点:简单直观,如果使用合理可以减少内存使用;
    • 缺点:控制哈希类型在ziplist和hashtable两种内部编码的转换, hashtable会消耗更多内存;


【4】列表(值的类型为列表)

1)列表类型定义: 用来存储多个有序的字符串, 如 aaa, bbb, ccc, ddd, eeee, 这5个元素从左到右组成了一个有序列表;

  • 元素定义: 列表中的每个字符串称为元素;
  • 一个列表最多可以存储 2^32-1个元素;
  • 常用操作:对列表两端插入(push)或弹出(pop)元素;

2)列表类型有2个特点:

  • 列表中的元素是有序的;
  • 列表中的元素可以是重复的;

在这里插入图片描述



【4.1】命令

【4.1.1】添加元素

1)rpush key value [value…] 从右边插入元素

192.168.163.211:6379> rpush users zhangsan lisi wangwu
(integer) 3

# lrange key 0 -1 可以从左到右获取列表的所有元素
192.168.163.211:6379> lrange users 0 -1
1) "zhangsan"
2) "lisi"
3) "wangwu"

2)lpush key value [value…] 从左边插入元素

192.168.163.211:6379> lpush users zhaoliu tianqi
(integer) 5
192.168.163.211:6379> lrange users 0 -1
1) "tianqi"
2) "zhaoliu"
3) "zhangsan"
4) "lisi"
5) "wangwu"

3)linsert key before | after pivot value 在某个元素前或者后插入元素

192.168.163.211:6379> lrange flags 0 -1
1) "a1"
2) "a2"
3) "a3"
4) "a1"
5) "a4"

# linsert key before | after pivot value 在某个元素前或者后插入元素 
192.168.163.211:6379> linsert flags before a1 java
(integer) 6
192.168.163.211:6379> 
192.168.163.211:6379> lrange flags 0 -1
1) "java"
2) "a1"
3) "a2"
4) "a3"
5) "a1"
6) "a4"


【4.1.2】查找

1)lrange key start end 获取指定范围内的列表

范围=[start,end],即包含end下标,从0开始计数;

列表下标有2个特点:

  • 下标从左到右分别是0到N-1 ;从右到左是 -1 到 -N ;
  • lrange中的end选项包含自身;
192.168.163.211:6379> lrange flags 0 -1
1) "java"
2) "a1"
3) "a2"
4) "a3"
5) "a1"
6) "a4"

# lrange key start end 获取指定范围内的列表
192.168.163.211:6379> lrange flags 1 3
1) "a1"
2) "a2"
3) "a3"

2)lindex key index 获取列表指定索引下标的元素

192.168.163.211:6379> lrange flags 0 -1
1) "java"
2) "a1"
3) "a2"
4) "a3"
5) "a1"
6) "a4"

# lindex key index 获取列表指定索引下标的元素
192.168.163.211:6379> lindex flags -1
"a4"
192.168.163.211:6379> lindex flags 0
"java"
192.168.163.211:6379> lindex flags 1
"a1"

3)llen key 获取列表长度

192.168.163.211:6379> lrange flags 0 -1
1) "java"
2) "a1"
3) "a2"
4) "a3"
5) "a1"
6) "a4"

# llen key 获取列表长度
192.168.163.211:6379> llen flags
(integer) 6


【4.1.3】删除

1)lpop key 从列表左侧弹出元素

192.168.163.211:6379> lrange flags 0 -1
1) "java"
2) "a1"
3) "a2"
4) "a3"
5) "a1"
6) "a4"

# lpop key 从列表左侧弹出元素
192.168.163.211:6379> lpop flags
"java"
192.168.163.211:6379> lrange flags 0 -1
1) "a1"
2) "a2"
3) "a3"
4) "a1"
5) "a4"

2)rpop key 从列表右侧弹出元素

192.168.163.211:6379> lrange flags 0 -1
1) "a1"
2) "a2"
3) "a3"
4) "a1"
5) "a4"

# rpop key 从列表右侧弹出元素
192.168.163.211:6379> rpop flags
"a4"
192.168.163.211:6379> lrange flags 0 -1
1) "a1"
2) "a2"
3) "a3"
4) "a1"

3)lrem key count value 删除指定元素

  • count=0 删除所有值等于value的元素
  • count>0 从左到右删除最多count个值等于value的元素
  • count<0 从右到左删除最多(count绝对值)个值等于value的元素
192.168.163.211:6379> lrange toms 0 -1
1) "tom1"
2) "tom1"
3) "tom1"
4) "tom1"
5) "tom2"
6) "tom2"
7) "tom2"
8) "tom3"
9) "tom3"

# lrem key count value 删除指定元素 
# lrem toms 3 tom1  从左到右删除最多3个值等于tom1的元素
192.168.163.211:6379> lrem toms 3 tom1
(integer) 3
192.168.163.211:6379> lrange toms 0 -1
1) "tom1"
2) "tom2"
3) "tom2"
4) "tom2"
5) "tom3"
6) "tom3"

count<0 从右到左删除最多(count绝对值)个值等于value的元素

192.168.163.211:6379> lrange toms 0 -1
1) "tom1"
2) "tom2"
3) "tom2"
4) "tom2"
5) "tom3"
6) "tom3"
7) "tom2"
8) "tom2"
9) "tom3"

# count<0 从右到左删除最多(count绝对值)个值等于value的元素
192.168.163.211:6379> lrem toms -3 tom2
(integer) 3
192.168.163.211:6379> lrange toms 0 -1
1) "tom1"
2) "tom2"
3) "tom2"
4) "tom3"
5) "tom3"
6) "tom3"

count=0 删除所有值等于value的元素

192.168.163.211:6379> lrange toms 0 -1
1) "tom1"
2) "tom2"
3) "tom2"
4) "tom3"
5) "tom3"
6) "tom3"

# count=0 删除所有值等于value(=tom3)的元素 
192.168.163.211:6379> lrem toms 0 tom3
(integer) 3
192.168.163.211:6379> lrange toms 0 -1
1) "tom1"
2) "tom2"
3) "tom2"

4)ltrim key start end 按照索引范围修剪key

保留[start, end]范围内的元素,其他元素删除,下标从0开始计数;

192.168.163.211:6379> lrange flags 0 -1
1) "a1"
2) "a2"
3) "a3"
4) "a4"
5) "a5"
6) "a6"

# ltrim key start end 按照索引范围修剪key
# ltrim flags 1 3  保留1号下标到3号下标范围的元素,即第2个到第4个这个范围的元素
192.168.163.211:6379> ltrim flags 1 3
OK
192.168.163.211:6379> lrange flags 0 -1
1) "a2"
2) "a3"
3) "a4"


【4.1.4】修改

1)lset key index newValue 修改指定下标的元素

192.168.163.211:6379> lrange flags 0 -1
1) "a2"
2) "a3"
3) "a4"

# lset key index newValue 修改指定下标的元素
192.168.163.211:6379> lset flags 0 a22
OK
192.168.163.211:6379> lrange flags 0 -1
1) "a22"
2) "a3"
3) "a4"


【4.1.5】阻塞

1)blpop key [key …] timeout 从左边阻塞式弹出

同理, brpop key [key …] timeout 从右边阻塞式弹出

192.168.163.211:6379> lrange flags 0 -1
1) "a22"
2) "a3"
3) "a4"

# blpop key [key ...] timeout 从左边阻塞式弹出
192.168.163.211:6379> blpop flags 1
1) "flags"
2) "a22"
192.168.163.211:6379> blpop flags 1
1) "flags"
2) "a3"
192.168.163.211:6379> lrange flags 0 -1
1) "a4"

补充: 有2个参数

  • key [key…] 多个类型为列表的键;
  • timeout 阻塞时间(单位秒)

2)如果列表为空,则客户端阻塞直到超时;

192.168.163.211:6379> lrange flags 0 -1
(empty array)

# 列表为空,则客户端阻塞直到超时
192.168.163.211:6379> blpop flags 10
(nil)
(10.08s)

3)若阻塞期间,有元素,则立即返回;如果阻塞时间为0,则客户端一直阻塞下去;

192.168.163.211:6379> lrange flags 0 -1
(empty array)

# 若阻塞期间,有元素,则立即返回
192.168.163.211:6379> blpop flags 10
1) "flags"
2) "a1"
(3.31s) # 当前列表为空,所以阻塞;阻塞期间有其他客户端插入元素,则立即弹出元素,整个过程耗时3.31秒 


【4.1.6】值为列表类型的命令总结

操作 命令 描述 时间复杂度
添加 rpush/lpush key value [value…] 从列表右侧/左侧插入 O(k) k是元素个数
添加 linsert key before|after pivot value 在元素pivot前或后插入元素value O(n) n是pivot到列表头或尾的距离
查找 lrange key start end 获取范围内的列表 O(s+n) s是start,n是范围长度
查找 lindex key index 获取指定下标的元素 O(n) n是索引偏移量
查找 llen key 获取列表长度 O(1)
删除 lpop/rpop key 从列表右侧/左侧弹出 O(1)
删除 lrem key count value 删除值等于value的元素,count有3种取值 O(n) n是列表长度
删除 ltrim key start end 按照范围修剪key,保留范围内的元素,其他元素删除 O(n) n是要裁剪的元素总数
修改 lset key index value 修改指定下标的元素 O(1) n是索引偏移量
阻塞操作 blpop/brpop key timeout 从左边/右边阻塞式弹出,超时时间timeout秒 O(1)

备注:命令的下标从0开始计数,下标范围包含start,end下标上的元素



【4.2】内部编码

1)列表类型的内部编码有2种(ziplist + linkedlist):

  • ziplist 压缩列表: 当列表的元素个数小于 list-max-ziplist-entries 配置(默认512),同时列表中每个元素的值的字节数小于list-max-ziplist-value 配置(默认64字节),redis会选择使用ziplist数据结构来保存列表,以减少内存使用;
  • linkedlist链表:当列表类型无法满足ziplist的条件时,redis会使用linkedlist数据结构保存列表;
192.168.163.211:6379> object encoding flags
"quicklist"

补充: quicklist结合了ziplist和linkedlist两者的优势,为列表类型提供了更为优秀的内部编码实现;



【4.3】使用场景

【4.3.1】消息队列

1)使用lpush + brpop命令即可实现阻塞队列;

  • 生产者使用 lpush 从列表左侧插入元素;
  • 多个消费者使用 brpop 命令阻塞式抢列表尾部元素;

2)多个客户端保证了消费的负载均衡和高可用性;

在这里插入图片描述



【4.3.2】文章列表

1)每个用户有自己的文章列表,现在需要分页展示列表。

  • 可以考虑使用列表保存文章;

2)使用列表保存文章的问题:

  • 问题1:如果每次分页查询到的文章太多,需要执行多次hgetall操作;
    • 解决方法:可以考虑使用 pipeline批量获取,或者把文章数据序列化为字符串类型,使用mget批量获取;
  • 问题2:分页获取文章列表时,lrange在列表两端性能好,在列表中间范围性能差;(底层是双向链表)
    • 解决方法:考虑把列表做二级拆分,或者使用redis的quicklist内部编码实现,在中间范围高效分页;


【4.3.3】列表类型的使用场景总结

1)列表类型的使用场景:

  • lpush + lpop = 栈,先进后出;
  • lpush + rpop = 队列,先进先出;
  • lpush + ltrim = 有限集合;
    • 左侧插入,左侧裁剪,仅保留范围内元素,其他元素裁剪掉;
  • lpush + brpop = 消息队列;
    • 左侧插入,右侧阻塞式弹出元素;


【5】集合(值的类型为集合)

1)集合类型定义:集合类型用于保存多个字符串元素,但不允许重复元素,且元素无序,不能通过通过下标获取元素;

  • 1个集合最多可以存储 2^32-1个元素;
  • 集合除了增删改查,还支持多个集合取交集,并集,差集;


【5.1】命令

【5.1.1】集合内操作

1)sadd key element [element…] 添加元素

返回结果为添加成功的元素个数;

192.168.163.211:6379> sadd set1 a1 a2 a3 a4
(integer) 4

# smembers key 获取所有元素,显然无序
192.168.163.211:6379> smembers set1
1) "a2"
2) "a3"
3) "a4"
4) "a1"

2)srem key element [element…] 删除元素

返回结果为成功删除的元素个数;

192.168.163.211:6379> smembers set1
1) "a2"
2) "a3"
3) "a4"
4) "a1"

# 删除元素
192.168.163.211:6379> srem set1 a1 a2
(integer) 2
192.168.163.211:6379> smembers set1
1) "a3"
2) "a4"

# 删除不存在的元素,返回0 
192.168.163.211:6379> srem set1 a1 a2
(integer) 0

3) scard key 计算元素个数

192.168.163.211:6379> smembers set1
1) "a3"
2) "a4"

# scard key 计算元素个数 
192.168.163.211:6379> scard set1
(integer) 2

4)sismember key member 判断元素member是否在key为键的集合中

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

192.168.163.211:6379> smembers set1
1) "a3"
2) "a4"

# sismenber key member 判断元素member是否在key为键的集合中
192.168.163.211:6379> sismember set1 a3
(integer) 1
192.168.163.211:6379> sismember set1 a4
(integer) 1
192.168.163.211:6379> sismember set1 a5
(integer) 0

5)srandmember key [count] 随机从集合返回指定个数元素

192.168.163.211:6379> smembers set1
1) "a5"
2) "a6"
3) "a3"
4) "a4"

# srandmember key [count]  随机从集合返回指定个数元素 
192.168.163.211:6379> srandmember set1 3
1) "a3"
2) "a6"
3) "a4"

6)spop key 从集合随机弹出元素

192.168.163.211:6379> smembers set1
1) "a5"
2) "a6"
3) "a3"

# spop key 从集合随机弹出元素
192.168.163.211:6379> spop set1
"a5"
192.168.163.211:6379> smembers set1
1) "a6"
2) "a3"

【注意1】 srandmember 和 spop 都是随机从集合选出元素, 两者不同的是spop命令会删除元素,而srandmember不会;

【注意2】smembers 和 lrange, hgetall 都属于比较重的命令,如果元素过多可能阻塞redis,可以使用sscan来完成;



【5.1.2】集合间操作

1)sinter key [key…] 求多个集合的交集

192.168.163.211:6379> smembers class1
1) "a2"
2) "a3"
3) "a4"
4) "a1"
192.168.163.211:6379> smembers class2
1) "a5"
2) "a2"
3) "a6"
4) "a4"

# sinter key [key...] 求多个集合的交集
192.168.163.211:6379> sinter class1 class2
1) "a2"
2) "a4"

2)sunion key [key…] 求多个集合的并集

# sunion key [key...]  求多个集合的并集   
192.168.163.211:6379> sunion class1 class2
1) "a5"
2) "a3"
3) "a4"
4) "a2"
5) "a6"
6) "a1"

3)sdiff key [key…] 求多个集合的差集

sdiff key1 key2 获取key1集合中有的而key2集合中没有的元素;

# sdiff key [key...] 求多个集合的差集 
192.168.163.211:6379> sdiff class1 class2
1) "a3"
2) "a1"

4)把交集,并集,差集的结果保存

  • sinterstore result_key key1 key2 把key1集合与key2集合的交集结果保存到result_key集合;
  • sunionstore result_key key1 key2 把key1集合与key2集合的并集结果保存到result_key集合;
  • sdiffstore result_key key1 key2 把key1集合与key2集合的差集结果保存到result_key集合;
# sinterstore result_key key1 key2 把key1集合与key2集合的交集结果保存到result_key集合
192.168.163.211:6379> sinterstore key3_1 class1 class2
(integer) 2

# sunionstore result_key key1 key2 把key1集合与key2集合的并集结果保存到result_key集合
192.168.163.211:6379> sunionstore key3_2 class1 class2
(integer) 6

# sdiffstore result_key key1 key2 把key1集合与key2集合的差集结果保存到result_key集合
192.168.163.211:6379> sdiffstore key3_3 class1 class2
(integer) 2

192.168.163.211:6379> smembers key3_1
1) "a2"
2) "a4"
192.168.163.211:6379> smembers key3_2
1) "a5"
2) "a3"
3) "a4"
4) "a2"
5) "a6"
6) "a1"
192.168.163.211:6379> smembers key3_3
1) "a3"
2) "a1"


【5.1.3】值为集合类型的命令总结

操作 命令 描述 时间复杂度
新增 sadd key element [element…] 新增元素 O(k) k是元素个数
删除 srem key element [element…] 删除元素 O(k) k是元素个数
删除 spop key 弹出元素 O(1)
查找 scard key 计算元素个数 O(1)
查找 sismember key element 判断元素element在key集合中是否存在 O(1)
查找 srandmember key [count] 随机从集合返回指定个数元素 O(count)
查找 smembers key 获取所有元素 O(n) n为元素总数
集合运算 sinter key [key…] 计算集合交集 O(m*k) m是键个数,k是多个集合中元素最少的个数
集合运算 sunion key [key…] 计算集合并集 O(k) k是多个集合元素个数和
集合运算 sdiff key [key…] 计算集合差集 O(k) k是多个集合元素个数和
集合运算 sinterstore key3 key1 key2 计算集合交集并存储到key3 O(m*k) m是键个数,k是多个集合中元素最少的个数
集合运算 sunionstore key3 key1 key2 计算集合并集并存储到key3 O(k) k是多个集合元素个数和
集合运算 sdiffstore key3 key1 key2 计算集合差集并存储到key3 O(k) k是多个集合元素个数和


【5.2】内部编码

1)集合类型的内部编码有2种:

  • intset 整数集合:当集合中的元素都是整数且元素个数小于 set-max-intset-entries 配置(默认512个)时,redis会使用intset来存储集合数据,从而减少内存使用;
  • hashtable 哈希表:当无法满足intset条件时,redis使用哈希表存储集合数据;

2)object encoding 查看键的内部编码:

# object encoding 查看键的内部编码 
192.168.163.211:6379> object encoding key3_2
"hashtable"
192.168.163.211:6379> smembers key3_2
1) "a5"
2) "a3"
3) "a4"
4) "a2"
5) "a6"
6) "a1"


【5.3】使用场景

1)集合类型比较典型的应用场景是标签;

  • 如用户贴上娱乐,体育的标签;

【5.3.1】集合使用场景总结

1)标签: sadd

2)生成随机数,比如抽奖; spop/srandmember

3)社交需求: sadd + sinter



【6】有序集合

1)有序集合类型定义: 有序集合是一种集合,不允许有重复元素,但可以排序;

  • 通过给元素设置一个分数,然后根据元素分数来排序;

在这里插入图片描述

2)列表、集合、有序集合的不同点:

数据结构 是否允许重复 是否有序 有序实现方式 应用场景
列表 索引下标 时间轴,消息队列
集合 标签,社交
有序集合 分数 排行榜系统,社交等


【6.1】命令

【6.1.1】有序集合内命令

1)zadd key score member [score member…] 添加成员

# zadd key score member [score member...] 添加成员
192.168.163.211:6379> zadd class3 1.1 tom1 1.2 tom2 1.3 tom3
(integer) 3

# zrange key start end [withscores] 返回指定排名范围的成员
192.168.163.211:6379> zrange class3 0 -1 withscores
1) "tom1"
2) "1.1000000000000001"
3) "tom2"
4) "1.2"
5) "tom3"
6) "1.3"
192.168.163.211:6379> zrange class3 0 -1
1) "tom1"
2) "tom2"
3) "tom3"

# zadd key score member [score member...] 添加成员 
192.168.163.211:6379> zadd class3 0.4 tom4
(integer) 1
192.168.163.211:6379> zrange class3 0 -1
1) "tom4"
2) "tom1"
3) "tom2"
4) "tom3"

补充1:有序集合相比集合提供了排序字段,但也有代价;

  • zadd的时间复杂度为 O(log(n))
  • sadd的时间复杂度为 O(1)

补充2: zadd命令添加了 nx, xx, ch, incr 4个选项; 【 格式为 zadd key [NX|XX] [GT|LT] [CH] [INCR] score member [score member …] 】

  • nx:member不存在才可以设置成功; 用于新增;
  • xx:member存在才可以设置成功;用于更新;
  • ch:返回此次操作后,有序集合元素和分数发生变化的个数;
  • incr: 对score做增加, 相当于 zincrby ;
192.168.163.211:6379> zrange class3 0 -1 withscores
1) "tom4"
2) "44"

# xx:member存在才可以设置成功;用于更新;
192.168.163.211:6379> zadd class3 xx 45 tom4
(integer) 0
192.168.163.211:6379> zrange class3 0 -1 withscores
1) "tom4"
2) "45"

# nx:member不存在才可以设置成功; 用于新增; 显然tom4存在,所以新增失败
192.168.163.211:6379> zadd class3 nx 46 tom4
(integer) 0
192.168.163.211:6379> zrange class3 0 -1 withscores
1) "tom4"
2) "45"

# nx:member不存在才可以设置成功; 用于新增; 显然tom5不存在,所以新增【成功】
192.168.163.211:6379> zadd class3 nx 51 tom5
(integer) 1
192.168.163.211:6379> zrange class3 0 -1 withscores
1) "tom4"
2) "45"
3) "tom5"
4) "51"

# incr: 对score做增加
192.168.163.211:6379> zadd class3 incr 1 tom5
"52"
192.168.163.211:6379> zrange class3 0 -1 withscores
1) "tom4"
2) "45"
3) "tom5"
4) "52"

2)zcard key 计算成员个数

192.168.163.211:6379> zrange class3 0 -1 withscores
1) "tom4"
2) "45"
3) "tom5"
4) "52"

# zcard key 计算成员个数
192.168.163.211:6379> zcard class3
(integer) 2

3)zsocre key member 计算某个成员的分数

192.168.163.211:6379> zscore class3 tom4
"45"

4)zrank key member 计算成员排名(升序, 排名从0开始算起)

zrevrank key member (降序, 排名从0开始算起)

192.168.163.211:6379> zrange class3 0 -1 withscores
1) "tom2"
2) "21"
3) "tom3"
4) "31"
5) "tom4"
6) "45"
7) "tom5"
8) "52"

# zrank key member 计算成员排名(升序,排名从0开始算起) 
192.168.163.211:6379> zrank class3 tom3
(integer) 1 # 顺数第1 

#  zrevrank key member (降序,排名从0开始算起) 
192.168.163.211:6379> zrevrank class3 tom3
(integer) 2 # 倒数第2

5)zrem key member [member…] 删除成员

192.168.163.211:6379> zrange class3 0 -1 withscores
1) "tom2"
2) "21"
3) "tom3"
4) "31"
5) "tom4"
6) "45"
7) "tom5"
8) "52"

# zrem key member [member...]  删除成员
192.168.163.211:6379> zrem class3 tom2 tom3
(integer) 2
192.168.163.211:6379> zrange class3 0 -1 withscores
1) "tom4"
2) "45"
3) "tom5"
4) "52"

6)zincrby key increment member 增加成员分数

192.168.163.211:6379> zrange class3 0 -1 withscores
1) "tom4"
2) "45"
3) "tom5"
4) "52"

# zincrby key increment member 增加成员分数 
192.168.163.211:6379> zincrby class3 1 tom4  # tom4的分数加1
"46"
192.168.163.211:6379> zrange class3 0 -1 withscores
1) "tom4"
2) "46"
3) "tom5"
4) "52"

7)zrange key start end [withscores] 返回指定排名范围的成员 (升序)

zrevrange key start end [withscores] (降序)

# zrange key start end [withscores] 返回指定排名范围的成员 (升序)
192.168.163.211:6379> zrange class3 0 -1 withscores
 1) "tom2"
 2) "21"
 3) "tom3"
 4) "31"
 5) "tom4"
 6) "46"
 7) "tom5"
 8) "52"
 9) "tom6"
10) "61"

# zrevrange key start end [withscores] (降序)
192.168.163.211:6379> zrevrange class3 0 -1
1) "tom6"
2) "tom5"
3) "tom4"
4) "tom3"
5) "tom2"

8)zrangebyscore key min max [withscores] [limit offset count] 返回指定分数范围的成员(升序)

zrevrangebyscore key min max [withscores] [limit offset count] 返回指定分数范围的成员(降序)

192.168.163.211:6379> zrange class3 0 -1 withscores
 1) "tom2"
 2) "21"
 3) "tom3"
 4) "31"
 5) "tom4"
 6) "46"
 7) "tom5"
 8) "52"
 9) "tom6"
10) "61"

# zrangebyscore key min max [withscores] [limit offset count]  返回指定分数范围的成员(升序)
192.168.163.211:6379> zrangebyscore class3 45 65 withscores
1) "tom4"
2) "46"
3) "tom5"
4) "52"
5) "tom6"
6) "61"

9)zcount key min max 返回指定分数范围成员个数

192.168.163.211:6379> zrangebyscore class3 45 65 withscores
1) "tom4"
2) "46"
3) "tom5"
4) "52"
5) "tom6"
6) "61"

# zcount key min max 返回指定分数范围成员个数
192.168.163.211:6379> zcount class3 45 65
(integer) 3

10)zremrangebyrank key start end 删除指定排名范围内的升序元素

192.168.163.211:6379> zrange class3 0 -1 withscores
 1) "tom2"
 2) "21"
 3) "tom3"
 4) "31"
 5) "tom4"
 6) "46"
 7) "tom5"
 8) "52"
 9) "tom6"
10) "61"

# zremrangebyrank key start end  删除指定排名内的升序元素 
192.168.163.211:6379> zremrangebyrank class3 1 3
(integer) 3
192.168.163.211:6379> zrange class3 0 -1 withscores
1) "tom2"
2) "21"
3) "tom6"
4) "61"

11)zremrangebysocre key min max 删除指定分数范围的成员

192.168.163.211:6379> zrange class3 0 -1 withscores
 1) "tom2"
 2) "21"
 3) "tom3"
 4) "32"
 5) "tom4"
 6) "42"
 7) "tom5"
 8) "52"
 9) "tom6"
10) "61"

# zremrangebysocre key min max 删除指定分数范围的成员 
192.168.163.211:6379> zremrangebyscore class3 35 55
(integer) 2
192.168.163.211:6379> zrange class3 0 -1 withscores
1) "tom2"
2) "21"
3) "tom3"
4) "32"
5) "tom6"
6) "61"


【6.1.2】有序集合间的操作命令

1) 计算交集

zinterstore result_key numkeys key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX]

  • result_key:结果键;
  • numkeys: 参与交集运算的键的个数;
  • key [key…] : 需要做交集计算的键;
  • WEIGHTS weight [weight …] :每个键的权重;即每个member用自己的分数乘以对应权重;默认权重为1;
  • AGGREGATE SUM|MIN|MAX :计算交集后,分数可以按照sum求和, min最小值,max最大值进行聚合,默认为sum;
192.168.163.211:6379> zrange class1 0 -1 withscores
1) "tom2"
2) "21"
3) "tom3"
4) "31"
5) "tom4"
6) "41"
7) "tom5"
8) "51"
192.168.163.211:6379> zrange class2 0 -1 withscores
1) "tom2"
2) "22"
3) "tom3"
4) "32"
5) "tom4"
6) "42"
7) "tom5"
8) "52"

# zinterstore result_key numkeys key [key ...]  计算交集 
192.168.163.211:6379> zinterstore result_key 2 class1 class2 
(integer) 4 
192.168.163.211:6379> zrange result_key 0 -1 withscores
1) "tom2"
2) "43"
3) "tom3"
4) "63"
5) "tom4"
6) "83"
7) "tom5"
8) "103"

2)计算并集

zunionstore result_key numkeys key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX]

参数解释如计算交集;

# zunionstore result_key numkeys key [key ...]  计算并集 
192.168.163.211:6379> zunionstore result_key2 2 class1 class2
(integer) 4
192.168.163.211:6379> zrange result_key2 0 -1 withscores
1) "tom2"
2) "43"
3) "tom3"
4) "63"
5) "tom4"
6) "83"
7) "tom5"
8) "103"


【6.1.3】有序集合类型的命令总结

操作 命令 描述 时间复杂度
新增 zadd key score member [score member…] 添加成员 O(k*logn) k是添加成员个数,n是有序集合成员个数
查询 zcard key 计算成员个数 O(1)
查询 zscore key member 计算成员分数 O(1)
查询 zrank key memberzrevrank key member 计算成员排名 O(logn) n是有序集合成员个数
查询 zrange key start end [withscores]zrevrange key start end [withscores] 返回指定排名范围内的成员-升序, zrevrange-降序 O(k+logn) k是要获取的成员个数,n是有序集合成员个数
查询 zrangebyscore key min max [withscores]zrevrangebysocre key min max [withscores] 返回指定分数范围的成员-升序, zrevrangebyscore-降序 O(k+logn) k是要获取的成员个数,n是有序集合成员个数
查询 zcount key min max 返回指定分数范围成员个数 O(logn) n是有序集合成员个数
删除 zrem key member [member…] 删除成员 O(k*logn) k是删除成员个数 n是有序集合成员个数
删除 zremrangebyrank key start end 删除指定排名范围内的升序元素 O(k+logn) k是要删除的成员个数,n是有序集合成员个数
删除 zremrangebyscore key start end 删除指定分数范围内的升序元素 O(k+logn) k是要删除的成员个数,n是有序集合成员个数
更新 zincrby key increment member 增加成员分数 O(logn) n是有序集合成员个数
集合运算 zinterstore result_key numkeys key [key…] 计算交集并存储结果为key O(nk)+O(mlogm) n是最小有序集合成员个数,k是有序集合的个数,m是结果集成员个数
集合运算 zunionstore result_key numkeys key [key…] 计算并集并存储结果为key O(n)+O(m*logm) n是有序集合成员个数和,m是结果集成员个数


【6.2】内部编码

1)有序集合类型的内部编码有2种:

  • ziplist 压缩列表:当有序集合的元素个数小于 zset-max-ziplist-entries配置(默认128),同时每个元素的值所占字节都小于 zset-max-ziplist-value配置(默认64字节)是,redis使用ziplist来存储有序集合数据,以减少内存使用 ;
  • skiplist 跳跃表: 当ziplist条件不满足时,redis使用skiplist存储有序集合数据,因此此时ziplist读写效率会下降;

2)查看有序集合编码

192.168.163.211:6379> zrange class3 0 -1 withscores
1) "tom2"
2) "21"
3) "tom3"
4) "32"
5) "tom6"
6) "61"
192.168.163.211:6379> object encoding class3
"listpack"

192.168.163.211:6379> zadd class3 99 tom999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
(integer) 1
192.168.163.211:6379> object encoding class3
"skiplist" # 类型转为skiplist


【6.3】使用场景

1)有序集合典型的使用场景是: 排行榜系统;

2)排行榜系统主要实现4个功能:

  • 用户赞数; zadd + zincrby
    • 新增用户赞数: zadd
    • 更新用户赞数: zincrby
  • 取消用户赞数(把用户从排行榜取消掉);
    • zrem key member
  • 展示获取赞数最多的用户;
    • zrevrangebyrank
  • 展示用户信息以及用户分数;
    • 把用户名作为键后缀, 将用户信息保存在哈希类型中;用户分数和排名可以使用zscore和zrank两个功能;
      • hgetall key
      • zscore key member
      • zrank key member


【7】键管理

【7.1】单个键管理

【7.1.1】键重命名

1)rename key newKey 键重命名

192.168.163.211:6379> set name tom
OK

# rename key newKey 键重命名 
192.168.163.211:6379> rename name name2
OK
192.168.163.211:6379> get name
(nil)
192.168.163.211:6379> get name2
"tom"

补充:rename前,若newKey已存在,则newKey的原值会被覆盖;

192.168.163.211:6379> set a b
OK
192.168.163.211:6379> set c d
OK

# rename前,若newKey已存在,则newKey的原值会被覆盖
192.168.163.211:6379> rename a c # 把键a重命名为键c
OK
192.168.163.211:6379> get a
(nil)
192.168.163.211:6379> get c
"b" # 键c原值为d,而rename后,被覆盖了,为b

2)renamenx key newKey 当newKey不存在时才执行键重命名

192.168.163.211:6379> set name1 tom1
OK
192.168.163.211:6379> set name2 tom2
OK

# renamenx key newKey 当newKey不存在时才执行键重命名 
192.168.163.211:6379> renamenx name1 name2
(integer) 0

# renamenx key newKey 当newKey不存在时才执行键重命名
192.168.163.211:6379> renamenx name1 name1_1 
(integer) 1
192.168.163.211:6379> get name1
(nil)
192.168.163.211:6379> get name2
"tom2"
192.168.163.211:6379> get name1_1
"tom1"

【补充】rename键重命名的注意点:

  • 注意点1:重命名时会执行del命令删除旧键,如果键对应的值所占内存比较大,可能会阻塞redis;
  • 注意点2:如果rename与renamenx中key与newKey相同,则重命名也可以成功;
192.168.163.211:6379> get name2
"tom2"

# 如果rename中key与newKey相同,则重命名也可以成功
192.168.163.211:6379> rename name2 name2
OK
192.168.163.211:6379> get name2
"tom2"

192.168.163.211:6379> get name2
"tom2"

# 如果renamenx中key与newKey相同,则重命名也可以成功
192.168.163.211:6379> renamenx name2 name2
(integer) 0
192.168.163.211:6379> get name2
"tom2"


【7.1.2】随机返回1个键

1)randomkey 随机返回1个键

192.168.163.211:6379> keys *
1) "c"
2) "name1_1"
3) "name2"

# randomkey 随机返回1个键 
192.168.163.211:6379> randomkey
"c"


【7.1.3】键过期

键过期的命令除了expire,ttl外,redis还提供了expireat, pexpire, pexpireat, pttl, persist 等命令

1)expire key seconds 键在seconds秒后过期

# expire key seconds 键在seconds秒后过期
192.168.163.211:6379> expire name2 10
(integer) 1

# ttl 查看剩余存活时间
192.168.163.211:6379> ttl name2
(integer) 8
192.168.163.211:6379> ttl name2
(integer) 1
192.168.163.211:6379> ttl name2
(integer) 0
192.168.163.211:6379> ttl name2 
(integer) -2 # -2 键不存在

ttl与pttl都可以查看键的剩余存活时间,但ttl单位是秒,而pttl单位是毫秒,有3种返回值;

  • 大于等于0的整数: 键剩余存活时间(ttl是秒,pttl是毫秒)
  • -1: 键没有设置过期时间;
  • -2:键不存在;

2)expireat key timestamp 键在秒级时间戳timestamp时刻后过期

192.168.163.211:6379> set name2 tom2
OK
192.168.163.211:6379> 

# expireat key timestamp 键在秒级时间戳timestamp时刻后过期
192.168.163.211:6379> expireat name2 1734752199
(integer) 1
192.168.163.211:6379> 
192.168.163.211:6379> get name2
"tom2"
192.168.163.211:6379> ttl name2
(integer) 75
192.168.163.211:6379> ttl name2
(integer) 74
192.168.163.211:6379> ttl name2
(integer) 73

补充:设置过期时间的其他命令

  • pexpire key milliseconds 键在milliseconds毫秒后过期
  • pexpire key milliseconds-timestamp 键在毫秒级时间戳timestamp时刻后过期

3)键过期命令的注意点:

注意点1:如果 expire key 时key不存在,则返回结果为0

192.168.163.211:6379> keys *
1) "c"
2) "name1_1"

# 如果 expire key 时key不存在,则返回结果为0
192.168.163.211:6379> expire name2 10
(integer) 0

注意点2:如果过期时间为负值,键会被立即删除,犹如使用del命令一样;

192.168.163.211:6379> get name2
"tom2"
192.168.163.211:6379> 

# 过期时间为负值,键会被立即删除,犹如使用del命令一样 (过期时间设置为-2,则该键立即被删除)
192.168.163.211:6379> expire name2 -2
(integer) 1
192.168.163.211:6379> get name2
(nil)
192.168.163.211:6379> ttl name2
(integer) -2

注意点3:persist命令可以把键的过期时间清除:

192.168.163.211:6379> expire name2 20
(integer) 1
192.168.163.211:6379> ttl name2
(integer) 18
192.168.163.211:6379> ttl name2
(integer) 16

# persist命令可以把键的过期时间清除
192.168.163.211:6379> persist name2
(integer) 1
192.168.163.211:6379> ttl name2
(integer) -1  # -1表示键永不过期

注意点4:对于字符串类型键,执行set命令会去掉过期时间,默认为-1(永不过期)

192.168.163.211:6379> get name3
"tom3"
192.168.163.211:6379> expire name3 100
(integer) 1
192.168.163.211:6379> ttl name3
(integer) 97

# 对于字符串类型键,执行set命令会去掉过期时间,默认为-1
192.168.163.211:6379> set name3 tom31
OK
192.168.163.211:6379> ttl name3
(integer) -1

注意点5:redis不支持二级数据结构(如哈希,列表)内部元素的过期功能;

注意点6:setex命令=set+expire命令, 不仅是原子执行,还减少1次网络io;



【7.2】遍历键

1)redis提供了2个命令遍历键,分别是keys和scan;

【7.2.1】全量遍历键-keys(不建议)

1)keys pattern 遍历满足pattern通配符的所有键

192.168.163.211:6379> keys *
1) "c"
2) "name2"
3) "name1_1"
4) "name3"
192.168.163.211:6379> keys name*
1) "name2"
2) "name1_1"
3) "name3"

pattern通配符语法解释:

* 匹配任意字符

? 匹配单个字符

[] 匹配部分字符; 如 [1,3]匹配1,3 ;而[1-10] 匹配1到10的任意数字

\x 用于转义,如匹配 * ? 等;

2)keys的问题:如果redis包含了大量键,执行keys命令很可能会造成阻塞;所以不建议在生产环境使用keys

【解决keys遍历键阻塞的问题】

  • 使用scan遍历键,能够有效防止阻塞;


【7.2.2】渐进式遍历-scan(推荐-常用)

1) scan cursor [match pattern] [count number]

  • cursor:必须参数,是一个游标,第1次遍历从0开始,每次scan遍历完都会返回当前游标的值,直到游标值为0,表示遍历结束;
  • match pattern:可选参数,作用是做模式匹配,与keys的通配符模式类似;
  • count nuber: 可选参数, 表示每次要遍历的键个数,默认是10;

2)scan遍历示例

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

# scan 第1次遍历从0开始
192.168.163.211:6379> scan 0
1) "26"  # 26-每次scan遍历完都会返回当前游标的值
2)  1) "r"
    2) "i"
    3) "k"
    4) "z"
    5) "h"
    6) "y"
    7) "c"
    8) "o"
    9) "a"
   10) "m"
   
# 第n次遍历传入第n-1次遍历返回的游标值
192.168.163.211:6379> scan 26
1) "9"
2)  1) "j"
    2) "f"
    3) "b"
    4) "u"
    5) "q"
    6) "l"
    7) "x"
    8) "e"
    9) "w"
   10) "v"
192.168.163.211:6379> scan 9
1) "0"  # 游标值为0,表示遍历结束
2) 1) "n"
   2) "p"
   3) "d"
   4) "g"
   5) "s"
   6) "t"

# 如果再从0开始遍历, 则又从头开始
192.168.163.211:6379> scan 0
1) "26"
2)  1) "r"
    2) "i"
    3) "k"
    4) "z"
    5) "h"
    6) "y"
    7) "c"
    8) "o"
    9) "a"
   10) "m"

补充:渐进式遍历命令列表

  • scan: 遍历字符串类型键;
  • hscan: 遍历哈希类型键;
  • sscan:遍历集合类型键;
  • zscan:遍历有序集合类型键;

3)渐进式遍历命令优缺点;

  • 优点:解决了如 hgetall,smemebers, zrange可能产生的阻塞问题
  • 缺点:如果在scan过程中键有变化(如新增,删除,修改),那么遍历效果可能会碰到如下问题:
    • 新增的键没有遍历到,遍历出了重复键的情况; 即若键有变化,scan命令并不能遍历所有键


【8】数据库管理

【8.1】redis数据库

1)redis数据库概述:

  • redis使用数字来区分数据库,redis单个实例默认配置有16个数据库,编号从0~15;
  • 当客户端连接到redis服务器,默认连接到0号redis数据库;
  • redis数据库之间没有任何关联,如0号数据库与15号数据库可以有相同的键;


【8.1.1】redis切换数据库(仅了解)

  1. select dbNum 切换到dbNum表示的数据库
# 切换到0号数据库 
192.168.163.211:6379[15]> select 0
OK
192.168.163.211:6379> set name0 tom00
OK
192.168.163.211:6379> get name0
"tom00"

# 切换到15号数据库 
192.168.163.211:6379> select 15
OK
192.168.163.211:6379[15]> get name0
"tom0"

【显然】0号数据库与15号数据库都可以有name0这个key,但它们的值是不同的;即redis数据库之间是没有物理关联的;

在这里插入图片描述

【8.1.1】redis3.0后弱化多数据库

1)redis3.0已经弱化了多数据库功能; 如redis分布式实现redis Cluster只允许使用0号数据库,只不过为了向前兼容,多数据库功能没有被完全废弃;

2)redis3.0弱化多数据库功能原因:

  • redis是单线程;
  • 多数据库的使用方式,会让调试与运维变得可能;(开发或运维过程中,我们希望技术门槛能够简单一点,上手快一点)
  • 部分redis的客户端不支持j;

3)如何模拟实现redis多数据库:可以在一台物理机器上部署多个redis实例;



【8.2】清除数据库(谨慎使用)

1)清除数据库:

  • flushdb: 清除当前数据库;
  • flushall : 清除所有数据库;

【注意】清除数据库,谨慎使用

  • flushdb、flushall会清除所有数据,一旦误操作后果不堪设想;
  • 如果键很多,则flushdb、flushall存在阻塞redis的可能性;