目录
- [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和hxlloh*llo匹配hllo和hxxxloh[ae]llo匹配hello和hallo,但是不匹配hilloh[^e]llo匹配hallo.hbllo...但是不匹配helloh[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 为什么这么快?
参考答案:
- 纯内存操作 :数据存放在内存中,读写不需要磁盘
I/O,这是最主要的原因。 C语言实现:更贴近底层,执行效率高。- 单线程模型:避免了多线程的锁竞争与上下文切换开销。
- 高效的数据结构与
I/O多路复用 :底层使用epoll等机制,单线程也能处理大量连接。 - 协议简单 :基于简单的
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