Redis 入门详解:核心特性、重要文件与全局命令实战

目录

  • [1. 前置知识](#1. 前置知识)
    • [1.1 Redis 的特性](#1.1 Redis 的特性)
    • [1.2 Redis 的重要文件及其作用](#1.2 Redis 的重要文件及其作用)
  • [2. Redis 全局命令](#2. Redis 全局命令)
    • [2.1 基本全局命令](#2.1 基本全局命令)
  • [3. 小结](#3. 小结)
  • [4. 经典面试题](#4. 经典面试题)
    • [4.1 Redis 为什么这么快?](#4.1 Redis 为什么这么快?)
    • [4.2 Redis 是单线程还是多线程?](#4.2 Redis 是单线程还是多线程?)
    • [4.3 Redis 的持久化方式有哪些?](#4.3 Redis 的持久化方式有哪些?)
    • [4.4 KEYS * 为什么在生产环境禁用?](#4.4 KEYS * 为什么在生产环境禁用?)
    • [4.5 EXISTS、DEL 为什么可以一次传多个 key?](#4.5 EXISTS、DEL 为什么可以一次传多个 key?)
    • [4.6 EXPIRE 和 TTL 的返回值分别代表什么?](#4.6 EXPIRE 和 TTL 的返回值分别代表什么?)
    • [4.7 Redis 如何知道哪些键已经过期?](#4.7 Redis 如何知道哪些键已经过期?)
    • [4.8 定时器有哪些常见实现方式?](#4.8 定时器有哪些常见实现方式?)
    • [4.9 Redis 适合做什么?不适合做什么?](#4.9 Redis 适合做什么?不适合做什么?)
    • [4.10 TYPE 命令的作用和返回值是什么?](#4.10 TYPE 命令的作用和返回值是什么?)

✅️ 1.前置知识

1.1 Redis的特性(了解 - 后续博文会详解)

① 速度快

一般情况下,Redis的执行速度非常快,官方报道的数字是每秒钟可以达到10万级别的读取速度,其主要原因有以下几点:

  • Redis的所有数据都是放到机器的内存中的,数据放到内存中进行操作时Redis速度快最主要的原因。
  • Redis其实现语言是C语言,众所周知c语言更加靠近底层,自然执行速度会比较快。
  • Redis使用单线程,避免了多线程对资源的竞争以及多线程切换带来的开销。

② 丰富的功能

  • 提供了键过期的功能,可以用来实现缓存
  • 提供了发布订阅功能,可以用来实现消息系统
  • 支持Lua脚本功能,可以利用Lua创造出新的Redis命令
  • 提供简单的事务功能,能够在一定程度上保证事务特性
  • 提供了流水线(Pipeline)功能,这样客户端能够机爱你个一批命令一次性传到Redis,减少了网络的开销

③ 客户端支持的语言多

Redis提供了简单的TCP通信协议,许多编程语言可以方便的接入Redis,Redis几乎涵盖了主流的编程语言,后续博文会针对C++和Java两种主流语言如何接入和使用Redis做详细的介绍和实操。

④ 持久化

众所周知,内存具有易失性质,一旦机器发生故障或者断电,内存中的数据就会丢失,所以数据放到内存中是不安全的(一般来看,说法不绝对),为了预防这个问题,Redis提供了两种持久化机制:RDB和AOF,Redis使用这两种策略将内存中的数据保存到硬盘中实现数据的持久化。

⑤ 主从复制架构(Replication)

Redis提供了复制功能,实现了多个相同数据的Redis副本(Replica),复制功能时分布式Redis的基础。

⑥ 高可用和分布式

Redis提供了高可用实现的Redis哨兵,能够保证Redis节点的故障发现和故障自动转移。也提供了Redis集群(Redis Cluster),是真正的分布式实现,提供了高可用,读写和容量的扩展性。

⑦ Redis的使用场景

  • 缓存(Cache)
    缓存机制随处可见,缓存的合理使用不仅仅可以加快数据的访问数据速度,而且能够有效的降低候选数据库的压力。Redis提供了键值过期时间设置,并且也提供了灵活控制最大内存和内存溢出后的淘汰策略。
  • 排行榜系统
    排行榜系统几乎存在于所有的网站,Redis提供了列表和有序集合的结构来实现各种排行榜系统。
  • 计数器应用
    计数器在网站中非常重要,比如视屏网站有播放数,电商有浏览数,为了保证数据的实时性,每一次播放和浏览都要做加一的操作,Redis天然支持计数功能而且计数的性能也非常好。
  • 社交网络
    社交平台的点赞,粉丝,推送等等操作时社交网络中的必备功能,由于社交网络的访问量通常比较大,传统的关系型数据库不太适合存储这些类型的数据,而Redis提供的数据机构可以比较容易的实现这些功能。
  • 消息队列系统
    Redis提供的发布订阅功能和阻塞队列的功能,虽然相对于专业的消息队列还不够强大,但是对于一般的消息队列功能基本能够满足。

📌 Rerdis不能做什么?

1.我们知道Redis的数据是放在内存中的,尽管现在的内存已经足够便宜(对于一些大公司来说),但是如果数据量非常大,比如每天有几亿的用户行为数据,使用Redis来存储的话,经济成本相当的高。

2.对于视频网站来说,视频基本信息需要经常操作,而用户的观看记录不一定是经常需要访问的数据,如果将用户观看记录(冷数据)放到Redis上,基本上对于内存来说就是一种浪费,但是对于视频基本信息(热数据)放到Redis中加速读写,也可以减轻后端存储的负载,可以说事半功倍。

1.2 Redis的重要文件及其作用

启动/停止命令或者脚本

bash 复制代码
/usr/bin/redis-benchmark
/usr/bin/redis-check-aof -> /usr/bin/redis-server
/usr/bin/redis-check-rdb -> /usr/bin/redis-server
/usr/bin/redis-cli
/usr/bin/redis-sentinel -> /usr/bin/redis-server
/usr/bin/redis-server
/usr/libexec/redis-shutdown

重要文件解读:

1.redis-server是Redis服务器程序的

2.redis-check-aof,redis-check-rdb,redis-sentinel都是redis-server的软连接。redis-check-aof是修复AOF文件用的工具,同理redis-check-rdb是修复RBD文件的工具,redis-sentlnel是Redis哨兵程序。

3.redis-cli是命令行客户端程序

4.redis-benchmark用于对Redis做性能基准测试的工具。

5.redis-shutdown是用于停止Redis的专用脚本。

配置文件

  • /etc/redis.conf 是 Redis 服务器的配置⽂件。
  • /etc/redis-sentinel.conf 是 Redis Sentinel 的配置⽂件。

持久化文件存储目录

bash 复制代码
# Redis持久化生产的RDB和AOF文件都默认生成与该目录下
/var/lib/redis/

日志文件目录

bash 复制代码
/var/log/redis/

/var/log/redis/目录下会保存Redis运行期间生产的日志文件,默认按天进行分割,并且会将一定日期的日志文件使用gzip格式压缩保存。

Redis命令行客户端

第一种交互方式:通过redis-cli -h IP -p port 的方式连接到Redis服务,后续所有的操作都是通过交互式的方式实现.

bash 复制代码
[root@localhost lijing]# redis-cli -h 127.0.0.1 -p 6379
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> set key value
OK
127.0.0.1:6379> get key
"value"
127.0.0.1:6379> 

第二种是命令方式:用redis-cli -h 主机号 -p 端口号 {需要执行的命令} 就可以直接得到返回的结果。

bash 复制代码
[root@localhost lijing]# redis-cli -h 127.0.0.1 -p 6379 ping
PONG
[root@localhost lijing]# redis-cli -h 127.0.0.1 -p 6379 set key king
OK
[root@localhost lijing]# redis-cli -h 127.0.0.1 -p 6379 get key
"king"

🌲 注意 :由于我们连接的Redis服务位于本地127.0.0.1,端口使用的是默认的6379端口,所以可以省略-h 主机 -p 端口号 .

下图是Redis客户端和服务器的交互过程:

✅️ 2.Redis全局命令

📌笔记:

使用终端窗口来操作Redis时,需要先执行如下步骤(针对Linux系统):

1.先执行命令systemctl start redis 来启动redis服务器端

2.然后使用redis-cli -h {host} -p {port} 命令来启动客户端并连接Redis服务器端,在客户端使用对应的Redis命令来完成对应的操作。

3.当使用完成Redis后,可以通过systemctl stop redis 来关闭服务器,这步操作仅限于学习阶段,实际生产环境中不能做这步操作。也可以在客户端使用quit命令退出redis客户端,而不需要关闭redis服务器。

2.1 基本全局命令

Redis有5种主要的数据结构,他们都是键值对的值,对于键来说有一些通用的命令。

📚 笔记:

Redis都是按照键值对的方式来存储数据的,get 和 set是redis最核心的两个命令,

get:根据key来获取对应的value

set:把key及其对应的value存进去

set key1 value1 : key1 和 value1都是字符串,可以不用加双引号,亦可以加双引号,也可以加单引号,Redis命令不区分大小写,即set key value 等价于 SET key value。

get key 返回key对应的value,如果不存在句返回一个nil

基本语法:

bash 复制代码
# 返回满足pattern的key
KEYS pattern
pattern:是包含特殊字符的字符串,存在的意义就是去描述另外的字符串张啥样的。

pattern示例如下:

  • h?llo匹配 hello ,hallo 和 hxllo
  • h*llo 匹配hllo 和 hxxxlo
  • h[ae]llo 匹配 hello 和 hallo ,但是不匹配hillo
  • h[^e]llo 匹配hallo.hbllo...但是不匹配hello
  • h[a-b]llo 匹配 hallo 和 hbllo

📌注意

1.上述的匹配规则不需要背诵,使用时去查询即可

2.KEYS命令的时间复杂度是O(N),所以在生成环境中禁止使用keys命令,尤其是大杀器 keys *

因为生成环境中的key非常多,而redis是一个单线程服务区,执行keys * 的时间非常长,这会导致其他命令阻塞,客户端长时间得不到响应,这是非常严重的。

keys命令示例:

bash 复制代码
[root@localhost lijing]# redis-cli
127.0.0.1:6379> mset first Tom lastname Stun age 18
OK
127.0.0.1:6379> keys *name
1) "lastname"
127.0.0.1:6379> keys a??
1) "age"
127.0.0.1:6379> keys *
1) "first"
2) "key"
3) "lastname"
4) "age"

EXISTS命令:判断某个key是否存在。

语法:EXISTS key [key ...]

时间复杂度:O(1)

返回值:key存在的个数

示例:

bash 复制代码
127.0.0.1:6379> set key1 "hello"
OK
127.0.0.1:6379> exists key1
(integer) 1
127.0.0.1:6379> exists key2
(integer) 0
127.0.0.1:6379> set key2 "worls"
OK
127.0.0.1:6379> exists key1 key2 key3
(integer) 2

🐼将命令分开的写法,会产生更加多轮次的网络通信,这样会导致效率比较低下,成本比较高 (相比于直接操作内存),Redis自身是非常清楚这个问题,所以Redis命令支持Pipeline,即一次就能够操作多个key的多种操作。

DEL:删除某个key及其对应的value,可以一次删除一个或者多个key

语法:DEL key [key ...]

时间复杂度:O(1)

返回值:删除掉的key的个数

示例:

bash 复制代码
127.0.0.1:6379> set key1 "hello"
OK
127.0.0.1:6379> set key2 "world"
OK
127.0.0.1:6379> DEl key1 key2
(integer) 2
127.0.0.1:6379> DEL key
(integer) 0

📚 笔记

Redis主要的场景就是作为缓存,减去数据库的访问压力。此时的Redis中存的只是有些热点数据,全量数据存放在数据库中,此时,如果把Redis中的key删除几个,一般来说问题不大,但是如果把Redis缓存中的所有的数据或者一大半的数据都删除了,这问题就大了,具体问题后面分析。

EXPIRE:为指定的key添加秒级的过期时间(Time To Live TTL),key存活时间超过了设置的时间,被设置了过期时间的键值对就会被自动删除。

语法:EXPIRE key seconds 、PEXPIRE key millseconds

时间复杂度:O(1)

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

示例:

bash 复制代码
127.0.0.1:6379> set mykey "hello"
OK
127.0.0.1:6379> expire mykey 10
(integer) 1
127.0.0.1:6379> TTL mykey
(integer) 3

📚笔记

很多应用场景都是有时间限制的,比如:

1.验证码

2.点外卖的优惠券在指定时间内是有效的

3.基于Redis实现的分布式锁,为了避免出现不能正确解锁的情况,通常会在加速的时候设置一下过期时间(所谓 的使用Redis作为分布式锁,就是给redis中写一个特殊的key value键值对)

TTL:获取指定key的过期时间,秒级。

语法:TTL key

时间复杂度:O(1)

返回值:剩余过期时间。-1表示没有关联过期时间,-2表示key不存在。

bash 复制代码
127.0.0.1:6379> set mykey "hello"
OK
127.0.0.1:6379> expire mykey 10
(integer) 1
127.0.0.1:6379> TTL mykey
(integer) 3

EXPIRE和TTL都有对应的支持毫秒为单位的版本:EXPIRE和PTTL.

键的过期机制:

✏️笔记

定时器:在某个时间到达之后,执行指定的任务,设置过期时间的key必须存在.

定时器实现定期删除key:

1.基于时间轮实现的定时器

时间轮本质上是一个环形数组,数组中的每一个格子称为一个槽位。每个槽位上挂着一个任务链表,链表中的每个元素代表一个到期后需要执行的任务。时间轮会按固定周期推进,比如每 100ms 走一格。指针每移动一格,就取出当前槽位任务链表中的任务,判断是否到期并执行。

用一个 300ms 过期的 Key 举例

  • 假设时间轮的指针每 100ms 移动一格:
  • 现在要添加一个 300ms 后过期 的 Key。
  • 因为每格代表 100ms,所以 300ms 对应 3 个刻度。
  • 系统会把这个Key对应的删除任务挂到当前指针位置往后第 3 个槽位的任务链表中。
  • 时间轮每过 100ms 移动一格,指针走到第 3 个槽位时,就会尝试执行该槽位链表上的任务。
  • 此时这个 Key 的过期时间已到,该槽位上的链表上的删除任务会被执行一次。

2.基于优先级级队列/堆实现定时器

先设置三个过期时间:set key1 12:00 set key2 13:00 set key3 14:00

在Redis过期的key中,就可以通过"过期时间越早,优先级越高",现在嘉定有很多key都设置了过期时间,就可以把这些key加入到一个优先级队列中,执行优先级规则为:过期时间早的,先出队列,即队首元素就是最早要过期的key.此时定时器只要分配一个线程,让这个线程去检查队首元素,看是否过期。如果队首元素还没有过期,后续元素一定没有过期。

此时扫描线程不需要遍历所有的key,只需要看这个队首元素即可。

另外在扫描线程检查队首元素过期时间的时候,也不能检查的太频繁 - 此时的做法就是根据当前时间和队首元素的过期时间,设置一个等待 -- 当时间差不多了,系统就会唤醒这个线程。

此时扫描线程不需要高频的扫描队首元素,把cpu的开销也节省下来。

万一线程休眠的时候,来了一个新任务,是11:30执行~~

可以在新任务添加的时候,唤醒一下刚才的线程--重新检查队首元素,再根据时间差距重新调整阻塞时间即可。
📌经典面试题:

Redis中可能会同时存在很多的key,这些key中可能会有很大一部分都有过期时间,此时,redis服务器咋知道哪些可以是已经过期要被删除,哪些key还没有过期?

如果直接遍历所有的key是不现实的,效率非常低。

所以Redis整体的策略是:

定期删除和惰性删除 两种方式相配合使用

惰性删除 :假设这个key已经到过期时间了,但是暂时还没有删除它,key还存在,紧接着,后面的一次访问,正好用到了这个key,于是这次访问就会让Redis服务器触发删除key的操作,同时返回一个nil.

定期删除 :每次抽取一部分key,验证过期时间,保证这个抽取时间的过程足够快。

追问 :为什么这里对于定期删除的时间会有明确的要求?

因为Redis是一个单线程程序,主要的任务(处理这个命令的任务,比如扫描过期key...)。如果扫描过期key这个任务消耗的时间久了,就可能会导致正常处理请求命令被阻塞(产生类似于keys * 这样的效果了)

即使有了上面两种策略结合,但是整体的效果一般!

TYPE:返回key对应的数据类型

语法:TYPE key

时间复杂度:O(1)

返回值:none,string,list,set,zset,hash 和 stream

示例:

bash 复制代码
127.0.0.1:6379> set key1 "value"
OK
127.0.0.1:6379> LPUSH key2 "value"
(integer) 1
127.0.0.1:6379> SADD key3 "value"
(integer) 1
127.0.0.1:6379> type key1
string
127.0.0.1:6379> type key2
list
127.0.0.1:6379> type key3

✅️ 3.小结

本文围绕 Redis 的入门知识展开,可以归纳为三条主线:

  • 为什么快、能做什么 :Redis 基于内存存储、由 C 语言实现、采用单线程模型,因此具备十万级每秒的读写能力;同时提供键过期、发布订阅、Lua 脚本、简单事务、Pipeline、RDB/AOF 持久化、主从复制、哨兵与集群等能力,可覆盖缓存、排行榜、计数器、社交网络、消息队列等典型场景。
  • 装在哪里、怎么连 :redis-server 是服务端程序,redis-cli 是命令行客户端,redis-benchmark 用于性能基准测试,redis-check-aof / redis-check-rdb 用于修复持久化文件,redis-sentinel 是哨兵程序;配置文件位于 /etc/redis.conf,持久化文件默认落在 /var/lib/redis/,日志默认落在 /var/log/redis/。
  • 怎么操作键 :KEYS 按 pattern 匹配键(O(N),生产环境禁用 keys *),EXISTS 判断键是否存在,DEL 删除键,EXPIRE / PEXPIRE 设置过期时间,TTL / PTTL 查询剩余存活时间,TYPE 返回键对应的数据类型。过期键的清理采用惰性删除 + 定期删除相结合的策略,并借助时间轮或优先级队列实现定时器。

一句话概括:Redis 是一个以内存为核心、以键值对为模型、以单线程换取简单与高效的高性能数据结构服务器。

✅️ 4.经典面试题

4.1 Redis 为什么这么快?

参考答案:

  1. 纯内存操作 :数据存放在内存中,读写不需要磁盘 I/O,这是最主要的原因。
  2. C 语言实现:更贴近底层,执行效率高。
  3. 单线程模型:避免了多线程的锁竞争与上下文切换开销。
  4. 高效的数据结构与 I/O 多路复用 :底层使用 epoll 等机制,单线程也能处理大量连接。
  5. 协议简单 :基于简单的 TCP 通信协议,解析成本低。
4.2 Redis 是单线程还是多线程?单线程为什么还这么快?

参考答案:

通常说的「Redis 单线程」指的是命令处理(核心网络 I/O 与键值读写)由单线程完成 。Redis 6.0 之后引入了多线程 I/O,用于分担网络读写,但命令执行仍然是单线程,因此不存在并发修改数据的线程安全问题。

单线程依然快的原因:内存操作、C 语言实现、无锁无切换开销、I/O 多路复用、数据结构经过专门优化。

4.3 Redis 的持久化方式有哪些?有什么区别?

参考答案:

  • RDB:将某一时刻的内存数据快照写入磁盘,文件紧凑、恢复速度快,但两次快照之间的数据可能丢失。
  • AOF :以追加方式记录每条写命令,数据安全性更高(可配置 appendfsync 策略),但文件体积大、恢复速度慢。

生产环境常见做法是两者结合 :用 RDB 做定期全量备份,用 AOF 保证数据尽量不丢。

4.4 KEYS * 为什么在生产环境禁用?

参考答案:

KEYS 命令的时间复杂度是 O(N),需要遍历所有键。Redis 命令执行是单线程的,当键数量很大时,keys * 会长时间占用主线程,导致其他命令全部阻塞、客户端长时间得不到响应,属于典型的「大杀器」命令。

替代方案:使用 SCAN 命令进行游标式渐进遍历,不会长时间阻塞主线程。

4.5 EXISTS、DEL 的时间复杂度是 O(1),为什么可以一次传多个 key?

参考答案:

单次判断或删除一个键是 O(1);传入多个 key 时,整体复杂度为 O(N)(N 为传入的键个数)。一次传多个键的意义在于减少网络往返次数 ,把多轮 RTT 合并为一轮,从而提升整体效率,这与 Pipeline 的思路一致。

4.6 EXPIRE 和 TTL 的返回值分别代表什么?

参考答案:

  • EXPIRE key seconds:返回 1 表示设置成功,返回 0 表示设置失败(如键不存在)。
  • TTL key:返回剩余过期时间(秒);返回 -1 表示该键存在但没有关联过期时间;返回 -2 表示该键不存在。

对应的毫秒级版本为 PEXPIRE 和 PTTL。

4.7 Redis 如何知道哪些键已经过期?过期键是如何删除的?

参考答案:

如果遍历所有键判断是否过期,效率极低,因此 Redis 采用惰性删除 + 定期删除相结合的策略:

  • 惰性删除 :键到期后并不立即删除,等到下一次访问该键时,才触发删除并返回 nil。优点是 CPU 友好,缺点是过期键可能长期占用内存。
  • 定期删除:每隔一段时间随机抽取一部分设置了过期时间的键,检查并删除其中已过期的键,同时限制单次扫描的耗时。

追问:为什么定期删除要限制执行时间?

因为 Redis 命令处理是单线程的,如果扫描过期键耗时过长,会阻塞正常请求,产生类似 keys * 的效果。

4.8 定时器有哪些常见实现方式?

参考答案:

  • 时间轮 :本质是环形数组,每个槽位挂一个任务链表,指针按固定周期推进(如每 100ms 一格)。添加一个 300ms 后过期的键时,将其删除任务挂到当前指针往后第 3 个槽位,指针走到该槽位时执行任务。适合大量定时任务、精度要求不极端的场景。
  • 优先级队列 / 堆:按过期时间排序,过期时间越早优先级越高,队首即最早过期的键。扫描线程只需检查队首元素,队首未过期则后续一定未过期;并根据队首过期时间设置阻塞等待,避免高频轮询。新任务入队时唤醒线程重新计算等待时间。
4.9 Redis 适合做什么?不适合做什么?

参考答案:

适合:缓存、排行榜、计数器、社交网络中的点赞/粉丝关系、轻量级消息队列(发布订阅、阻塞队列)等访问量大、对实时性要求高的场景。

不适合:

  • 海量冷数据存储:内存成本高,把每天几亿条用户行为数据全放 Redis 经济上不可行。
  • 冷数据长期驻留:如视频观看记录这类不常访问的数据放内存是一种浪费,应把热数据放 Redis 加速、冷数据留在磁盘数据库。
4.10 TYPE 命令的作用和返回值是什么?

参考答案:

TYPE key 用于返回指定键对应的数据类型,时间复杂度 O(1)。返回值包括 none(键不存在)、string、list、set、zset、hash 和 stream。在排查数据异常或编写通用工具脚本时非常实用。

set

相关推荐
敲上瘾22 分钟前
Qt Widgets入门:项目模板解析与Hello World
开发语言·数据库·c++·qt·ui·qt6.3
小南家的青蛙1 小时前
基于Spring Boot 2.x开发的微服务访问Redis的方案
redis·springboot
bksczm1 小时前
Redis 持久化:RDB 快照与 AOF 日志
数据库·redis·mybatis
码途行者2 小时前
网站的SQL 注入:从攻击原理到防御措施
开发语言·sql·php
旖旎夜光2 小时前
【C++ 基础入门】C++ 多态(中):虚函数表、多态原理与动静绑定
开发语言·c++·笔记·学习
你看我名字2 小时前
产品参数昨天还对,今天已过期:数眼智能与大模型应用如何做资料版本校验
开发语言·架构
老秦包你会3 小时前
计算机网络第二课 ----Socket(套接字)编程预备
开发语言·计算机网络·php
旖旎夜光3 小时前
【C++ 基础入门】C++多态(下):单/多继承虚表、多态补充与 21 道高频题
开发语言·c++·笔记·学习
IT规划师3 小时前
Redis 数据类型全解 - Stream 深度:消费者组、Pending 队列与死信处理(十)
redis·.net