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

devtools/2024/12/23 8:20:05/

文章目录

  • 【README】
  • 【1】redis通用命令与数据结构
    • 【1.1】通用命令
    • 【1.2】数据结构与内部编码
    • 【1.3】redis单线程架构
      • 【1.3.1】redis单线程优缺点
  • 【2】字符串(值的类型为字符串)
    • 【2.1】常用命令
      • 【2.1.1】设置值
      • 【2.1.2】获取值
      • 【2.1.3】批量设置值
      • 【2.1.4】批量获取值
      • 【2.1.5】计数
    • 【2.2】不常用命令
      • 【2.2.1】追加值
      • 【2.2.2】字符串长度
      • 【2.2.3】设置并返回原值
      • 【2.2.4】设置指定位置的字符
      • 【2.2.5】获取部分字符串
      • 【2.2.6】值为字符串类型的命令总结
    • 【2.3】内部编码
    • 【2.4】值为字符串类型的典型使用场景
  • 【3】哈希(值的类型为哈希)
    • 【3.1】命令
      • 【3.1.1】设置值
      • 【3.1.2】获取值
      • 【3.1.3】删除field
      • 【3.1.4】计算field个数
      • 【3.1.5】 批量设置或获取域值对
      • 【3.1.6】判断field域是否存在
      • 【3.1.7】获取所有域和值
      • 【3.1.8】自增操作
      • 【3.1.9】计算value的字符串长度
      • 【3.1.10】值为哈希类型的命令总结
    • 【3.2】内部编码
    • 【3.3】使用场景
      • 【3.3.1】 哈希类型与关系型数据库的不同
      • 【3.3.2】缓存用户信息的方法
  • 【4】列表(值的类型为列表)
    • 【4.1】命令
      • 【4.1.1】添加元素
      • 【4.1.2】查找
      • 【4.1.3】删除
      • 【4.1.4】修改
      • 【4.1.5】阻塞
      • 【4.1.6】值为列表类型的命令总结
    • 【4.2】内部编码
    • 【4.3】使用场景
      • 【4.3.1】消息队列
      • 【4.3.2】文章列表
      • 【4.3.3】列表类型的使用场景总结
  • 【5】集合(值的类型为集合)
    • 【5.1】命令
      • 【5.1.1】集合内操作
      • 【5.1.2】集合间操作
      • 【5.1.3】值为集合类型的命令总结
    • 【5.2】内部编码
    • 【5.3】使用场景
      • 【5.3.1】集合使用场景总结
  • 【6】有序集合
    • 【6.1】命令
      • 【6.1.1】有序集合内命令
      • 【6.1.2】有序集合间的操作命令
      • 【6.1.3】有序集合类型的命令总结
    • 【6.2】内部编码
    • 【6.3】使用场景
  • 【7】键管理
    • 【7.1】单个键管理
      • 【7.1.1】键重命名
      • 【7.1.2】随机返回1个键
      • 【7.1.3】键过期
    • 【7.2】遍历键
      • 【7.2.1】全量遍历键-keys(不建议)
      • 【7.2.2】渐进式遍历-scan(推荐-常用)
  • 【8】数据库管理

【README】

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

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



redis_11">【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"


redis_130">【1.3】redis单线程架构

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

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

在这里插入图片描述

redis_141">【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 keyKey的值自增O(1)
decr keyKey的值自减O(1)
incrby key incrementKey的值增加incrementO(1)
decrby key incrementKey的值减少incrementO(1)
incrbyfloat key incrementKey的值增加incrementO(1)
append key value向key的值的尾部追加valueO(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) 1192.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) 2192.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
OK192.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) 1192.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域的值为valueO(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 incrementkey的field域自增给定值increment(整型)O(1)
hincrbyfloat key field incrementkey的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前或后插入元素valueO(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) 2192.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计算集合交集并存储到key3O(m*k) m是键个数,k是多个集合中元素最少的个数
集合运算sunionstore key3 key1 key2计算集合并集并存储到key3O(k) k是多个集合元素个数和
集合运算sdiffstore key3 key1 key2计算集合差集并存储到key3O(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 withscores1) "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 withscores1) "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 withscores1) "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 withscores1) "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…]计算交集并存储结果为keyO(nk)+O(mlogm) n是最小有序集合成员个数,k是有序集合的个数,m是结果集成员个数
集合运算zunionstore result_key numkeys key [key…]计算并集并存储结果为keyO(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】数据库管理

redis_2423">【8.1】redis数据库

1)redis数据库概述:



redis_2435">【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数据库之间是没有物理关联的;

在这里插入图片描述

redis30_2461">【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存在阻塞redis的可能性;

http://www.ppmy.cn/devtools/144632.html

相关文章

AI应用-本地模型实现AI生成PPT(简易版)

文章目录 前言技术栈效果展示 一、实现思路二、实现步骤1.本地安装marp-cli2.后端实现3.前端实现 三、代码地址及说明 前言 在许多项目中&#xff0c;生成 PPT 是常见的需求&#xff0c;尤其在教育和报告展示中。传统的生成 PPT 的方法需要手动创建&#xff0c;而使用生成模型…

支付域——清结算系统体系

摘要 本文深入探讨了支付清算的基础知识和跨机构清算原理&#xff0c;涵盖了组织、账户、支付工具和系统的基础&#xff0c;支付流程的模型&#xff0c;以及支付清算的全局实现。文章还详细介绍了支付机构的五大业务和支付系统的总架构&#xff0c;并通过案例分析了支付清算的…

【原生js案例】移动端如何实现页面的入场和出场动画

好的css动画&#xff0c;能给用户体验带来很大的提升&#xff0c;同时也能增加app的趣味性&#xff0c;给人眼前一亮的感觉。那如何实现这种全屏的弹窗入场和退场的动画 实现效果 代码实现 UI样式美化 #musicDetails{width: 100%;height: 100%;top:0;left:0;position: absol…

聊聊开源的虚拟化平台--PVE

原文链接&#xff1a;聊聊开源的虚拟化平台–PVE PVE&#xff08;Proxmox Virtual Environment&#xff09;是一种开源的虚拟化平台&#xff0c;支持容器化&#xff08;LXC&#xff09;和虚拟机&#xff08;KVM&#xff09;&#xff0c;可用于创建和管理虚拟化环境。它基于Debi…

【Linux系统编程】:信号(2)——信号的产生

1.前言 我们会讲解五种信号产生的方式: 通过终端按键产生信号&#xff0c;比如键盘上的CtrlC。kill命令。本质上是调用kill()调用函数接口产生信号硬件异常产生信号软件条件产生信号 前两种在前一篇文章中做了介绍&#xff0c;本文介绍下面三种. 2. 调用函数产生信号 2.1 k…

基于PWLCM混沌映射的麋鹿群优化算法(Elk herd optimizer,EHO)的多无人机协同路径规划,MATLAB代码

一、麋鹿群优化算法EHO 基本概念 麋鹿群优化算法&#xff08;EHO&#xff0c;Elephant Herding Optimization&#xff09;是2024年提出的一种启发式优化算法&#xff0c;它的灵感来自麋鹿群的繁殖过程。麋鹿有两个主要的繁殖季节&#xff1a;发情和产犊。在发情季节&#xff0…

duilib.dll报错崩溃?一文学会如何解决!

duilib.dll是一个动态链接库&#xff08;DLL&#xff09;文件&#xff0c;属于DuiLib&#xff08;DirectUI Library&#xff09;项目的核心组件。DuiLib是一个开源的C UI框架&#xff0c;专为Windows应用程序提供高效、灵活和可定制的用户界面解决方案。 duilib.dll包含了一系…

如何将 Java 微服务引入云

所有公司都是软件公司&#xff0c;企业将始终面临保持用户和应用程序之间集成可扩展、高效、快速和高质量的挑战。为了解决这个问题&#xff0c;云、微服务和其他现代解决方案越来越多地出现在架构决策中。 问题是&#xff0c;Java 是否准备好在企业环境中处理这些不同的概念&…