环境准备在 Ubuntu 安装 redis
① 使用 apt 安装
apt install redis -y
② 检查 redis 是否正常运行
netstat -anp | grep redis

③ 远程连接配置
vim /etc/redis/redis.conf 打开 Redis 配置文件
修改 bind127.0.0.1 监听地址,允许所有IP访问 bind 0.0.0.0
关闭保护模式,允许无密码远程访问) protected-mode no

保存并退出 : esc 并输入 :wq
④ 重启 Resid 服务 , 让配置生效
systemctl restart redis-server
⑤ 验证是否生效
netstat-anp|grep redis

一. 通用命令(部分) :
1. 键(Key)管理命令
KEYS pattern:(返回 key 值)
查找所有匹配给定模式的键(例如 KEYS * 会列出当前库所有键)。注意:该命令时间复杂度较高,在生产环境中应谨慎使用,以免阻塞服务器。
返回值: 匹配的 key ;
例如 :
127.0.0.1:6379> set key1 1
OK
127.0.0.1:6379> keys key1
key1
EXISTS key [key ...]:(key 值存在个数)
判断一个或多个键是否存在。
返回值 : key 值匹配的个数 ; (如果传入相同的 key 会, 也会统计个数)
例如 :
127.0.0.1:6379> set key1 name
OK
127.0.0.1:6379> exists key1
1
DEL key [key ...]:(删除 key)
删除一个或多个指定的键。
返回值 : 删除 key 的个数, (如果一次删除多个值时, 有些 key 不存在, 强行删除并不会报错)
例如 :
127.0.0.1:6379> set key1 name
OK
127.0.0.1:6379> del key1
1
TYPE key:(key 类型)
查看指定键存储的数据类型(如 string、hash、list 等)。
返回值 : key 对应的数据类型
示例 :
127.0.0.1:6379> set key1 demo1
OK
127.0.0.1:6379> type key1
string
2. 过期时间管理
EXPIRE key seconds:(设置过期时间)
为键设置过期时间(单位:秒)。
返回值 : 1 表示成功, 0 表示失败
示例 :
127.0.0.1:6379> set key1 name
OK
127.0.0.1:6379> expire key1 10
1
PEXPIRE key milliseconds:
为键设置过期时间(单位:毫秒)。
大体同上
TTL key:
查看键的剩余存活时间(单位:秒)
返回值 : 剩余过期时间 ; (-1 表示没有关联过期时间, -2 表示 key 不存在)
示例 :
127.0.0.1:6379> set key1 name
OK
127.0.0.1:6379> expire key1 10
1
127.0.0.1:6379> ttl key1
6 # 倒计时
127.0.0.1:6379> ttl key1
5
127.0.0.1:6379> ttl key1
-2 # key已经不存在
127.0.0.1:6379>
PTTL key:
查看键的剩余存活时间(单位:毫秒)
大体同上
PERSIST key:
移除键的过期时间,使其变为永久有效。
返回值 : 1 表示成功移除, 0 表示 key 不存在或没有过期时间
示例 :
127.0.0.1:6379> set key1 name
OK
127.0.0.1:6379> expire key1 100
1
127.0.0.1:6379> ttl key1
97 # 剩余过期时间
127.0.0.1:6379> persist key1
1 # 移除过期时间成功
127.0.0.1:6379> ttl key1
-1 # 不存在过期时间
二. 5种常用数据类型
Redis 提供了 5 种基础数据类型,每种类型在对外提供统一操作命令的同时,在底层会根据数据量的大小和类型,动态选择不同的内部编码(Encoding),以实现内存空间和查询性能的极致平衡。


1. String(字符串)
String 是 Redis 最基础的数据类型,可存储字符串、整数、浮点数甚至序列化的对象,单个 Value 最大容量为 512MB
常用命令:
- SET key value:设置键值对(支持
EX秒级过期、NX仅当不存在时设置等选项) - GET key:获取指定键的值。
- INCR key**/** DECR key:将键中的数值原子性地递增 1 或递减 1
- APPEND key value:向字符串末尾追加内容,返回追加后的总长度
- GETRANGE key start end:获取指定区间的子串(区间为左闭右闭,支持负数下标)
底层数据结构与编码:
String 的底层基于**简单动态字符串(SDS)**实现,包含 len(已用长度)、alloc(分配长度)和 buf(字节数组),具备 O(1) 获取长度、二进制安全等特性。根据存储内容的不同,分为 3 种内部编码:
- int****编码:当存储的字符串可以表示为 64 位有符号整数时,直接将整数值存入指针位,无需额外分配内存
- embstr****编码:当存储短字符串(长度 ≤ 44 字节)时,RedisObject 和 SDS 结构被分配在一块连续的内存中,减少了内存碎片和分配次数
- raw****编码:当存储长字符串(长度 > 44 字节)时,RedisObject 和 SDS 分开分配内存,支持字符串的修改操作
2. Hash(哈希)
Hash 是 field-value 的映射表,非常适合用于存储对象(如用户信息),支持单独读写某个字段
常用命令:
- HSET key field value:设置哈希表中指定字段的值
- HGET key field:获取指定字段的值
- HMGET key field1 field2:批量获取多个字段的值
- HGETALL key:获取哈希表中所有的字段和值(大 Key 慎用,易阻塞)
- HINCRBY key field increment:为指定字段的整数值加上增量
底层数据结构与编码:
Redis 会根据字段数量和单值大小,在紧凑连续内存和标准哈希表之间自动切换:
- listpack****编码 (Redis 7.0 前为
ziplist):当字段数量 ≤ 512 且所有字段和值的长度均 ≤ 64 字节时触发。它将 field 和 value 交替平铺在一块连续内存中,极其节省内存,但查找需要线性扫描,时间复杂度为 O(N) - hashtable**(dict) 编码** :当数据量超过上述阈值时,Redis 会将其转换为标准的哈希表。采用"数组+链表"解决哈希冲突,增删改查时间复杂度均为 O(1),且支持渐进式 Rehash(将扩容时的数据迁移分散到后续的读写请求中,避免阻塞主线程)
3. List(列表)
List 是有序的字符串列表,允许重复元素,常用于实现消息队列或记录列表
常用命令:
- LPUSH key element**/** RPUSH key element:从左侧(头部)或右侧(尾部)插入元素
- LPOP key**/** RPOP key:从左侧或右侧弹出并返回元素
- LRANGE key start stop:获取指定区间内的元素
- BLPOP key timeout**/** BRPOP key timeout:阻塞式弹出,若列表为空则阻塞等待,是构建消息队列的核心命令
底层数据结构与编码:
从 Redis 3.2 开始,List 统一使用 quicklist**(快速列表)** 编码(早期为 ziplist + linkedlist 混合)
- quicklist****结构 :宏观上是一个双向链表,但每个链表节点内部又是一个
ziplist(Redis 7.0 后为listpack)。这种"分段连续存储"的设计既保留了链表两端插入/弹出 O(1) 的高效性,又大幅降低了指针带来的内存碎片开销,兼顾了空间和时间
4. Set(集合)
Set 是无序的字符串集合,元素不允许重复,常用于去重、交集/并集运算及随机抽奖
常用命令:
- SADD key member:向集合中添加一个或多个元素
- SMEMBERS key:返回集合中的所有元素
- SINTER key1 key2:返回多个集合的交集
- SRANDMEMBER key count:随机返回集合中的指定数量元素
底层数据结构与编码:
Set 的底层编码根据元素类型和数量动态切换:
- intset**(整数集合)**:当集合中所有元素都是整数,且数量 ≤ 512 个时使用。它将元素按从小到大排序存储在连续内存中,极度紧凑
- listpack****编码(Redis 7.2 新增):当元素为短字符串,且数量 ≤ 128、单值 ≤ 64 字节时使用,用于替代部分 hashtable 场景以降低内存开销
- hashtable**(dict) 编码**:当不满足上述紧凑存储条件时自动转换。此时 Set 复用了 Hash 类型的字典结构,只是 value 指针固定为 NULL,查找时间复杂度为 O(1)
5. ZSet / Sorted Set(有序集合)
ZSet 在 Set 的基础上为每个元素增加了一个 score(分数),元素按 score 从小到大排序,常用于排行榜、延迟队列等场景
常用命令:
- ZADD key score member:添加元素及其分数
- ZRANGE key start stop:按排名(从小到大)返回区间内的元素
- ZRANGEBYSCORE key min max:按分数区间返回元素
- ZRANK key member:返回指定元素的排名(从 0 开始)
底层数据结构与编码:
ZSet 的底层同样根据数据规模动态切换:
- listpack****编码 (Redis 7.0 前为
ziplist):当元素数量 ≤ 128 且单个元素长度 ≤ 64 字节时使用。将 member 和 score 成对紧凑存储在连续内存中,节省空间但操作需遍历 - skiplist**(跳表) +** hashtable**(字典) 组合**:当数据量超过阈值时触发
-
- 跳表(SkipList):通过多层索引链表实现 O(logN) 的按分数范围查询和排序
- 字典(Dict) :以 member 为 key、score 为 value,实现 O(1) 的按成员查分数(如
ZSCORE命令)。 - 这两个结构共享同一份数据节点(通过指针引用),避免了内存的重复浪费
三. 浅谈redis 性能为什么快
Redis 之所以能够保持极高的性能,核心在于其架构设计巧妙地避开了传统数据库的瓶颈。结合其"单线程"架构,Redis 的高性能主要由以下四大核心设计支撑:
1. 纯内存操作,消除 I/O 瓶颈
这是 Redis 快的最根本原因。与将数据存储在磁盘上的传统数据库不同,Redis 将所有数据存储在内存(RAM)中
2. 核心命令单线程,极致降低系统开销
虽然 Redis 并非绝对意义上的单线程进程(例如后台持久化、异步删除等由子线程/进程处理),但其核心网络 I/O 和命令执行逻辑是由单个主线程串行处理的。这种设计带来了巨大的性能优势:
- 避免锁竞争:单线程天然保证了命令执行的原子性,无需引入复杂的互斥锁、读写锁等机制,避免了多线程环境下的锁竞争和死锁风险
- 消除上下文切换:避免了多线程调度时频繁的上下文切换(每次切换约微秒级)带来的 CPU 性能损耗,CPU 可以纯粹且连续地专注于业务逻辑
- 利用 CPU 缓存:单线程频繁访问内存中的数据,数据通常能保留在 CPU 缓存中,大幅降低了内存访问延迟
3. IO 多路复用,单线程支撑高并发
既然核心是单线程,Redis 是同时处理成千上万个客户端连接在于 IO 多路复用(如 epoll/kqueue)+ 事件驱动模型。
- 非阻塞 I/O:主线程运行一个事件循环,将所有的客户端套接字(Socket)注册到多路复用 API 中
- 按需处理:该机制只返回活跃的、有读写事件发生的连接,无需遍历所有连接。这使得单个线程就能高效地监听和处理数万个并发连接,完美解决了 C10K 问题
4. 高效的数据结构与简单的协议
- 定制化数据结构:Redis 底层采用了大量高度优化的 C 语言数据结构(如 SDS、跳表、压缩列表、哈希表等),并针对不同数据规模动态选择最优编码,使得绝大多数操作的时间复杂度维持在 O(1) 或 O(logN)
- 协议简单:Redis 采用 RESP(Redis Serialization Protocol)文本协议,解析开销极低
延伸:Redis 6.0 之后的多线程
值得注意的是,从 Redis 6.0 开始,官方引入了多线程机制。但这仅仅是将网络 I/O 的读写(接收请求、解析协议、响应结果)交由多线程并行处理 。其核心的命令执行阶段,依然严格保持单线程串行执行。这是因为 Redis 的性能瓶颈通常在于网络 I/O 而非 CPU 计算,引入多线程 I/O 旨在进一步提升网络吞吐,同时完美保留了单线程模型无锁、无上下文切换的核心优势