实用指南:Redis 学习笔记

redis 是按照键值对的方式来存储数据的。

redis 是一个客户端-服务器结构的程序,客户端和服务器之间通过网络来进行通信。

在启动redis客户端的时候,加上一个 --raw 这样的选项,就可以使 redis 客户端尝试把二进制数据进行翻译。

加上 --raw 之前:image-20250906160734241

加上后:image-20250906160938093

redis 命令

redis 命令官方文档:Commands | Docs

redis 文档给出的语法格式说明:

[] 相当于一个独立的单元,表示可选项。

| 表示“或者”的意思。

[] 和 [] 之间,是可以同时存在的。

redis 命令不区分大小写

进入redis

redis-cli

存数据

设置一个键值对

set key value
  • key 是字符串
  • 这个命令后面可以跟很多参数,去看看官方文档

设置多个键值对

mset key value [key value...]
  • 时间复杂度O(N),这里的N指的是你输入key、value 的数量

image-20250906145834308

因为 redis 的客户端和服务器之间是靠网络来进行交互的,而网络的传输速度又比较慢,当需要设置多个 key 时,更推荐使用 mset 来进行设置。

当key不存在时,则设置成功,key不存在时,设置失败

setnx key value

设置key的过期时间

单位: 秒

setex key 秒 value

单位:毫秒

psetex key 毫秒 value

拿数据

获取一个value

get key
  • 如果当前 key 不存在,则返回 nil
  • get命令只能拿字符串类型的value

获取多个value

mget key [key...]
  • 时间复杂度O(N),这里的N指的是你输入 key 的数量

image-20250906145834308

因为 redis 的客户端和服务器之间是靠网络来进行交互的,而网络的传输速度又比较慢,当需要获取多个 value 时,更推荐使用 mget 来进行设置。


用来查询当前服务器上匹配的key

keys pattern
  • pattern:正则表达式
  • keys 命令的时间复杂度是 O(N)

判断 key 是否存在

exists key
  • exists 会返回 key 存在的个数
  • exist 命令的时间复杂度是 O(1)

删除 数据

del key [key ...]
  • del 可以一次性删除多个 key
  • del 会返回删除 key 的个数
  • del 命令的时间复杂度为O(1)

添加过期时间(秒)

expire key 秒数
  • 时间复杂度 O(1)

  • 返回值:1 表示设置成功;0表示设置失败

添加过期时间 (毫秒)

pexpire key 毫秒数
  • 时间复杂度 O(1)
  • 返回值:1 表示设置成功;0表示设置失败

查询过期时间(秒)

ttl key
  • 时间复杂度 O(1)
  • 返回值:剩余的过期时间,-1 表示没有关联过期时间,-2 表示 key 不存在

查询过期时间(毫秒)

pttl key
  • 时间复杂度(1)
  • 返回值:剩余的过期时间,-1 表示没有关联过期时间,-2 表示 key 不存在

type 查看key所对应的value的类型

type key
  • redis 中的key的类型为string

  • redis 中的value的类型可以为 none,list,zset,set,string,hash,stream。

  • 时间复杂度 O(1)

    image-20250904165618451

    stream:在redis作为消息队列时,使用这个类型的value


查看key所对应value的编码类型

object encoding key

image-20250906105724123


字符串类型

image-20250907120632395

让key所对应的value + 1

incr key

让key所对应的value + n

incrby key 数字

让key所对应的value - 1

decr key

让key所对应的value - n

decrby key 数字
  • 以上命令操作的 value 必须为整数,否则报错。若操作的 value 大于8个字节,则报错。

  • 若以上命令操作的 key 不存在,则就会把这个 key 的 value 当作 0 来使用。

  • 以上命令的返回值都为 value 加/减 操作完成后的值。例如:

让key所对应的 value +/- n

incrbyfloat key 小数
  • value 必须为数字,否则报错。

  • 若命令操作的 key 不存在,则就会把这个 key 的 value 当作 0 来使用。

  • 返回值为 value 加/减 操作完成后的值

当多个客户端同时针对key进行加减操作时,不会出现“线程安全”问题。因为 redis 采用了单线程模型


让 key 所对应的 value 的后面加上 指定的字符串

append key 字符串
  • key 对应的 value 必须为字符串类型。
  • 如果 key 不存在,则该命令等价于 set 命令
  • 返回值:字符串的长度,单位:字节

截取 字符串

getrange key start end
  • key 对应的 value 必须为字符串类型。
  • 截取范围是 [start,end],闭区间
  • 如果截取汉字,得到的结果很可能不是你想要的
  • 范围可以为负数,比如 -1 表示倒数第一个元素,下标为 len - 1 的元素
  • 返回值:截取后的字符串

替换 字符串

setrange key offset 字符串
  • key 对应的 value 必须为字符串类型。offset 表示替换开始的位置
  • 如果截取汉字,得到的结果很可能不是你想要的
  • 返回值为替换后的字符串长度
  • 当 key 不存在时:image-20250906170515165

查看字符串长度

strlen key
  • key 对应的 value 必须为字符串类型。
  • 返回值:字符串长度,单位 字节

hash 类型

image-20250907190844777

向哈希表 key 中设置一个或多个 field-value 对。

hset key field value [field value...]
  • 通过 key 可以找到 field 和 value 这个整体
  • 通过 field 可以找到 value
  • value 只能是字符串类型
  • 返回值:设置成功的个数

获取哈希表 key 中指定 field 对应的值。

hget key field
  • 返回值:
    • 成功:返回 field 对应的 value
    • 失败(keyfield 不存在):返回 (nil)

检查哈希表 key 中是否存在指定的 field

hexists key field
  • 返回值:1 表示存在,0 表示不存在

删除哈希表 key 中的一个或多个指定 field

hdel key field [field...]
  • hdel 删除的是 key 对应的 field value 整体,而 del 命令删除的是整个 key
  • 返回值:删除成功的个数

查看 key 中的所有 field

hkeys key
  • 时间复杂度: O(N),N 为 field 的个数

查看 key 中的所有 field 对应的value

hvals key
  • 时间复杂度: O(N),N 为 value的个数

查看 key 中的所有的 field 和所有的value

hgetall key
  • 时间复杂度: O(N),N 为 field - value的个数
  • 返回值:一个交替排列的 field 和 value 的列表。
    • 格式:[field1, value1, field2, value2, field3, value3, ...]

查看 key 中的一个或多个 field 对应的 value

hmget key field [field...]
  • 返回值:一个值的列表,顺序与请求的 field 顺序一致。如果某个 field 不存在,则对应返回 nil

用于增量迭代哈希表中的字段。这是安全遍历大 Hash 的唯一推荐方式

hscan key cursor [MATCH pattern] [COUNT count]
  • 参数
    • cursor:游标,从 0 开始。一次调用后返回一个新的游标,下次调用需使用此新游标,直到返回 0 表示迭代结束。
    • MATCH pattern:可选,匹配字段名的模式(通配符风格),例如 user:*
    • COUNT count:可选,建议的每次返回的元素数量(只是一个提示,实际返回可能或多或少),默认值为 10。
  • 返回值:一个包含两个元素的数组:
    1. 下一次迭代要使用的游标(一个整数)。
    2. 本次迭代返回的 field-value 对列表(也是一个列表,格式和 HGETALL 一样是交替的)。

获取哈希表 key 中的字段(field)的数量。

hlen key
  • 时间复杂度:O(1)。Redis 在内部维护了这个计数,所以获取速度极快,与字段多少无关。

  • 返回值

    • 整数:Hash 中的字段数量。
    • 0:如果 key 不存在。

在 key 对应的 field 不存在时,则设置 hash 中的字段和值

hsetnx key field value
  • 返回值

    • 1:如果字段是新字段,设置成功。
    • 0:如果字段已存在,设置失败。

hash 中的 value 加/减 整数

hincrby key field 整数
  • 返回值:执行加法操作后的新值(整数)。
  • 特殊说明
    • 如果 keyfield 不存在,会先将其值初始化为 0,再执行操作。
    • 增量 increment 可以是负数,实现减法操作。HINCRBY user:1000 score -10 (积分扣10)。

hash 中的 value 加/减 小数

hincrbyfloat key field 数字
  • 返回值:执行加法操作后的新值(字符串形式的浮点数)。

  • 特殊说明

    • HINCRBY,如果字段不存在,则先初始化为 0.0 再操作。
    • 增量 increment 可以是负值,也可以是小数(如 1.5, -0.7)。

list 类型

image-20250908113249937

添加元素(头插)

lpush key value [value ...]
  • lpush 用的是头插法

  • 只能在 key 为 list 结构时使用

  • 返回值:插入后的 list 长度


获取范围内的元素

lrange key start stop
  • 获取的范围是 [start,stop] 闭区间
  • 只能在 key 为 list 结构时使用
  • 返回值:start 到 stop 下标的元素

当 key 存在时,头插元素

lpushx key value [value...]
  • 只能在 key 为 list 结构时使用
  • 返回值:插入后的 list 长度
  • lpush 与 lpushx 的区别:image-20250907215526356

添加元素(尾插)

rpush key value [value...]
  • 只能在 key 为 list 结构时使用
  • 返回值:插入后的 list 长度

当 key 存在时,尾插元素

rpushx key value [value...]
  • 只能在 key 为 list 结构时使用
  • 返回值:插入后的 list 长度

头删 元素

lpop key [count]
  • count 表示要头删的元素个数,在 redis 6.2 及以上版本中才能生效
  • 时间复杂度:O(N),其中 N 是移除的元素个数。
  • 返回值:
    • count 未指定或为 1 时:返回被删除的元素值。如果列表为空则返回 nil
    • count 指定且大于 1 时:返回一个包含被删除元素的数组。如果列表为空则返回空数组。

尾删元素

rpop key [count]
  • count 表示要尾删的元素个数,在 redis 6.2 及以上版本中才能生效
  • 时间复杂度:O(N),其中 N 是移除的元素个数。
  • 返回值:被删除的元素

通过下标查找元素

lindex key index
  • 时间复杂度:O(N),N 为列表长度
  • 返回值:查找到的元素

在列表中某个基准值(pivot) 的前面或后面插入一个新元素

linsert key <before | after> pivot 元素
  • before 表示在基准值元素之后插入;after 表示在基准值元素之前插入。
  • 如果列表中有多个与 pivot 相等的元素,只以第一个遇到的(从左向右扫描)为基准
  • 时间复杂度:O(N),其中 N 是列表的长度。
  • 返回值:
    • 成功插入后,返回操作完成后列表的长度
    • 如果基准值 pivot 不存在于列表中,返回 -1
    • 如果列表 key 不存在,将其视为空列表,同样返回 -1

查看列表长度

llen key
  • 时间复杂度:O(1)

  • 返回值:列表长度


连续删除相同元素

lrem key count element
  • count 表示要删除元素的个数,element 表示要删除的是谁
  • 参数 count 的含义和行为:
    • image-20250908104153102
  • 时间复杂度:O(N + M),其中 N 是列表的长度,M 是实际被移除的元素个数。
  • 返回值:返回实际被移除的元素个数

移除范围外的所有元素

ltrim key start stop
  • 时间复杂度:O(N),其中 N 是列表的长度
  • 删除范围为 [start , stop] 之外的所有元素,闭区间

修改指定下标的元素

lset key index element
  • index 表示要修改的元素的索引,element 表示要改成的元素
  • 时间复杂度:O(N),其中 N 是列表的长度

set 类型

image-20250908202614233


添加元素

sadd key member [member...]
  • 返回值:本次操作插入成功的元素个数

查看所有元素

smembers key
  • 时间复杂度:O(N)

查看集合中是否存在当前元素

sismember key member
  • 判断 member 是否在当前集合中

  • 返回值:

    • 1: 表示成员是集合的成员(存在)

    • 0: 表示成员不是集合的成员(不存在),或者键本身不存在


从 key 中随机删除元素

spop key [count]
  • key: 集合的键名。
  • count(可选) 要移除并返回的成员数量。在 Redis 2.6+ 版本中支持。
  • 返回值:被删除的元素

从 key 中随机获取元素

srandmember key [count]
  • key: 集合的键名
  • count(可选) 要获取的成员数量
  • 返回值:
    • 如果未指定 countcount 为 1:返回一个随机元素。如果集合为空,则返回 nil
    • 如果指定了 count
      • 正数 (count > 0): 返回一个包含 count不重复随机元素的数组。如果 count 大于集合大小,则返回整个集合。
      • 负数 (count < 0): 返回一个包含 abs(count) 个元素的数组,允许元素重复(就像有放回的抽样)。如果集合为空,则返回空数组。

移动元素

smove source destination member
  • 参数:
    • source: 源集合的键名。
    • destination: 目标集合的键名。
    • member: 要移动的成员。
  • 把 source 中的 member 移动到 destination 中
  • 返回值:
    • 1: 表示成员存在且移动成功。
    • 0: 表示成员在源集合中不存在,移动操作未执行。

删除元素

srem key member [member...]
  • 返回值:删除成功的元素个数

计算并返回给定所有集合键的交集。即返回同时存在于所有指定集合中的成员。

sinter key [key...]
  • key: 一个或多个集合的键名。必须至少提供一个 key

  • 返回值:

    • 包含所有交集成员的列表。

    • 如果指定的任何一个键不存在,则被视为空集合。如果有一个键不存在,那么结果交集就是空集

    • 如果所有键都不存在,结果也是空集。

  • 时间复杂度:

    • O(N * M),最坏情况下的复杂度,其中 N 是最小集合的基数(元素个数),M 是集合的数量。

求交集并存储结果

sinterstore destination key [key...]
  • 参数:

    • destination: 用于存储交集结果的目标集合的键名。
    • key: 一个或多个用于计算交集的源集合的键名。必须至少提供一个 key
  • 返回值:存储到目标集合中的元素数量

  • 时间复杂度:

    • O(N * M),最坏情况下的复杂度,其中 N 是最小集合的基数(元素个数),M 是集合的数量。

集合求并集

sunion key [key...]
  • key: 一个或多个集合的键名。必须至少提供一个 key
  • 返回值:求并集之后的结果
  • 时间复杂度:O(N),其中 N 是所有给定集合的成员数量总和。

集合求并集并保存

sunionstore destination key [key...]
  • 功能:计算给定所有集合键的并集,并将结果保存到 destination 指定的集合中。
  • 返回值:存储到目标集合中的并集元素的数量
  • 时间复杂度:O(N),其中 N 是所有给定集合的成员数量总和。

求差集

sdiff key [key...]
  • 功能:计算并返回第一个集合与其他所有集合的差集。即返回存在于第一个集合中,但不在任何后续集合中的成员
  • 返回值:包含差集成员的列表
  • 时间复杂度:O(N),其中 N 是所有给定集合的成员数量总和。

求差集并保存

sdiffstore destination key [key...]
  • 功能:计算集合的差集,并将结果保存到 destination 指定的集合中
  • 返回值:存储到目标集合中的差集元素的数量
  • 时间复杂度:O(N),其中 N 是所有给定集合的成员数量总和。

redis 常用数据结构

在redis中使用string等数据类型时,redis对这些类型做了特殊优化,它的内部编码方式可能不是string等数据类型,而是其他类型,但是依然保证承诺有效。

image-20250904172452044

string 类型:

  • raw 可以存储较长的字符串,各种类型的字符串
  • int 存储数字类型的字符串
  • embstr 存储比较短的字符串

hash 类型:

  • hashtable:最基本的哈希表
  • ziplist:压缩列表,当哈希表中的数据较少时,做的特殊优化,可以节省空间

list 类型:

  • linkedlist:基本的链表
  • ziplist:压缩列表

从 redis 3.2 版本开始,list 的内部编码使用的是 quicklist(将linkedlist与ziplist的优点相结合)

set 类型:

  • hashtable:最基本的哈希表
  • intset:存储整数的set

zset 类型:

  • skiplist:跳表,每个节点上有多个指针域。从调表上查询元素的时间复杂度为 O(log N)
  • ziplist:压缩列表

redis 的应用场景

作为缓存

redis 可以用来作为缓存,存储热点数据。

image-20250907130332227

当服务器接收到查询请求时,请求会先去redis中查找,如果查到了,直接返回响应。如果没查到,则去 mysql 中查询,查询到数据后,将数据写入到 redis 中,之后返回响应。

问题:redis 每次没查到数据,之后会将数据写入 redis 中,那么 redis 使用的内存会不会一直增加?

答:每次将查询到的数据写入 redis 中时,可以给数据添加一个超时时间。其次,redis 也提供了内存淘汰机制。


作为计数器和排行榜

redis 也可以用来作为计数器

  • 场景:文章点赞数、视频播放量、用户积分实时排行榜。

  • 实现:使用 INCR, DECR 命令实现原子性计数。使用 ZSET(有序集合)可以轻松实现排行榜,能快速进行排名、按分数范围查询等操作。


会话存储

  • 场景:在集群环境下存储用户登录会话信息。
  • 实现:将Web应用中的Session(用户登录状态、购物车信息等)集中存储在Redis中。
  • 优势:相比存储在应用服务器内存中,这样做使得用户请求可以发送到集群中的任何一台服务器,都能获取到登录状态,实现了服务的无状态化,便于水平扩展。

redis 小知识

redis 删除策略

  • 定期删除,每过一段时间,redis会随机抽查一些设置过 过期时间的key,把过期的key删除

  • 惰性删除相结合,当一个key过期时,redis并不会立即删除这个key,而是等到下次用到时,再进行删除操作。

经过上面两个删除策略后,仍然会残留很多过期的key。为了解决这个问题,redis又引入了内存淘汰机制。


redis 是单线程模型,它只用一个线程来处理命令请求。也不是说redis服务器内部真的只有一个线程,其实也有多线程,只不过多线程是用来处理网络IO的。

因为redis是单线程模型,所以当两个自增请求同时到达并且还是操作同一个对象时,redis 不会出现只自增了一次的情况,因为虽然这两个请求都是同时到达的,但是实际上它们是串行执行的(因为只有一个线程在处理请求)。

单线程模型的优点:

  • 不需要加锁解锁,减少了性能开销

  • 不会出现线程安全问题

  • 代码简单易懂,维护起来方便

单线程模型的缺点:

  • 当一条命令耗费时间过长时,会阻塞其他命令的执行。

redis为什么就可以使用单线程模型呢?

因为 redis 的核心业务逻辑都是短平快的,不太消耗 cpu,也就不太吃多核了。


redis 是单线程模型,为啥效率还这么高、速度这么快呢?

因为 redis 操作数据都是在内存中完成的,而在内存中处理数据本来就很快。redis的核心功能也比较简单,就是对数据简单的处理一下。redis 的瓶颈不在于 cpu,而在于 网络 IO 的读写速度,redis 采取了 epoll 这样的 IO 多路复用机制。再一个,redis 的数据结构都经过优化,对数据操作的时间复杂度都在 O(1)或 O(logN)之内。还有,单线程模型它不需要加锁解锁等操作,不需要额外消耗 cpu。

IO多路复用:IO多路复用——深入浅出理解select、poll、epoll的实现 - 知乎

简单来说就是一个线程处理多个 socket


redis 不会将存储的数据进行编码转换,这也就意味着出现乱码的概率降低。redis中存储的都是二进制数据。


posted @ 2025-09-10 10:16  wzzkaifa  阅读(13)  评论(0)    收藏  举报