Redis 完全指南:从入门到集群 — 数据类型 + 持久化 + 主从复制 + 哨兵 + 集群实战手册
📖 阅读时间:约 40 ~ 60 分钟 🎯 适合人群:后端开发工程师、运维工程师、缓存架构师、NoSQL 数据库学习者 📝 本文字数:约 4 万字 | 含 200+ 实战命令与配置 🏷️ 推荐标签:Redis 缓存 NoSQL key-value 主从复制 哨兵 Redis集群 持久化 消息队列 运维
📋 文章摘要
Redis 是目前最流行的开源内存数据库,以高性能、丰富的数据类型和灵活的应用场景著称,广泛应用于缓存、分布式锁、计数器、排行榜、消息队列等领域。本文系统讲解 Redis 从入门到生产级集群的完整知识体系:
- 基础篇:Redis 简介、核心优势、与其他 key-value 存储对比、数据类型概览、哈希槽概念、八大应用场景、核心特性(单线程/epoll/缓存机制)、Pipeline 流水线
- 安装配置篇:Redis 安装部署、配置文件详解、config 命令动态修改配置、慢查询、最大内存配置
- 持久化篇:RDB 快照持久化、AOF 追加持久化、混合持久化、持久化策略选择
- 客户端篇:redis-cli 客户端使用、常用命令大全、shell 脚本访问、图形工具连接、Python 程序连接
- 数据类型篇:String、List、Set、Sorted Set(ZSET)、Hash 五大基本数据类型详解与实战
- 消息队列篇:生产者/消费者模式、发布者/订阅者模式
- 高可用篇:主从复制配置、哨兵(Sentinel)自动故障转移、3 主 3 从集群部署
- 容器篇:Docker 容器化部署 Redis
全文包含完整的命令、配置文件、实验步骤和验证方法,所有命令均可直接复制执行。
📌 本文核心收获:读完本文,你将掌握 Redis 从基础操作到生产级高可用集群的完整能力,能够独立完成 Redis 部署、缓存设计、持久化配置和高可用架构搭建。
📑 目录
- 一、Redis 基础与核心特性
- 1.1 Redis 简介与优势
- 1.2 数据类型与哈希槽
- 1.3 常见应用场景
- 1.4 核心特性详解
- 1.5 Pipeline 流水线
- 二、Redis 安装与配置管理
- 2.1 Redis 安装
- 2.2 配置文件详解
- 2.3 config 命令动态修改配置
- 三、Redis 持久化机制
- 3.1 RDB 快照持久化
- 3.2 AOF 追加持久化
- 四、客户端连接与常用命令
- 4.1 客户端连接 Redis
- 4.2 Redis 常用命令
- 五、Redis 数据类型详解
- 5.1 字符串 String
- 5.2 列表 List
- 5.3 集合 Set
- 5.4 有序集合 Sorted Set
- 5.5 哈希 Hash
- 六、消息队列与多语言连接
- 6.1 消息队列
- 6.2 Shell 脚本访问 Redis
- 6.3 图形工具连接 Redis
- 6.4 Python 程序连接 Redis
- 七、主从复制与哨兵
- 7.1 主从复制
- 7.2 哨兵服务(Sentinel)
- 八、Redis 集群与容器部署
- 8.1 Redis 集群(3 主 3 从)
- 8.2 容器部署 Redis
- 九、总结与要点回顾
前言:为什么 Redis 如此重要?
在当今的互联网架构中,缓存是提升系统性能的关键组件。而 Redis 凭借其极致的性能、丰富的数据类型和灵活的应用场景,成为了缓存领域的事实标准。
Redis 的核心价值在于:
- 极致性能:基于内存操作,单线程模型避免锁竞争,QPS 可达 10 万+
- 丰富数据类型:不仅支持简单的 key-value,还支持 String、List、Set、ZSet、Hash、Bitmap、HyperLogLog、Geo 等多种数据结构
- 持久化支持:RDB + AOF 两种持久化机制,保证数据不丢失
- 高可用架构:主从复制、哨兵自动故障转移、Cluster 分布式集群
- 广泛应用:缓存、分布式锁、计数器、排行榜、消息队列、限流等场景全覆盖
📌 学习路径建议:本文按照「基础 → 安装配置 → 持久化 → 客户端 → 数据类型 → 消息队列 → 高可用 → 集群」的顺序编排,建议初学者按顺序学习,有经验的工程师可直接跳转到对应章节。
1.redis简介

1.1简介
Redis 是完全开源的,遵守 BSD 协议,是一个高性能的 key-value 数据库。
Redis 与其他 key-value 缓存产品有以下三个特点:
1:Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
2:Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
3:Redis支持数据的备份,即master-slave模式的数据备份。
1.2Redis 优势
redis性能极高 – (10万~100万+ QPS),微秒级响应,mysql较慢(通常 几千~几万 QPS),毫秒级响应(依赖索引和硬件)
丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。
1.3Redis与其他key-value存储有什么不同?
Redis有着更为复杂的数据结构并且提供对他们的原子性操作,这是一个不同于其他数据库的进化路径。Redis的数据类型都是基于基本数据结构的同时对程序员透明,无需进行额外的抽象。
Redis运行在内存中但是可以持久化到磁盘,所以在对不同数据集进行高速读写时需要权衡内存,因为数据量不能大于硬件内存。 在内存数据库方面的另一个优点是,相比在磁盘上相同的复杂的数据结构,在内存中操作起来非常简单,这样Redis可以做很多内部复杂性很强的事情。 同时,在磁盘格式方面他们是紧凑的以追加的方式产生的,因为他们并不需要进行随机访问。
1.4Redis 数据类型
Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。
1.5哈希槽的基本概念
槽的数量 Redis集群将整个键空间划分为16384个哈希槽(0-16383),这是经过权衡后确定的固定数量
数据映射规则 通过HASH_SLT = CRC16(key) mod 16384公式计算键对应的槽号,确保相同key会始终映射到同一槽。
数据分布与节点管理 节点分配机制 每个集群节点负责部分槽的集合(如节点A负责0-5000,节点B负责5001-10000)
动态迁移与扩容 槽的分配支持在线调整,但相比传统哈希取余算法,迁移数据量更可控
1.6 Redis常见应用场景
Redis 凭借其高性能、丰富的数据结构和灵活的部署方式,已成为现代应用架构中的关键组件。除了大家熟知的缓存功能,Redis 在众多业务场景中都能发挥重要作用。本文将系统梳理 Redis 的常见使用场景,深入分析每种场景的技术实现和最佳实践,帮助开发者充分发挥 Redis 的价值。
一、热点数据缓存
这是 Redis 最经典的应用场景。在电商、社交等高频访问场景中,将热点数据(如商品详情、用户信息)缓存到 Redis,可大幅降低数据库压力,提升系统响应速度。
实现要点:
- 缓存策略:采用 Cache-Aside 模式(先查缓存,未命中再查数据库并更新缓存)
- 过期控制:设置合理的 TTL(生存时间),避免缓存数据长期不更新
- 缓存键设计:使用业务前缀 + 唯一标识,如product:info:1001
- 空值处理:对查询不到的数据缓存空值(短 TTL),防止缓存穿透
二、分布式锁
在分布式系统中,多个服务实例需要协同操作共享资源时,分布式锁是保证数据一致性的关键机制。Redis 的SET NX命令天然适合实现分布式锁。
实现要点:
- 加锁:使用SET key value NX PX 30000(不存在则设置,过期时间 30 秒)
- 解锁:通过 Lua 脚本原子性判断并删除锁,避免误删
- 续期:对长时间运行的任务,需定时延长锁的有效期
- 重试机制:获取锁失败时,实现合理的重试策略
三、计数器与限流
Redis 的原子递增命令INCR非常适合实现计数器功能,结合过期时间可实现接口限流。
典型应用:
- 文章阅读量、视频播放次数统计
- 接口访问频率限制(如每分钟最多 100 次请求)
- 秒杀活动中的库存计数
四、排行榜系统
Redis 的 Sorted Set(有序集合)数据结构完美适配排行榜需求,支持按分数实时排序。
适用场景:
- 商品销量榜、用户积分榜
- 游戏排行榜(如分数、通关时间)
- 实时投票结果展示
五、分布式会话
在分布式系统中,使用 Redis 存储用户会话信息,可实现多服务实例间的会话共享。
实现优势:
- 会话信息集中存储,服务水平扩展无压力
- 支持设置过期时间,自动清理无效会话
- 可实现会话共享,提升用户体验
六、消息队列
利用 Redis 的 List 数据结构可实现简单的消息队列,适合对消息可靠性要求不高的场景。
实现方式:
- 生产者:使用LPUSH将消息推入队列
- 消费者:使用BRPOP阻塞式获取消息(避免轮询)
- 支持多个消费者协同工作,实现负载均衡
七、位图应用
Redis 的 Bitmap(位图)是一种节省空间的二进制数据结构,适合存储大量布尔值信息。
典型场景:
- 用户签到记录(如一年内的签到情况)
- 活跃用户统计(如最近 7 天活跃用户)
- 权限标记(如用户拥有的权限集合)
八、地理信息存储
Redis 的 GEO 数据结构专门用于存储和操作地理坐标信息,适合 LBS(基于位置的服务)场景。
应用场景:
- 附近的人 / 店铺查询
- 地理位置标记与距离计算
- 区域内的实体搜索
1.6 Redis特性
- 速度快:10WQPS,基于内存,C语言实现
- 单线程
- 持久化
- 支持多种数据结构
- 支持多种编程语言
- 功能丰富:支持Lua脚本,发布订阅,事务,pipeline等功能
- 简单:代码短小精悍(单机核心代码只有23000行左右),单线程开发容易,不依赖外部库,使用简单
- 主从复制
- 支持高可用和分布式
1.6.1 单线程



Redis6.0版本前一直是单线程方式处理用户的请求
- 数据库对于数据的插入删除查询… 都有更复杂的功能支持,这样的功能势必要花费更多的开销。
- 比如,针对插入删除,数据库中的各种约束,都会使数据库做额外的工作(分组,联合查询…)
- Redis 每个基本操作都是短平快,就是简单操作一下内存数据,不是什么特别消耗 CPU 的操作,就算搞多个线程,提升也不大
IO多路复用
本质上是让我们一个线程,管理多个 socket
针对 TCP 来说,服务器这边每次要服务一个客户端,都需要给这个客户端安排一个 socket,通过这个 socket 来和客户端进行通信
一个服务器要服务多个客户端,同时就有很多个 socket。这些 socket 上都是无时无刻都在传输数据吗?还是大部分时间都是闲置的?
- 很多情况下,每个客户端和服务器之间的通信也没那么频繁。此时这么多 socket 大部分时间都是静默的,上面是没有数据需要传输的。
- 同一时刻,只有少数 socket 是活跃的,这就是我们使用 IO复用的重大前提
最开始介绍 TCP 服务器的时候,有一个版本就是每个客户端都分配一个线程==>客户端多了,线程就多了,系统开销就大了
基于上述的这些原因,就搞了“IO多路复用”,一个线程来处理多个 socket
- 操作系统,给程序员提供的机制,提供了一套 API,内部的功能都是操作系统内核实现的
- Linux 上提供的 IO多路复用,主要是三套 API:select、poll、epoll
1.6.2 epoll
比如你今晚,你想吃烧烤,你妈想吃饺子,你爸想吃炒菜
- 最关键的就是老爸能够喊我,哪个客户端来数据了,操作系统就能通知到应用程序
- 服务器开发中最主流的方案,尤其是 IO多路复用中的 epoll
epoll 相当于时间通知/回调机制 select 是你自己再几个摊位之间不停地问,没人提醒你
如果这三件事都是交互特别频繁的,还是老老实实多搞几个线程靠谱,一个线程容易忙不过来
- C++ 可以直接使用 Linux 原生的 epoll API
- Java 可以使用 NIO(标准库提供的一组类,底层就是封装了 epoll)
注意事项: 一次只运行一条命令 避免执行长(慢)命令:keys *,flushall, flushdb, slow lua script, mutil/exec,operate bigvalue(collection) 其实不是单线程:早期版本是单进程单线程,3.0版本后实际还有其它的线程,实现特定功能,如:fysnc file descriptor,close file descriptor

1.6.3 缓存实现过程
计算机系统中的三层存储结构

计算机系统中,默认有两种缓存

缓存的第一个特征:在一个层次化的系统中,缓存一定是一个快速子系统,数据存在缓存中时,能避免每次从慢速子系统中存取数据。
缓存的第二个特征:缓存系统的容量大小总是小于后端慢速系统的,我们不可能把所有数据都放在缓存系统中。
1.6.4 Redis 缓存处理请求的两种情况
把 Redis 用作缓存时,我们会把 Redis 部署在数据库的前端,业务应用在访问数据时,会先查询 Redis 中是否保存了相应的数据。此时,根据数据是否存在缓存中,会有两种情况。
- 缓存命中:Redis 中有相应数据,就直接读取 Redis,性能非常快。
- 缓存缺失:Redis 中没有保存相应数据,就从后端数据库中读取数据,性能就会变慢。而且,一旦发生缓存缺失,为了让后续请求能从缓存中读取到数据,我们需要把缺失的数据写入 Redis,这个过程叫作缓存更新。缓存更新操作会涉及到保证缓存和数据库之间的数据一致性问题。
发生缓存命中或缺失时,应用读取数据的情况

使用 Redis 缓存时,我们基本有三个操作:
- 应用读取数据时,需要先读取 Redis;
- 发生缓存缺失时,需要从数据库读取数据;
- 发生缓存缺失时,还需要更新缓存。
那么,这些操作具体是由谁来做的呢?
Redis 作为旁路缓存的使用操作
Redis 是一个独立的系统软件,和业务应用程序是两个软件,当我们部署了 Redis 实例后,它只会被动地等待客户端发送请求,然后再进行处理。所以,如果应用程序想要使用 Redis 缓存,我们就要在程序中增加相应的缓存操作代码。所以,我们也把 Redis 称为旁路缓存,也就是说,读取缓存、读取数据库和更新缓存的操作都需要在应用程序中来完成。
那么,使用 Redis 缓存时,具体来说,我们需要在应用程序中增加三方面的代码:
- 当应用程序需要读取数据时,我们需要在代码中显式调用 Redis 的 GET 操作接口,进行查询;
- 如果缓存缺失了,应用程序需要再和数据库连接,从数据库中读取数据;
- 当缓存中的数据需要更新时,我们也需要在应用程序中显式地调用 SET 操作接口,把更新的数据写入缓存。
为了使用缓存,Web 应用程序需要有一个表示缓存系统的实例对象 redisCache,还需要主动调用 Redis 的 GET 接口,并且要处理缓存命中和缓存缺失时的逻辑,例如在缓存缺失时,需要更新缓存。
1.6.5 缓存的类型
按照 Redis 缓存是否接受写请求,我们可以把它分成只读缓存和读写缓存。
1.6.5.1 只读缓存
当 Redis 用作只读缓存时,应用要读取数据的话,会先调用 Redis GET 接口,查询数据是否存在。而所有的数据写请求,会直接发往后端的数据库,在数据库中增删改。对于删改的数据来说,如果 Redis 已经缓存了相应的数据,应用需要把这些缓存的数据删除,Redis 中就没有这些数据了。
当应用再次读取这些数据时,会发生缓存缺失,应用会把这些数据从数据库中读出来,并写到缓存中。这样一来,这些数据后续再被读取时,就可以直接从缓存中获取了,能起到加速访问的效果。

只读缓存直接在数据库中更新数据的好处是,所有最新的数据都在数据库中,而数据库是提供数据可靠性保障的,这些数据不会有丢失的风险。
1.6.5.2 读写缓存
对于读写缓存来说,除了读请求会发送到缓存进行处理(直接在缓存中查询数据是否存在),所有的写请求也会发送到缓存,在缓存中直接对数据进行增删改操作。
但是,和只读缓存不一样的是,在使用读写缓存时,最新的数据是在 Redis 中,而 Redis 是内存数据库,一旦出现掉电或宕机,内存中的数据就会丢失。
根据业务应用对数据可靠性和缓存性能的不同要求,我们会有同步直写和异步写回两种策略。其中,同步直写策略优先保证数据可靠性,而异步写回策略优先提供快速响应。
同步直写是指,写请求发给缓存的同时,也会发给后端数据库进行处理,等到缓存和数据库都写完数据,才给客户端返回。
而异步写回策略,则是优先考虑了响应延迟。此时,所有写请求都先在缓存中处理。等到这些增改的数据要被从缓存中淘汰出来时,缓存将它们写回后端数据库。
同步直写和异步写回

小结
缓存的两个特征,分别是在分层系统中,数据暂存在快速子系统中有助于加速访问;缓存容量有限,缓存写满时,数据需要被淘汰。而 Redis 天然就具有高性能访问和数据淘汰机制,正好符合缓存的这两个特征的要求,所以非常适合用作缓存。
Redis 作为旁路缓存的特性,旁路缓存就意味着需要在应用程序中新增缓存逻辑处理的代码。
1.6.6 缓存穿透,缓存击穿和缓存雪崩
在程序中,没有缓存的时候,调用流程是这样的:

当我们加入缓存以后,程序就会变成以下样子:

加入缓存之后,程序不直接调用数据库,而是先调用缓存,当缓存中存在数据就直接返回,当缓存中没有数据才去查询数据库大大降低了数据库的压力,加快了程序响应的速度。
缓存的优点 相对于数据库而言,缓存的操作性更高效,原因有以下几点:
- 缓存一般使用 key-value 查询数据的,不需要想数据库那样还有查询条件。
- 缓存的数据存储在内存中,而数据库的数据是存储在磁盘中的,内存的操作性能远远大于磁盘。
- 缓存更容易实现分布式部署(一台服务器变成多台相连的服务器集群),而数据库比较难实现分布式部署,缓存的性能更容易平行扩展。
缓存的分类 缓存可以分为以下两类:
- **本地缓存:**也叫单机缓存,也就是说在一台服务器上缓存,因此只适用于当前系统。
- **分布式缓存:**用来应用在分布式系统中的缓存。分布式系统就是将一套服务器部署到多台服务器,通过负载(通常是 Nginx)分发将用户的请求按照一定的规则分发到不同服务器上,如下图:

在上图加入分布式缓存后变成下图:

雪崩问题
1.定义
指缓存层由于大面积失效(如同一时间批量过期)或缓存服务宕机,导致所有请求瞬间涌入数据库,导致数据库崩溃的现象。
2. 通俗理解
想象一个游乐场,所有出口的闸机(Redis)同时坏了或者同时过期了,几千人一下子全部冲向检票口,大门(数据库)瞬间被挤爆。
3. 发生场景
- 批量 Key 同时过期:例如电商首页促销,1000 个商品商品的缓存都设置了 10 分钟 过期,时间一到,1000 个请求同时打库。
- Redis 集群宕机:Redis 挂了,请求无路可走,直接穿透到 DB。
4. 后果
系统级故障,服务不可用,数据库压力达到峰值甚至挂掉。
我们来对比一下正常情况与缓存雪崩情况:
正常情况:

雪崩情况:

如何解决缓存雪崩问题
解决方案
缓存穿透
1. 定义
指查询一个根本不存在的数据(缓存中没有,数据库中也没有)。因为缓存不缓存空值,所以每次请求都会绕过缓存直接去查数据库。
2. 通俗理解
你拿着一张假身份证去游乐场检票,检票员(Redis)看了一眼说没这张票,就让你去后台查(数据库)。后台查了半天也没有,就让你回去了。结果来了一万个拿着假身份证的人,都去后台折腾,后台累坏了。
3. 发生场景
- 攻击者恶意查询 user_id = -1 或不存在的商品 ID。
- 业务代码 Bug 导致反复查无效 Key。
4. 后果
无效请求反复打击数据库,导致 DB 资源浪费,甚至被 DOS 攻击打挂。

红色表示缓存穿透的执行路径,可以看出缓存穿透会给数据库造成巨大的压力。
如何解决缓存穿透问题 有以下几种解决方案:
- 接口层增加校验,如用户鉴权校验,ID做基础校验,id<=0的直接拦截
- 缓存空结果:对查询的空结果也进行缓存,如果是集合,可以缓存一个空的的集合,如果是缓存单个对象,可以字段标识来区分,避免请求穿透到数据库。
- 布隆过滤器处理:将所有可能对应的数据为空的 key 进行统一的存放,并在请求前做拦截,避免请求穿透到数据库(这样的方式实现起来相对麻烦,比较适合命中不高,但是更行不频繁的数据)。
缓存击穿
1. 定义
指热点 Key(访问量极大的 Key)在某个时刻过期,此时恰好有大量并发请求同时打过来。因为缓存中没有,这些请求都会去查询数据库。
2. 通俗理解
游乐场只有一个 VIP 通道(热点 Key),这个通道刚好关闭了(过期了),但此时有一万人正排队等着通过,结果大家一拥而上,把通道旁边的闸机(数据库)挤坏了。
3. 发生场景
- 秒杀活动、热点新闻爆发。
- 某个商品缓存刚好过期,瞬间 10w QPS 涌入。
4. 区别(重点!)
- 雪崩是大面积(很多 Key 一起挂)。
- 击穿是单点(一个热点 Key 挂了,但它很重要)。
5. 后果
针对特定库表的攻击,导致该表压力剧增,响应变慢甚至拖垮。

如何解决缓存击穿问题 有以下几种解决方案:
加锁排队:和处理缓存雪崩的加锁类似,都是在查询数据库的时候加锁排队,缓存操作请求以此来减少服务器的运行压力。 设置永不过时:对于某些经常使用的缓存,我们可以设置为永不过期,这样就能保证缓存的稳定性,但要注意在数据更改后,要及时更新此热点缓存,否则就会造成查询结果误差。
1.7 Pipeline流水线
redis客户端执行一条命令分4个过程:
发送命令-〉命令排队-〉命令执行-〉返回结果
这个过程称为Round trip time(简称RTT, 往返时间),mget mset有效节约了RTT,但大部分命令(如hgetall,并没有mhgetall)不支持批量操作,需要消耗N次RTT ,这个时候需要pipeline来解决这个问题
Redis提供了批量操作命令(例如mget、mset等),有效地节约RTT。但大部分命令是不支持批量操作的,例如要执行n次hgetall命令,并没有mhgetall命令存在,需要消耗n次RTT。Redis的客户端和服务端可能部署在不同的机器上。例如客户端在北京,Redis服务端在上海,两地直线距离约为1300公里,那么1次RTT时间=1300×2/(300000×2/3)=13毫秒(光在真空中传输速度为每秒30万公里,这里假设光纤为光速的2/3),那么客户端在1秒内大约只能执行80次左右的命令,这个和Redis的高并发高吞吐特性背道而驰。
Pipeline(流水线)机制能改善上面这类问题,它能将一组Redis命令进行组装,通过一次RTT传输给Redis,再将这组Redis命令的执行结果按顺序
未使用pipeline执行N条命令
没有使用Pipeline执行了n条命令,整个过程需要n次RTT

使用pipeline执行N条命令
使用Pipeline执行了n次命令,整个过程需要1次RTT。Pipeline并不是什么新的技术或机制,很多技术上都使用过。而且RTT在不同网络环境下会有不同,例如同机房和同机器会比较快,跨机房跨地区会比较慢。Redis命令真正执行的时间通常在微秒级别,所以才会有Redis性能瓶颈是网络这样的说法。

两者性能对比

小结:这是一组统计数据出来的数据,使用Pipeline执行速度比逐条执行要快,特别是客户端与服务端的网络延迟越大,性能体能越明显。
1.8 redis的安装
centos7 redis源码安装
#安装依赖包
[root@localhost ~]# yum install tcl gcc gcc-c++ -y
#解压redis安装包
[root@localhost ~]# tar zxvf redis-6.2.14.tar.gz -C /usr/local/
#编译安装
[root@localhost ~]# cd /usr/local/redis-6.2.14/
[root@localhost redis-6.2.14]# make && make install
#启动并放在后台运行
[root@localhost redis-6.2.14]# redis-server redis.conf &
#查看启动端口
[root@localhost redis-6.2.14]# netstat -antp | grep 6379
tcp 0 0 127.0.0.1:6379 0.0.0.0:* LISTEN 7751/redis-server 1
tcp6 0 0 ::1:6379 :::* LISTEN 7751/redis-server 1
#客户端进入
[root@localhost redis-6.2.14]# redis-cli
127.0.0.1:6379>
centos 8 yum安装
[root@localhost ~]# dnf info redis
Last metadata expiration check: 0:00:18 ago on Thu 12 Feb 2026 03:04:51 PM CST.
Available Packages
Name : redis
Version : 5.0.3
Release : 5.module_el8.4.0+955+7126e393
Architecture : x86_64
Size : 927 k
Source : redis-5.0.3-5.module_el8.4.0+955+7126e393.src.rpm
Repository : appstream
Summary : A persistent key-value database
URL : http://redis.io
License : BSD and MIT
Description : Redis is an advanced key-value store. It is often referred to as a data
: structure server since keys can contain strings, hashes, lists, sets and
: sorted sets.
:
: You can run atomic operations on these types, like appending to a string;
: incrementing the value in a hash; pushing to a list; computing set
: intersection, union and difference; or getting the member with highest
: ranking in a sorted set.
:
: In order to achieve its outstanding performance, Redis works with an
: in-memory dataset. Depending on your use case, you can persist it either
: by dumping the dataset to disk every once in a while, or by appending
: each command to a log.
:
: Redis also supports trivial-to-setup master-slave replication, with very
: fast non-blocking first synchronization, auto-reconnection on net split
: and so forth.
:
: Other features include Transactions, Pub/Sub, Lua scripting, Keys with a
: limited time-to-live, and configuration settings to make Redis behave like
: a cache.
:
: You can use Redis from most programming languages also.
[root@localhost ~]# dnf install -y redis
[root@localhost ~]# systemctl enable redis –now
#查看redis-server监听6379端口
[root@localhost ~]# ss -tunlp
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
tcp LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=954,fd=3))
tcp LISTEN 0 511 127.0.0.1:6379 0.0.0.0:* users:(("redis–server",pid=10350,fd=6))
tcp LISTEN 0 128 [::]:22 [::]:* users:(("sshd",pid=954,fd=4))
[root@localhost ~]# pstree -p | grep redis
|-redis-server(10350)-+-{redis-server}(10351)
| |–{redis-server}(10352)
| `-{redis-server}(10353)
[root@localhost ~]# redis-cli
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> info
redis_version:5.0.3
redis_git_sha1:00000000
......
开启Redis多实例
[root@localhost ~]# redis-server –port 6380
[root@localhost ~]# ss -ntl
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
LISTEN 0 511 127.0.0.1:6379 0.0.0.0:*
LISTEN 0 511 0.0.0.0:6380 0.0.0.0:*
[root@localhost ~]# redis-cli -p 6380
127.0.0.1:6380> ping
PONG
Redis配置文件说明
why 6379
Redis 默认使用端口号 6379,这一选择主要源于其作者 Salvatore Sanfilippo(昵称 antirez)的一个个人趣闻,而非技术上的特殊含义。
根据作者在个人博客和社交媒体上的说明,6379 这个数字来源于手机键盘上字母 “MERZ” 对应的按键。6379 “MERZ” 是作者及其朋友用来形容“愚蠢”的俚语,源自意大利女演员 Alessia Merz 在电视节目中的一些表现,作者觉得她的话很可笑,于是创造了这个词。6379当为 Redis 选择默认端口时,antirez 没有过多思考,直接将 “MERZ” 在手机键盘上对应的数字 6379 用作了 Redis 的默认端口。
意大利女演员 Alessia Merz

[root@localhost ~]# vim /etc/redis.conf
69 bind 127.0.0.1 #Redis 仅监听本地回环地址 127.0.0.1,这意味着只有本机可以连接 Redis。若需远程访问,应改为 bind 0.0.0.0(但务必配合密码使用)。
88 protected-mode yes #启用保护模式。当 Redis 未设置密码且未明确绑定到特定 IP(或绑定了外网 IP)时,只允许来自 127.0.0.1 和 ::1 的连接。这是防止未授权访问的安全机制。
92 port 6379 #Redis 监听 TCP 端口 6379(默认端口)。
101 tcp-backlog 511 #设置 TCP 连接全队列(completed connection queue)的最大长度为 511。高并发场景下可能需要调大,并同步调整系统参数 /proc/sys/net/core/somaxconn
113 timeout 0 #客户端空闲连接超时时间为 0,表示永不因空闲而断开连接。
130 tcp-keepalive 300 #每 300 秒(5 分钟)向客户端发送一次 TCP keepalive 探测包,用于检测死连接。
136 daemonize no #Redis 不作为守护进程运行,而是以前台方式启动(适合容器化部署或手动调试)。
147 supervised no #不与操作系统进程管理器(如 systemd 或 upstart)集成。
158 pidfile /var/run/redis_6379.pid #指定 Redis 主进程 PID 写入的文件路径。注意:该目录需存在且 Redis 进程有写权限。
166 loglevel notice #日志级别设为 notice,记录重要信息(如启动、关闭、持久化事件),适合生产环境。
171 logfile /var/log/redis/redis.log #日志输出到指定文件 /var/log/redis/redis.log。确保该路径可写。
186 databases 16 #Redis 启动时创建 16 个逻辑数据库(编号 0 到 15),可通过 SELECT <dbid> 切换。
194 always-show-logo yes #在启动redis时是否显示或在日志中记录记录redis的logo
218 save 900 1 #900 秒内至少有 1 个 key 变更 → 触发 bgsave
219 save 300 10 #300 秒内至少有 10 个 key 变更 → 触发 bgsave
220 save 60 10000 #60 秒内至少有 10000 个 key 变更 → 触发 bgsave
stop-writes-on-bgsave-error yes #如果后台 RDB 保存失败(如磁盘满),Redis 将拒绝写入操作,防止数据丢失未被察觉。
rdbcompression yes #对 RDB 文件中的字符串值启用 LZF 压缩,节省磁盘空间(推荐开启)。
rdbchecksum yes #在 RDB 文件末尾添加 CRC64 校验和,提高文件损坏检测能力(性能损耗约 10%)。
dir /var/lib/redis #指定 Redis 工作目录。RDB 文件、AOF 文件等都将存储在此目录下。必须是目录路径(不能是文件)。
dbfilename dump.rdb #RDB默认文件名
replica-serve-stale-data yes #当从节点(replica)与主节点失联时,仍可继续响应客户端请求(可能返回过期数据)。若设为 no,则除部分管理命令外均返回错误。
replica-read-only yes #从节点默认为只读模式,禁止客户端在其上执行写命令(防止数据不一致)。
repl-diskless-sync no #主节点在全量同步时,先将 RDB 文件写入磁盘,再发送给从节点(而非直接通过 socket 流式传输)。
repl-diskless-sync-delay 5 #当启用 repl-diskless-sync yes 时,主节点等待 5 秒再开始传输,以便更多从节点能加入批量同步。
replica-priority 100 #从节点优先级(供 Redis Sentinel 选主使用)。数值越小优先级越高;设为 0 表示永不参与选主。
requirepass foobared #设置 Redis 访问密码为 foobared,客户端需先执行 AUTH foobared 才能操作。
maxclients 10000 # 最大客户端连接数
appendonly no # 禁用 AOF 持久化(仅使用 RDB)
appendfilename "appendonly.aof" # AOF 文件名(当前未生效)
appendfsync everysec # 若启用 AOF,每秒 fsync 一次(平衡性能与安全)
no-appendfsync-on-rewrite no # RDB/AOF 重写期间仍执行 appendfsync
auto-aof-rewrite-percentage 100 # AOF 文件比上次重写后增长 100% 时触发重写
auto-aof-rewrite-min-size 64mb # AOF 文件至少 64MB 才触发自动重写
aof-load-truncated yes # 启动时若 AOF 被截断,尝试加载并继续启动
aof-use-rdb-preamble yes # AOF 重写使用 RDB 前导(混合持久化,加快恢复)
lua-time-limit 5000 # Lua 脚本最大执行时间(毫秒)
slowlog-log-slower-than 10000 # 慢查询阈值:10000 微秒(10 毫秒)
slowlog-max-len 128 # 慢查询日志最多保留 128 条
latency-monitor-threshold 0 # 延迟监控阈值(0 = 关闭)
notify-keyspace-events "" # 键空间通知事件(空 = 禁用)
hash-max-ziplist-entries 512 # Hash 使用 ziplist 编码的最大元素数
hash-max-ziplist-value 64 # Hash 使用 ziplist 编码的单个 field/value 最大字节数
list-max-ziplist-size -2 # List 的 quicklist 每个 ziplist 节点最大为 8KB(-2)
list-compress-depth 0 # List 的 quicklist 压缩深度(0 = 不压缩)
set-max-intset-entries 512 # Set 使用 intset 编码的最大整数元素数
zset-max-ziplist-entries 128 # Sorted Set 使用 ziplist 编码的最大元素数
zset-max-ziplist-value 64 # Sorted Set 使用 ziplist 编码的单个 member/score 最大字节数
hll-sparse-max-bytes 3000 # HyperLogLog 稀疏表示最大字节数(超过转稠密)
stream-node-max-bytes 4096 # Stream 单个节点最大字节数
stream-node-max-entries 100 # Stream 单个节点最大消息条数
activerehashing yes # 启用渐进式 rehash,避免卡顿
client-output-buffer-limit normal 0 0 0 # 普通客户端输出缓冲区无限制
client-output-buffer-limit replica 256mb 64mb 60 # 从节点:硬限 256MB,软限 64MB 持续 60 秒则断开
client-output-buffer-limit pubsub 32mb 8mb 60 # Pub/Sub 客户端:硬限 32MB,软限 8MB 持续 60 秒则断开
hz 10 # 后台任务每秒执行 10 次(如过期 key 清理)
dynamic-hz yes # 动态调整 hz(连接多时自动提高)
aof-rewrite-incremental-fsync yes # AOF 重写时增量 fsync(每 32MB 刷一次)
rdb-save-incremental-fsync yes # RDB 保存时增量 fsync(每 32MB 刷一次)
config命令实现动态修改配置
config命令用于查看当前redis配置,以及不重启redis服务实现动态更改redis配置等
[!CAUTION]
不是所有配置都可以动态修改,且此方法无法持久化保存
CONFIG SET parameter value 时间复杂度:O(1) CONFIG SET命令可以动态地调整Redis服务器的配置(configuration)而无须重启。
可以使用它修改配置参数,或者改变Redis的持久化(Persistence)方式。 CONFIG SET可以修改的配置参数可以使用命令CONFIG GET *来列出,所有被CONFIG SET修改的配置参数都会立即生效。
CONFIG GET parameter 时间复杂度:O(N),其中N为命令返回的配置选项数量。 cONFIG GET命令用于取得运行中的Redis服务器的配置参数(configuration parameters),在Redis2.4版本中,有部分参数没有办法用CONFIG GET访问,但是在最新的Redis2.6版本中,所有配置参数都已经可以用CONFIG GET访问了。
CONFIG GET接受单个参数parameter作为搜索关键字,查找所有匹配的配置参数,其中参数和值以“键-值对”(key-value pairs)的方式排列。 比如执行CONFIG GET s*命令,服务器就会返回所有以s开头的配置参数及参数的值:
案例:版本差异
127.0.0.1:6379> config set port 8888
OK
127.0.0.1:6379> config set port 8888
(error) ERR Unsupported CONFIG parameter: port
设置客户端连接密码
127.0.0.1:6379> CONFIG SET requirepass 123456
OK
[root@localhost ~]# redis-cli -a 123456
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:6379> CONFIG GET requirepass
1) "requirepass"
2) "123456"
获取当前配置
127.0.0.1:6379> CONFIG GET *
1) "dbfilename"
2) "dump.rdb"
3) "requirepass"
4) "123456"
......
127.0.0.1:6379> CONFIG GET bind
1) "bind"
2) "127.0.0.1"
127.0.0.1:6379> CONFIG SET BIND 0.0.0.0
(error) ERR Unsupported CONFIG parameter: BIND
127.0.0.1:6379> config set bind 0.0.0.0
OK
设置Redis使用最大内存容量
127.0.0.1:6379> CONFIG GET maxmemory
1) "maxmemory"
2) "0"
127.0.0.1:6379> CONFIG SET maxmemory 10086
OK
127.0.0.1:6379> CONFIG GET maxmemory
1) "maxmemory"
2) "10086"
127.0.0.1:6379> CONFIG SET maxmemory 1G
OK
127.0.0.1:6379> CONFIG GET maxmemory
1) "maxmemory"
2) "1000000000"
慢查询
许多存储系统(例如MySQL)提供慢查询日志帮助开发和运维人员定位系统存在的慢操作。所谓慢查询日志就是系统在命令执行前后计算每条命令的执行时间,当超过预设阀值,就将这条命令的相关信息(例如:发生时间,耗时,命令的详细信息)记录下来,Redis也提供了类似的功能。
一条客户端命令声明周期如下:
1)发送命令 2)命令排队 3)命令执行 4)返回结果
两点说明:

多慢算慢?—》超出阈值就算慢
[root@localhost ~]# vim /etc/redis.conf
995 slowlog-log-slower-than 1 #单位为us,指定超过1us即为慢的指令,默认值为10000us
999 slowlog-max-len 1024 #指定只保存最近的1024条慢记录,默认值为128
[root@localhost ~]# systemctl restart redis
[root@localhost ~]# redis-cli -a 123456
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:6379> SLOWLOG LEN
(integer) 5
127.0.0.1:6379> SLOWLOG GET
1) 1) (integer) 5 #项目唯一ID
2) (integer) 1770892970 #unix时间戳
3) (integer) 3 #执行耗时(微秒)
4) 1) "SLOWLOG" #执行的命令
2) "LEN"
5) "127.0.0.1:56406" #客户端IP和端口
6) "" #客户端名称(未设置)
2) 1) (integer) 4
2) (integer) 1770892963
3) (integer) 379
4) 1) "COMMAND"
5) "127.0.0.1:56406"
6) ""
3) 1) (integer) 3
2) (integer) 1770892963
3) (integer) 1
4) 1) "AUTH"
2) "123456"
5) "127.0.0.1:56406"
6) ""
4) 1) (integer) 2
2) (integer) 1770892863
3) (integer) 18
4) 1) "CONFIG"
2) "GET"
3) "bind"
5) "127.0.0.1:38008"
6) ""
5) 1) (integer) 1
2) (integer) 1770892859
3) (integer) 659
4) 1) "COMMAND"
5) "127.0.0.1:38008"
6) ""
6) 1) (integer) 0
2) (integer) 1770892859
3) (integer) 1
4) 1) "AUTH"
2) "123456"
5) "127.0.0.1:38008"
6) ""
127.0.0.1:6379>
127.0.0.1:6379> SLOWLOG RESET
OK
RAIDS持久化
什么是 Redis 持久化?
Redis 作为一个键值对内存数据库(NoSQL),数据都存储在内存当中,在处理客户端请求时,所有操作都在内存当中进行,如下所示
这样做有什么问题呢?其实,只要稍微有点计算机基础知识的人都知道,存储在内存当中的数据,只要服务器关机(各种原因引起的),内存中的数据就会消失了。
不仅服务器关机会造成数据消失,Redis 服务器守护进程退出,内存中的数据也一样会消失。
对于只把 Redis 当缓存来用的项目来说,数据消失或许问题不大,重新从数据源把数据加载进来就可以了。
但如果直接把用户提交的业务数据存储在 Redis 当中,把 Redis 作为数据库来使用,在其放存储重要业务数据,那么 Redis 的内存数据丢失所造成的影响也许是毁灭性。 为了避免内存中数据丢失,Redis 提供了对持久化的支持,我们可以选择不同的方式将数据从内存中保存到硬盘当中,使数据可以持久化保存。

Redis 提供了 RDB 和 AOF 两种不同的数据持久化方式:
- RDB:Redis DataBase
- AOF:Appen-only File
RDB
RDB 是一种快照存储持久化方式,具体就是将 Redis 某一时刻的内存数据保存到硬盘的文件当中,默认保存的文件名为 dump.rdb,而在 Redis 服务器启动时,会重新加载 dump.rdb 文件的数据到内存当中恢复数据。
开启 RDB 持久化方式
开启 RDB 持久化方式很简单,客户端可以通过向 Redis 服务器发送 save 或 bgsave 命令让服务器生成 RDB 文件,或者通过服务器配置文件指定触发 RDB 条件。
save 命令:是一个同步操作
127.0.0.1:6379> save
OK

当客户端向服务器发送 Save 命令请求进行持久化时,服务器会阻塞 Save 命令之后的其他客户端的请求,直到数据同步完成。
如果数据量太大,同步数据会执行很久,而这期间 Redis 服务器也无法接收其他请求,所以,最好不要在生产环境使用 Save 命令。
范例:save执行过程会使用主进程进行快照
[root@localhost ~]# pstree -p | grep redis-server ; ll -h /var/lib/redis
|-redis-server(2104)-+-{redis-server}(2105)
| |–{redis-server}(2106)
| `–{redis-server}(2107)
total 4.0K
-rw-r–r– 1 redis redis 92 Feb 21 17:58 dump.rdb
[root@localhost ~]# redis-cli
127.0.0.1:6379> debug populate 5000000 #这是 Redis 的一个调试命令(debug command),用于快速生成大量测试数据,这个命令不会触发持久化(AOF / RDB 不会记录它,因为它属于调试命令)。
OK
(5.99s)
127.0.0.1:6379> save
OK
(5.07s)
[root@localhost ~]# pstree -p | grep redis-server ; ll -h /var/lib/redis
|-redis-server(2104)-+-{redis-server}(2105)
| |–{redis-server}(2106)
| `–{redis-server}(2107)
total 64M
-rw-r–r– 1 redis redis 92 Feb 21 17:59 dump.rdb
-rw-r–r– 1 redis redis 51M Feb 21 17:59 temp-2104.rdb #主进程号2104
Bgsave:与 Save 命令不同,Bgsave 命令是一个异步操作。
127.0.0.1:6379> bgsave
Background saving started

当客户端发服务发出 bgsave 命令时,Redis 服务器主进程会 Forks 一个子进程来数据同步问题,在将数据保存到 RDB 文件之后,子进程会退出。
所以,与 save 命令相比,Redis 服务器在处理 Bgsave 采用子线程进行 IO 写入。
而主进程仍然可以接收其他请求,但 Forks 子进程是同步的,所以 Forks 子进程时,一样不能接收其他请求。
这意味着,如果 Forks 一个子进程花费的时间太久(一般是很快的),Bgsave 命令仍然有阻塞其他客户的请求的情况发生。
**服务器配置自动触发:**除了通过客户端发送命令外,还有一种方式,就是在 Redis 配置文件中的 Save 指定到达触发 RDB 持久化的条件,比如【多少秒内至少达到多少写操作】就开启 RDB 数据同步。
例如我们可以在配置文件 redis.conf 指定如下的选项:
# 以下是默认值
[root@localhost ~]# vim /etc/redis.conf
218 save 900 1 # 900秒内修改了1个KEY即触发保存RDB
219 save 300 10 # 300秒内修改了10个KEY即触发保存RDB
220 save 60 10000 # 60秒内修改了10000个KEY即触发保存RDB
这种通过服务器配置文件触发 RDB 的方式,与 Bgsave 命令类似,达到触发条件时,会 Forks 一个子进程进行数据同步。
范例:手动执行备份RDB
[root@localhost ~]# redis-cli
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> debug populate 5000000
OK
(8.99s)
127.0.0.1:6379> get key:0
"value:0"
127.0.0.1:6379> get key:1
"value:1"
127.0.0.1:6379> get key:2
"value:2"
127.0.0.1:6379> get key:499999
"value:499999"
127.0.0.1:6379> get key:5000000
(nil)
127.0.0.1:6379> bgsave
Background saving started
#再开个窗口
[root@localhost ~]# pstree -p | grep redis-server ; ll -h /var/lib/redis
|-redis-server(2104)-+-redis-server(2266)
| |–{redis-server}(2105)
| |–{redis-server}(2106)
| `-{redis-server}(2107)
total 191M
-rw-r–r– 1 redis redis 127M Feb 21 17:54 dump.rdb
-rw-r–r– 1 redis redis 62M Feb 21 17:55 temp-2266.rdb #新开了个子进程2266
RDB 文件
前面介绍了三种让服务器生成 RDB 文件的方式,无论是由主进程生成还是子进程来生成,其过程如下:
- 生成临时 RDB 文件,并写入数据。
- 完成数据写入,用临时文代替代正式 RDB 文件。
- 删除原来的 DB 文件。
RDB 默认生成的文件名为 dump.rdb,当然,我可以通过配置文件进行更加详细配置。
241 rdbcompression yes # 是否压缩rbd文件
253 dbfilename dump.rdb # rdb文件的名称
263 dir /var/lib/redis # rdb文件保存目录
RDB的几个优点:
- 与 AOF 方式相比,通过 RDB 文件恢复数据比较快。
- RDB 文件非常紧凑,适合于数据备份。
- 通过 RDB 进行数据备份,由于使用子进程生成,所以对 Redis 服务器性能影响较小。
RDB 的几个缺点:
- 如果服务器宕机的话,采用 RDB 的方式会造成某个时段内数据的丢失,比如我们设置 10 分钟同步一次或 5 分钟达到 1000 次写入就同步一次,那么如果还没达到触发条件服务器就死机了,那么这个时间段的数据会丢失。
- 使用 Save 命令会造成服务器阻塞,直接数据同步完成才能接收后续请求。
- 使用 Bgsave 命令在 Forks 子进程时,如果数据量太大,Forks 的过程也会发生阻塞,另外,Forks 子进程会耗费内存。
AOF
聊完了 RDB,来聊聊 Redis 的另外一个持久化方式:AOF(Append-only file)。
与 RDB 存储某个时刻的快照不同,AOF 持久化方式会记录客户端对服务器的每一次写操作命令,并将这些写操作以 Redis 协议追加保存到以后缀为 AOF 文件末尾。
在 Redis 服务器重启时,会加载并运行 AOF 文件的命令,以达到恢复数据的目的。

①开启 AOF 持久化方式
Redis 默认不开启 AOF 持久化方式,我们可以在配置文件中开启并进行更加详细的配置,如下面的 redis.conf 文件:
699 appendonly no
703 appendfilename "appendonly.aof"
729 appendfsync everysec
751 no-appendfsync-on-rewrite no
dir /var/lib/redis
错误开启AOF功能,会导致数据丢失
注意:AOF模式默认是关闭的,第一次开启AOF后,并重启服务生效后,会因为AOF的优先级高于RDB,而AOF默认没有数据文件存在,从而导致所有数据丢失
[root@localhost ~]# redis-cli
127.0.0.1:6379> dbsize
(integer) 5000000
[root@localhost ~]# vim /etc/redis.conf
699 appendonly yes #修改此行
[root@localhost ~]# systemctl restart redis
[root@localhost ~]# redis-cli
127.0.0.1:6379> dbsize
(integer) 0
[root@localhost ~]# rm -f /var/lib/redis/appendonly.aof
[root@localhost ~]# vim /etc/redis.conf
699 appendonly no #修改此行
[root@localhost ~]# systemctl restart redis
[root@localhost ~]# redis-cli
127.0.0.1:6379> FLUSHALL
OK
127.0.0.1:6379> DEBUG POPULATE 5000000
OK
(4.86s)
127.0.0.1:6379> DBSIZE
(integer) 5000000
正确启用AOF功能,防止数据丢失
[root@localhost ~]# ll /var/lib/redis/
total 4
-rw-r–r– 1 redis redis 127M Feb 21 17:27 dump.rdb
[root@localhost ~]# redis-cli
127.0.0.1:6379> config get appendonly
1) "appendonly"
2) "no"
127.0.0.1:6379> config set appendonly yes #自动触发AOF重写,会自动备份所有数据到AOF文件
OK
127.0.0.1:6379> config get appendonly
1) "appendonly"
2) "yes"
[root@localhost ~]# ll /var/lib/redis/
total 8
-rw-r–r– 1 redis redis 127M Feb 21 17:29 appendonly.aof
-rw-r–r– 1 redis redis 127M Feb 21 17:29 dump.rdb
[root@localhost ~]# vim /etc/redis.conf
699 appendonly yes #修改此行
[root@localhost ~]# systemctl restart redis
[root@localhost ~]# redis-cli
127.0.0.1:6379> DBSIZE
(integer) 5000000
②三种写入策略
在上面的配置文件中,我们可以通过 appendfsync 选项指定写入策略,有三个选项:
appendfsync always
**always:**客户端的每一个写操作都保存到 AOF 文件当中,这种策略很安全,但是每个写操作都有 IO 操作,所以也很慢。
**everysec:**appendfsync 的默认写入策略,每秒写入一次 AOF 文件,因此,最多可能会丢失 1s 的数据。
**no:**Redis 服务器不负责写入 AOF,而是交由操作系统来处理什么时候写入 AOF 文件。更快,但也是最不安全的选择,不推荐使用。
③AOF 文件重写
AOF重写是通过读取服务器当前的数据库状态来实现的。
我举个例子大家就明白了,假设我对 Redis 执行了下面六条命令:
rpush list "A"
rpush list "B"
rpush list "C"
rpush list "D"
rpush list "E"
rpush list "F"
那么服务器为了保存当前 list键 的状态,会在AOF文件中写入上述六条命令。
而我现在要对 AOF 进行重写的话,其实最高效最简单的方式不是挨个读取和分析现有AOF文件中的这六条命令。
而是直接从数据库中读取键 list 的值,然后用一条命令:rpush list "A" "B" "C" "D" "E" "F",可以直接代替原 AOF 文件中的六条命令。
命令由六条减少为一条,重写的目的就达到了。
**两种重写方式:**通过在 redis.conf 配置文件中的选项 no-appendfsync-on-rewrite 可以设置是否开启重写。
这种方式会在每次 Fsync 时都重写,影响服务器性能,因此默认值为 no,不推荐使用。
no-appendfsync-on-rewrite no
客户端向服务器发送 bgrewriteaof 命令,也可以让服务器进行 AOF 重写。
127.0.0.1:6379> bgrewriteaof
Background append only file rewriting started
[root@localhost ~]# pstree -p | grep redis-server ; ll -h /var/lib/redis/
|-redis-server(1924)-+-redis-server(1940)
| |–{redis-server}(1925)
| |–{redis-server}(1926)
| `-{redis-server}(1927)
total 318M
-rw-r–r– 1 redis redis 127M Feb 24 14:29 appendonly.aof
-rw-r–r– 1 redis redis 127M Feb 24 14:27 dump.rdb
-rw-r–r– 1 redis redis 43M Feb 24 14:32 temp-rewriteaof-1940.aof
AOF 重写方式也是异步操作,即如果要写入 AOF 文件,则 Redis 主进程会 Forks 一个子进程来处理,如下所示:

重写 AOF 文件的好处:
- 压缩 AOF 文件,减少磁盘占用量。
- 将 AOF 的命令压缩为最小命令集,加快了数据恢复的速度。
③AOF 文件损坏 在写入 AOF 日志文件时,如果 Redis 服务器宕机,则 AOF 日志文件文件会出格式错误。
在重启 Redis 服务器时,Redis 服务器会拒绝载入这个 AOF 文件,可以通过以下步骤修复 AOF 并恢复数据:
- 备份现在 AOF 文件,以防万一。
- 使用 redis-check-aof 命令修复 AOF 文件,该命令格式如下:
[root@localhost ~]# redis-check-aof –fix /var/lib/redis/appendonly.aof
The AOF appears to start with an RDB preamble.
Checking the RDB preamble to start:
[offset 0] Checking RDB file –fix
[offset 26] AUX FIELD redis-ver = '5.0.3'
[offset 40] AUX FIELD redis-bits = '64'
[offset 52] AUX FIELD ctime = '1771664520'
[offset 67] AUX FIELD used-mem = '858552'
[offset 83] AUX FIELD aof-preamble = '1'
[offset 85] Selecting DB ID 0
[offset 1202] Selecting DB ID 1
[offset 1245] Checksum OK
[offset 1245] \\o/ RDB looks OK! \\o/
[info] 110 keys read
[info] 0 expires
[info] 0 already expired
RDB preamble is OK, proceeding with AOF tail...
AOF analyzed: size=1245, ok_up_to=1245, diff=0
AOF is valid
- 重启 Redis 服务器,加载已经修复的 AOF 文件,恢复数据。
AOF 的优点:
- AOF 只是追加日志文件,因此对服务器性能影响较小,速度比 RDB 要快,消耗的内存较少。
AOF 的缺点:
- AOF 方式生成的日志文件太大,即使通过 AFO 重写,文件体积仍然很大。
- 恢复数据的速度比 RDB 慢。
选择 RDB 还是 AOF 呢? 通过上面的介绍,我们了解了 RDB 与 AOF 各自的优点与缺点,到底要如何选择呢? 通过下面的表示,我们可以从几个方面对比一下 RDB 与 AOF,在应用时,要根据自己的实际需求,选择 RDB 或者 AOF。
其实,如果想要数据足够安全,可以两种方式都开启。

当 RDB 与 AOF 两种方式都开启时,Redis 会优先使用 AOF 日志来恢复数据,因为 AOF 保存的文件比 RDB 文件更完整。
**小结:**上面讲了一大堆 Redis 的持久化机制的知识,其实,如果你只是单纯把 Redis 作为缓存服务器,那么可以完全不用考虑持久化。
但是,在如今的大多数服务器架构中,Redis 不单单只是扮演一个缓存服务器的角色,还可以作为数据库,保存我们的业务数据,此时,我们则需要好好了解有关 Redis 持久化策略的区别与选择。
客户端连接Redis
[root@localhost ~]# redis-cli
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> info #显示当前节点redis运行状态信息
redis_version:5.0.3
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:9529b692c0384fb7
redis_mode:standalone
os:Linux 4.18.0-553.6.1.el8.x86_64 x86_64
arch_bits:64
multiplexing_api:epoll
atomicvar_api:atomic-builtin
gcc_version:8.4.1
process_id:10350
run_id:fd68b669756ef777ee5d2d4f2a74e851cc243d6a
tcp_port:6379
uptime_in_seconds:676
uptime_in_days:0
hz:10
configured_hz:10
lru_clock:9272993
executable:/usr/bin/redis-server
config_file:/etc/redis.conf
connected_clients:1
client_recent_max_input_buffer:2
client_recent_max_output_buffer:0
blocked_clients:0
used_memory:853896
used_memory_human:833.88K
used_memory_rss:13340672
used_memory_rss_human:12.72M
used_memory_peak:853896
used_memory_peak_human:833.88K
used_memory_peak_perc:100.00%
used_memory_overhead:840694
used_memory_startup:791000
used_memory_dataset:13202
used_memory_dataset_perc:20.99%
allocator_allocated:1457768
allocator_active:1818624
allocator_resident:11468800
total_system_memory:3810115584
total_system_memory_human:3.55G
used_memory_lua:37888
used_memory_lua_human:37.00K
used_memory_scripts:0
used_memory_scripts_human:0B
number_of_cached_scripts:0
maxmemory:0
maxmemory_human:0B
maxmemory_policy:noeviction
allocator_frag_ratio:1.25
allocator_frag_bytes:360856
allocator_rss_ratio:6.31
allocator_rss_bytes:9650176
rss_overhead_ratio:1.16
rss_overhead_bytes:1871872
mem_fragmentation_ratio:16.43
mem_fragmentation_bytes:12528776
mem_not_counted_for_evict:0
mem_replication_backlog:0
mem_clients_slaves:0
mem_clients_normal:49694
mem_aof_buffer:0
mem_allocator:jemalloc-5.1.0
active_defrag_running:0
lazyfree_pending_objects:0
loading:0
rdb_changes_since_last_save:0
rdb_bgsave_in_progress:0
rdb_last_save_time:1770879997
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:-1
rdb_current_bgsave_time_sec:-1
rdb_last_cow_size:0
aof_enabled:0
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:-1
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok
aof_last_cow_size:0
total_connections_received:2
total_commands_processed:5
instantaneous_ops_per_sec:0
total_net_input_bytes:90
total_net_output_bytes:26219
instantaneous_input_kbps:0.00
instantaneous_output_kbps:0.00
rejected_connections:0
sync_full:0
sync_partial_ok:0
sync_partial_err:0
expired_keys:0
expired_stale_perc:0.00
expired_time_cap_reached_count:0
evicted_keys:0
keyspace_hits:0
keyspace_misses:0
pubsub_channels:0
pubsub_patterns:0
latest_fork_usec:0
migrate_cached_sockets:0
slave_expires_tracked_keys:0
active_defrag_hits:0
active_defrag_misses:0
active_defrag_key_hits:0
active_defrag_key_misses:0
role:master
connected_slaves:0
master_replid:24b2280440a498439ac89b31c65249a5af62b08f
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
used_cpu_sys:0.628455
used_cpu_user:0.440542
used_cpu_sys_children:0.000000
used_cpu_user_children:0.000000
cluster_enabled:0
127.0.0.1:6379>
安装的相关程序介绍
[root@localhost ~]# ll /usr/bin/redis-*
-rwxr-xr-x 1 root root 656280 Oct 20 2021 redis-benchmark #性能测试程序
lrwxrwxrwx 1 root root 12 Oct 20 2021 redis-check-aof –> redis-server #AOF文件检查程序
lrwxrwxrwx 1 root root 12 Oct 20 2021 redis-check-rdb –> redis-server #RDB文件检查程序
-rwxr-xr-x 1 root root 827608 Oct 20 2021 redis-cli #客户端程序
lrwxrwxrwx 1 root root 12 Oct 20 2021 redis-sentinel –> redis-server #哨兵程序,软连接到服务器端主程序
-rwxr-xr-x 1 root root 1800864 Oct 20 2021 redis-server #服务端主程序
客户端程序redis-cli
redis-cli
redis-cli -h <Redis服务器IP> -p <PORT> -a <PASSWORD> –no-auth-warning
Redis常用命令
info
显示当前节点redis运行状态信息
127.0.0.1:6379> INFO
redis_version:5.0.3
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:9529b692c0384fb7
redis_mode:standalone
os:Linux 4.18.0-553.6.1.el8.x86_64 x86_64
arch_bits:64
......
127.0.0.1:6379> INFO Server
redis_version:5.0.3
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:9529b692c0384fb7
redis_mode:standalone
os:Linux 4.18.0-553.6.1.el8.x86_64 x86_64
......
[root@localhost ~]# redis-cli info Cluster
cluster_enabled:0
select
切换数据库,相当于MySQL的USE DBNAME指令
[root@localhost ~]# redis-cli
127.0.0.1:6379> info cluster
cluster_enabled:0
127.0.0.1:6379> select 0
OK
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> select 15
OK
127.0.0.1:6379[15]> select 16
(error) ERR DB index is out of range
127.0.0.1:6379[15]>
注意:在Redis cluster模式下不支持多个数据库
KEYS
查看当前库下所有key,此命令慎用
| keys | O(n) |
| dbsize | O(1) |
| del | O(1) |
| exists | O(1) |
| expire | O(1) |
| type | O(1) |
一、 顺丰高级开发工程师在线执行了 Redis 危险命令导致某公司损失 400 万
最近安全事故濒发啊,前几天发生了《顺丰高级运维工程师的删库事件》,今天又看到了 PHP 工程师在线执行了 Redis 危险命令导致某公司损失 400 万。。
什么样的 Redis 命令会有如此威力,造成如此大的损失?
具体消息如下:
据云头条报道,某公司技术部发生 2 起本年度 PO 级特大事故,造成公司资金损失 400 万,原因如下:
由于 PHP 工程师直接操作上线 redis,执行 keys * wxdb(此处省略)cf8* 这样的命令,导致redis锁住,导致 CPU 飙升,引起所有支付链路卡住,等十几秒结束后,所有的请求流量全部挤压到了 rds 数据库中,使数据库产生了雪崩效应,发生了数据库宕机事件。
该公司表示,如再犯类似事故,将直接开除,并表示之后会逐步收回运维部各项权限。
一个命令损失数百万,这,需要赔偿吗?
代码不规范,同事两行泪,撸码需谨慎!
[root@localhost ~]# redis-cli
127.0.0.1:6379> SELECT 0
OK
127.0.0.1:6379> KEYS *
1) "key:91"
2) "key:87"
......
127.0.0.1:6379> SELECT 1
OK
127.0.0.1:6379[1]> KEYS *
(empty list or set)
127.0.0.1:6379[1]> MSET one 1 two 2 three 3 four 4 # 一次设置4个key
OK
127.0.0.1:6379[1]> KEYS *o*
1) "four"
2) "two"
3) "one"
127.0.0.1:6379[1]> KEYS t??
1) "two"
127.0.0.1:6379[1]> KEYS t[w]*
1) "two"
127.0.0.1:6379[1]> KEYS *
1) "three"
2) "four"
3) "two"
4) "one"
bgsave
手动在后台执行RDB持久化操作
127.0.0.1:6379> BGSAVE
Background saving started
[root@localhost ~]# redis-cli BGSAVE
Background saving started
DBSIZE
返回当前库下所有key数量
127.0.0.1:6379> DBSIZE
(integer) 5000000
127.0.0.1:6379> SELECT 1
OK
127.0.0.1:6379[1]> DBSIZE
(integer) 0
FLUSHDB
强制清空当前库中所有key,此命令慎用
127.0.0.1:6379[1]> select 0
OK
127.0.0.1:6379> DBSIZE
(integer) 5000000
127.0.0.1:6379> FLUSHDB #清空db0所有数据
OK
(2.59s)
127.0.0.1:6379> DBSIZE
(integer) 0
127.0.0.1:6379> SELECT 1 #db1数据不影响
OK
127.0.0.1:6379[1]> DBSIZE
(integer) 4
FLUSHALL
强制清空当前Redis服务器所有数据库中的所有key,即删除所有数据,此命令慎用
127.0.0.1:6379> FLUSHALL
OK
[root@localhost ~]# vim /etc/redis.conf
523 rename-command FLUSHALL "" #flushdb和flushall 配置和AOF功能冲突,需要设置appendonly no,不区分命令大小写
699 appendonly no
[root@localhost ~]# systemctl restart redis
[root@localhost ~]# redis-cli
127.0.0.1:6379> FLUSHALL
(error) ERR unknown command `FLUSHALL`, with args beginning with:
127.0.0.1:6379> FLUSHDB
OK
SHUTDOWN
可用版本:>=1.0.0
时间复杂度:O(N),其中 N 为关机时需要保存的数据库键数量。
SHUTDOWN命令执行以下操作:
关闭Redis服务,停止所有客户端连接
如果有至少一个保存点在等待,执行SAVE命令
如果AOF选项被打开,更新AOF文件
关闭redis服务器(server)
如果持久化被打开的话,SHUTDOWN命令会保证服务器正常关闭而不丢失任何数据。
另一方面,假如只是单纯地执行SAVE命令,然后再执行QUIT命令,则没有这一保证一一因为在执行 SAVE之后、执行QUIT之前的这段时间中间,其他客户端可能正在和服务器进行通讯,这时如果执行QUIT就会造成数据丢失。
#建议禁用此指令
[root@localhost ~]# vim /etc/redis.conf
rename-command shutdown ""
Redis数据类型
Redis是一个非关系型数据库(NoSQL),它是一种Key-Value型的NoSQL。Redis所有的key(键)都是字符串。对应的value(值)的数据类型,主要包括常见的5种数据类型,分别是:String、List、Set、Zset(Sorted set)、Hash。
- string(字符串): 基本的数据存储单元,可以存储字符串、整数或者浮点数。
- **hash(哈希)😗*一个键值对集合,可以存储多个字段。
- **list(列表)😗*一个简单的列表,可以存储一系列的字符串元素。
- **set(集合)😗*一个无序集合,可以存储不重复的字符串元素。
- zset(sorted set:有序集合): 类似于集合,但是每个元素都有一个分数(score)与之关联。

字符串string
字符串是一种最基本的Redis值类型。Redis字符串是二进制安全的,这意味着一个Redis字符串能包含任意类型的数据,例如:一张JPEG格式的图片或者一个序列化的Ruby对象。一个字符串类型的值最多能存储512M字节的内容。Redis中所有key都是字符串类型的。此数据类型最为常用
| `SET key value [EX seconds] [PX milliseconds] [NX | XX]` |
| GET key | 获取 key 的值 |
| SETEX key seconds value | 设置并指定过期时间(秒) |
| SETNX key value | 仅当 key 不存在时设置(用于分布式锁) |
| INCR key | 将 key 中的数字加 1 |
| INCRBY key increment | 将 key 中的数字增加指定值 |
| DECR / DECRBY | 减操作 |
| MSET key1 value1 [key2 value2 …] | 批量设置 |
| MGET key1 [key2 …] | 批量获取 |
创建一个key
set指令可以创建一个key并赋值,使用格式
SET key value[EX seconds] [PX milliseconds] [NX|XX]
时间复杂度:O(1)
将字符串值value关联到key。
如果key已经持有其他值,SET就覆写旧值,无视类型。
当SET命令对一个带有生存时间(TTL)的键进行设置之后,该键原有的TTL将被清除。
从Redis2.6.12版本开始,SET命令的行为可以通过一系列参数来修改:
EX seconds:将键的过期时间设置为seconds秒。执行SET key value EX seconds的效果等同于执行 SETEX key seconds value
PX milliseconds:将键的过期时间设置为milliseconds毫秒。执行SET key value PX milliseconds的效果等同于执行 PSETEX key milliseconds value。
NX:只在键不存在时,才对键进行设置操作。执行SET key value NX的效果等同于执行SETNX key value。
XX:只在键已经存在时,才对键进行设置操作
范例:
#不论key是否存在,都设置
127.0.0.1:6379> set key1 value1 #设置键key1,值value1
OK
127.0.0.1:6379> get key1 #查看键key1的值
"value1"
127.0.0.1:6379> type key1 #判断类型
string
127.0.0.1:6379> set title ceo ex 3 #设置自动过期时间3s
OK
127.0.0.1:6379> set name wang #设置键name,值wang
OK
127.0.0.1:6379> get name #查看键name的值
"wang"
127.0.0.1:6379> get title #3s后查看键title
(nil)
127.0.0.1:6379> get NAME #前面赋值的键是小写name,区分大小写
(nil)
127.0.0.1:6379> set NAME li #赋值大写NAME,li
OK
127.0.0.1:6379> get NAME #大写NAME对应li
"li"
127.0.0.1:6379> get name #小写name对应wang
"wang"
127.0.0.1:6379> setnx title coo #只有键title不存在值时,赋值coo
(integer) 1
127.0.0.1:6379> get title
"coo"
127.0.0.1:6379> setnx title ceo #只有键title不存在值时,赋值ceo
(integer) 0
127.0.0.1:6379> get title #由于键title存在值,未更新
"coo"
127.0.0.1:6379> set title ceo xx #只有键已经存在,才操作
OK
127.0.0.1:6379> get title
"ceo"
127.0.0.1:6379> get age
(nil)
127.0.0.1:6379> set age 20 xx #没有age这个键,不操作
(nil)
127.0.0.1:6379> get age
(nil)
查看一个key的值
127.0.0.1:6379> get key1
"value1"
127.0.0.1:6379> get name age
(error) ERR wrong number of arguments for 'get' command
删除key
127.0.0.1:6379> DEL key1
(integer) 1
127.0.0.1:6379> DEL key1 key2
(integer) 0
批量设置多个key
127.0.0.1:6379> MSET key1 value1 key2 vaule2
OK
批量获取多个key
127.0.0.1:6379> MGET key1 key2
1) "value1"
2) "vaule2"
127.0.0.1:6379> KEYS n*
1) "name"
127.0.0.1:6379> KEYS *
1) "name"
2) "key1"
3) "NAME"
4) "title"
5) "key2"
追加key的数据
127.0.0.1:6379> APPEND key1 " append new value"
(integer) 22
127.0.0.1:6379> get key1
"value1 append new value"
设置新值并返回旧值
127.0.0.1:6379> set name wang
OK
127.0.0.1:6379> getset name xiyangyang
"wang"
127.0.0.1:6379> get name
"xiyangyang
返回字符串key对应值的字节数
127.0.0.1:6379> set name wang
OK
127.0.0.1:6379> STRLEN name #返回字节数
(integer) 4
127.0.0.1:6379> APPEND name " teng"
(integer) 9
127.0.0.1:6379> get name
"wang teng"
127.0.0.1:6379> STRLEN name
(integer) 9
判断key是否存在
127.0.0.1:6379> set name wang ex 10
OK
127.0.0.1:6379> EXISTS name #10秒内
(integer) 1
127.0.0.1:6379> EXISTS name age #10秒内
(integer) 2 #返回值为2,表示存在2个key,0表示不存在
127.0.0.1:6379> EXISTS name #10秒后
(integer) 0
获取key的过期时长
ttl key查看key的剩余生存时间,如果key过期后,会自动删除
- -1 返回值表示永不过期,默认创建的key是永不过期,重新对key赋值,也会从有剩余生命周期变成永不过期
- -2 返回值表示没有此key
- num key的剩余有效期
127.0.0.1:6379> ttl key
(integer) -2
127.0.0.1:6379> TTL key1
(integer) -1
127.0.0.1:6379> set name wang EX 100
OK
127.0.0.1:6379> TTL name
(integer) 97
127.0.0.1:6379> TTL name
(integer) 92
127.0.0.1:6379> SET name li #重新设置,默认永不过期
OK
127.0.0.1:6379> TTL name
(integer) -1
127.0.0.1:6379> SET name wang EX 200
OK
127.0.0.1:6379> TTL name
(integer) 198
127.0.0.1:6379> get name
"wang"
重置key的过期时长
127.0.0.1:6379> TTL name
(integer) 97
127.0.0.1:6379> EXPIRE name 1000
(integer) 1
127.0.0.1:6379> TTL name
(integer) 996
取消key的过期时长
127.0.0.1:6379> TTL name
(integer) 957
127.0.0.1:6379> PERSIST name
(integer) 1
127.0.0.1:6379> TTL name
(integer) -1
数字递增
利用INCR命令簇(INCR,DECR,INCRBY,DECRBY)来把字符串当作原子计数器使用。
INCR key 将 key 中的数字加 1
INCRBY key increment 将 key 中的数字增加指定值
127.0.0.1:6379> set num 10
OK
127.0.0.1:6379> INCR num
(integer) 11
127.0.0.1:6379> get num
"11"
127.0.0.1:6379> INCR num
(integer) 12
127.0.0.1:6379> get num
"12"
数字递减
127.0.0.1:6379> set num 10
OK
127.0.0.1:6379> DECR num
(integer) 9
127.0.0.1:6379> get num
"9"
数字增加
将key对应的数字加decrement(可以是负数)。如果key不存在,操作之前,key就会被置为0。如果key的value类型错误或者是个不能表示成数字的字符串,就返回错误。这个操作最多支持64位有符号的正型数字。
127.0.0.1:6379> SET mykey 10
OK
127.0.0.1:6379> INCRBY mykey 5
(integer) 15
127.0.0.1:6379> get mykey
"15"
127.0.0.1:6379> INCRBY mykey -10
(integer) 5
127.0.0.1:6379> get mykey
"5"
127.0.0.1:6379> INCRBY nokey1 5
(integer) 5
127.0.0.1:6379> get nokey1
"5"
数字减少
decrby可以减小数值(也可以增加)
127.0.0.1:6379> SET mykey 10
OK
127.0.0.1:6379> DECRBY mykey 8
(integer) 2
127.0.0.1:6379> get mykey
"2"
127.0.0.1:6379> DECRBY mykey -20
(integer) 22
127.0.0.1:6379> get mykey
"22"
127.0.0.1:6379> DECRBY nokey2 3
(integer) -3
127.0.0.1:6379> get nokey2
"-3"
127.0.0.1:6379> get name
"wang"
127.0.0.1:6379> del name
(integer) 1
127.0.0.1:6379> get name
(nil)
列表list
| LPUSH key value [value …] | 从左边插入元素 |
| RPUSH key value [value …] | 从右边插入元素 |
| LPOP key | 弹出左边第一个元素 |
| RPOP key | 弹出右边第一个元素 |
| LRANGE key start stop | 获取列表指定范围的元素(支持负数索引) |
| LLEN key | 获取列表长度 |
| LINDEX key index | 获取指定索引的元素 |
| LREM key count value | 删除列表中 count 个等于 value 的元素 |

Redis列表实际就是简单的字符串数组,按照插入顺序进行排序
支持双向读写,可以添加一个元素到列表的头部(左边)或者尾部(右边),一个列表最多可以包含2^32-1=4294967295个元素
每个列表元素用下标来标识,下标0表示列表的第一个元素,以1表示列表的第二个元素,以此类推。
也可以使用负数下标,以-1表示列表的最后一个元素,-2表示列表的倒数第二个元素,元素值可以重复,常用于存入日志等场景,此数据类型比较常用
列表特点
- 有序
- 可重复
- 左右都可以操作
创建列表和数据
LPUSH和RPUSH都可以插入列表
LPUSH key value [value _..]
时间复杂度:o(1)
将一个或多个值value插入到列表key的表头
如果有多个value值,那么各个value值按从左到右的顺序依次插入到表头:比如说,对空列表mylist执行命令LPUSH mylist a b c,列表的值将是c b a,这等同于原子性地执行LPUSH mylist a、 LPUSH mylist b和 LPUSH mylist c三个命令。
如果key不存在,一个空列表会被创建并执行LPUSH操作。
当key存在但不是列表类型时,返回一个错误。
RPUSH key value [value ...]
时间复杂度:o(1)
将一个或多个值value插入到列表key的表尾(最右边)。
如果有多个value值,那么各个value值按从左到右的顺序依次插入到表尾:比如对一个空列表mylist 执行RPUSH mylist a b c,得出的结果列表为a b c,等同于执行命令RPUSH mylist a、RPUSH mylist b、RPUSH mylist c。
如果key不存在,一个空列表会被创建并执行RPUSH操作。
当key存在但不是列表类型时,返回一个错误。
右侧插入叫做rpush,右侧删除叫做rpop
左侧插入叫做lpush,左侧删除叫做lpop
范例:
127.0.0.1:6379> LPUSH phone xiaomi apple huawei
(integer) 3
127.0.0.1:6379> TYPE phone
list
127.0.0.1:6379> RPUSH id 18 19 20
(integer) 3
127.0.0.1:6379> type id
list
列表追加新数据
127.0.0.1:6379> LPUSH list1 tom
(integer) 1
127.0.0.1:6379> RPUSH list1 jack
(integer) 2
获取列表长度
127.0.0.1:6379> LLEN list1
(integer) 2
获取列表指定位置元素数据

127.0.0.1:6379> LPUSH list2 a b c d
(integer) 4
127.0.0.1:6379> LINDEX list2 0 #获取0编号的元素
"d"
127.0.0.1:6379> LINDEX list2 3 #获取最后一个的元素
"a"
127.0.0.1:6379> LRANGE list2 1 2
1) "c"
2) "b"
127.0.0.1:6379> LRANGE list2 0 3
1) "d"
2) "c"
3) "b"
4) "a"
127.0.0.1:6379> LRANGE list2 0 -1
1) "d"
2) "c"
3) "b"
4) "a"
127.0.0.1:6379> RPUSH list3 a b c d e
(integer) 5
127.0.0.1:6379> LINDEX list3 0
"a"
127.0.0.1:6379> LRANGE list3 0 -1
1) "a"
2) "b"
3) "c"
4) "d"
5) "e"
修改列表指定索引值
127.0.0.1:6379> RPUSH list4 a b c d e f
(integer) 6
127.0.0.1:6379> LRANGE list4 0 -1
1) "a"
2) "b"
3) "c"
4) "d"
5) "e"
6) "f"
127.0.0.1:6379> lset list4 2 cloud
OK
127.0.0.1:6379> LRANGE list4 0 -1
1) "a"
2) "b"
3) "cloud"
4) "d"
5) "e"
6) "f"
删除列表数据
127.0.0.1:6379> LPUSH list5 a b c d
(integer) 4
127.0.0.1:6379> lrange list5 0 3
1) "d"
2) "c"
3) "b"
4) "a"
127.0.0.1:6379> lpop list5 #弹出左边第一个元素,即删除第一个
"d"
127.0.0.1:6379> lrange list5 0 3
1) "c"
2) "b"
3) "a"
127.0.0.1:6379> RPOP list5 #弹出右边第一个元素,即删除最后一个
"a"
127.0.0.1:6379> lrange list5 0 3
1) "c"
2) "b"
#LTRIM对一个列表进行修剪(trim),让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删
127.0.0.1:6379> RPUSH list6 a b c d e
(integer) 5
127.0.0.1:6379> lrange list6 0 -1
1) "a"
2) "b"
3) "c"
4) "d"
5) "e"
127.0.0.1:6379> LTRIM list6 2 3
OK
127.0.0.1:6379> lrange list6 0 -1
1) "c"
2) "d"
删除list
127.0.0.1:6379> DEL list1
(integer) 1
127.0.0.1:6379> EXISTS list1
(integer) 0
集合set
| SADD key member [member …] | 添加一个或多个成员 |
| SMEMBERS key | 获取集合所有成员 |
| SISMEMBER key member | 判断成员是否在集合中 |
| SREM key member [member …] | 删除一个或多个成员 |
| SCARD key | 获取集合成员数量 |
| SINTER key1 [key2 …] | 求多个集合的交集 |
| SUNION key1 [key2 …] | 求多个集合的并集 |
| SDIFF key1 [key2 …] | 求差集 |
Set是一个无序的字符串合集
同一个集合中的每个元素是唯一无重复的
支持在两个不同的集合中对数据进行逻辑处理,常用于取交集,并集,统计等场景,例如:实现共同的朋友
集合特点
- 无序
- 无重复
- 集合间操作
创建集合
127.0.0.1:6379> SADD set1 v1
(integer) 1
127.0.0.1:6379> SADD set2 v2 v4
(integer) 2
127.0.0.1:6379> TYPE set1
set
127.0.0.1:6379> TYPE set2
set
集合中追加数据
127.0.0.1:6379> SADD set1 v2 v3 v4
(integer) 3
127.0.0.1:6379> SADD set1 v2 #已存在的value,无法再添加
(integer) 0
获取集合的所有数据
127.0.0.1:6379> SMEMBERS set1
1) "v4"
2) "v3"
3) "v1"
4) "v2"
127.0.0.1:6379> SMEMBERS set2
1) "v4"
2) "v2"
删除集合中的元素
127.0.0.1:6379> SADD name1 xiyangyang meiyangyang feiyangyang
(integer) 3
127.0.0.1:6379> SMEMBERS name1
1) "feiyangyang"
2) "meiyangyang"
3) "xiyangyang"
127.0.0.1:6379> srem name1 meiyangyang
(integer) 1
127.0.0.1:6379> SMEMBERS name1
1) "feiyangyang"
2) "xiyangyang"
集合间操作
获取集合的交集
交集:同时属于集合A和集合B的元素
比如QQ共同好友
127.0.0.1:6379> SINTER set1 set2
1) "v4"
2) "v2"
取集合的并集
并集:属于集合A或者属于集合B的元素
127.0.0.1:6379> SUNION set1 set2
1) "v3"
2) "v1"
3) "v2"
4) "v4"
取集合的差集
差集:属于集合A但不属于集合B的元素
比如我朋友的朋友
127.0.0.1:6379> SDIFF set1 set2
1) "v3"
2) "v1"
有序集合sorted set(ZSET)
| ZADD key score member [score member …] | 添加带分数的成员 |
| ZRANGE key start stop [WITHSCORES] | 按分数升序返回成员 |
| ZREVRANGE key start stop [WITHSCORES] | 按分数降序返回成员 |
| ZREM key member [member …] | 删除成员 |
| ZCARD key | 获取成员数量 |
| ZSCORE key member | 获取成员的分数 |
| ZRANK key member | 获取成员的排名(升序) |
Redis有序集合和Redis集合类似,是不包含相同字符串的合集。
它们的差别是,每个有序集合的成员都关联着一个双精度浮点型的评分
这个评分用于把有序集合中的成员按最低分到最高分排序。
有序集合的成员不能重复,但评分可以重复,一个有序集合中最多的成员数为2^32-1=4294967295个,经常用于排行榜的场景

有序集合特点
- 有序
- 无重复元素
- 每个元素是由score和value组成
- score可以重复
- value不可以重复
创建有序集合
127.0.0.1:6379> ZADD zset1 1 v1 #分数为1
(integer) 1
127.0.0.1:6379> ZADD zset1 2 v2
(integer) 1
127.0.0.1:6379> ZADD zset1 2 v3 #分数可重复,元素值不可以重复
(integer) 1
127.0.0.1:6379> ZADD zset1 3 v4
(integer) 1
127.0.0.1:6379> TYPE zset1
zset
#一次生成多个数据
127.0.0.1:6379> ZADD zset2 1 v1 2 v2 3 v3 4 v4 5 v5
(integer) 5
实现排名
127.0.0.1:6379> ZADD course 90 linux 99 go 60 python 50 cloud
(integer) 4
127.0.0.1:6379> ZRANGE course 0 -1 #正序排序后显示集合内所有的key,按score从小到大显示
1) "cloud"
2) "python"
3) "linux"
4) "go"
127.0.0.1:6379> ZREVRANGE course 0 -1 #倒序排序后显示集合内所有的key,score从大到小显示
1) "go"
2) "linux"
3) "python"
4) "cloud"
127.0.0.1:6379> ZRANGE course 0 -1 WITHSCORES #正序显示指定集合内所有key和得分情况
1) "cloud"
2) "50"
3) "python"
4) "60"
5) "linux"
6) "90"
7) "go"
8) "99"
127.0.0.1:6379> ZREVRANGE course 0 -1 WITHSCORES #倒序显示指定集合内所有key和得分情况
1) "go"
2) "99"
3) "linux"
4) "90"
5) "python"
6) "60"
7) "cloud"
8) "50"
查看集合的成员个数
127.0.0.1:6379> ZCARD course
(integer) 4
127.0.0.1:6379> ZCARD zset1
(integer) 4
127.0.0.1:6379> ZCARD zset2
(integer) 5
基于索引查找数据
127.0.0.1:6379> ZRANGE course 0 2
1) "cloud"
2) "python"
3) "linux"
127.0.0.1:6379> ZRANGE course 0 10 #超出范围不报错
1) "cloud"
2) "python"
3) "linux"
4) "go"
127.0.0.1:6379> ZRANGE zset1 1 3
1) "v2"
2) "v3"
3) "v4"
127.0.0.1:6379> ZRANGE zset1 0 2
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> ZRANGE zset1 2 2
1) "v3"
查询指定数据的排名
127.0.0.1:6379> ZRANK course go
(integer) 3
127.0.0.1:6379> ZRANK course python
(integer) 1
获取分数
127.0.0.1:6379> ZSCORE course cloud
"50"
删除元素
127.0.0.1:6379> ZRANGE course 0 -1
1) "cloud"
2) "python"
3) "linux"
4) "go"
127.0.0.1:6379> ZREM course python go
(integer) 2
127.0.0.1:6379> ZRANGE course 0 -1
1) "cloud"
2) "linux"
哈希hash
| HSET key field value | 设置 hash 字段的值 |
| HGET key field | 获取 hash 字段的值 |
| HMSET key field1 value1 [field2 value2 …] | 批量设置字段(Redis 4.0+ 推荐用 HSET) |
| HMGET key field1 [field2 …] | 批量获取字段 |
| HGETALL key | 获取 hash 中所有字段和值 |
| HDEL key field [field …] | 删除一个或多个字段 |
| HEXISTS key field | 判断字段是否存在 |
hash即字典,用于保存字符串字段field和字符串值value之间的映射,即key/value做为数据部分
hash特别适合用于存储对象场景
一个hash最多可以包含2^32-1个key/value键值对
哈希特点
- 无序
- K/V对
- 适用于存放相关的数据


创建hash
格式:
HSET hash field value
时间复杂度:o(1)
将哈希表hash中域field的值设置为value
如果给定的哈希表并不存在,那么一个新的哈希表将被创建并执行HSET操作。
如果域field已经存在于哈希表中,那么它的旧值将被新值value覆盖。
范例:
127.0.0.1:6379> HSET 9527 name zhouxingxing age 20
(integer) 2
127.0.0.1:6379> TYPE 9527
hash
127.0.0.1:6379> hgetall 9527
1) "name"
2) "zhouxingxing"
3) "age"
4) "20"
#增加字段
127.0.0.1:6379> HSET 9527 gender male
(integer) 1
127.0.0.1:6379> HGETALL 9527
1) "name"
2) "zhouxingxing"
3) "age"
4) "20"
5) "gender"
6) "male
查看hash的指定filed的value
127.0.0.1:6379> HGET 9527 name
"zhouxingxing"
127.0.0.1:6379> HGET 9527 age
"20"
127.0.0.1:6379> HMGET 9527 name age
1) "zhouxingxing"
2) "20"
删除hash的指定的field/value
127.0.0.1:6379> HDEL 9527 age
(integer) 1
127.0.0.1:6379> HGET 9527 age
(nil)
127.0.0.1:6379> HGETALL 9527
1) "name"
2) "zhouxingxing"
3) "gender"
4) "male"
批量设置hash key的多个field和value
127.0.0.1:6379> HMSET 9527 name zhouxingxing age 50 city hongkong
OK
127.0.0.1:6379> HGETALL 9527
1) "name"
2) "zhouxingxing"
3) "gender"
4) "male"
5) "age"
6) "50"
7) "city"
8) "hongkong"
查看hash指定field的value
127.0.0.1:6379> HMGET 9527 name age
1) "zhouxingxing"
2) "50"
查看hash的所有field
127.0.0.1:6379> HKEYS 9527
1) "name"
2) "gender"
3) "age"
4) "city"
查看hash所有value
127.0.0.1:6379> HVALS 9527
1) "zhouxingxing"
2) "male"
3) "50"
4) "hongkong"
查看指定hash的所有field及value
127.0.0.1:6379> HGETALL 9527
1) "name"
2) "zhouxingxing"
3) "gender"
4) "male"
5) "age"
6) "50"
7) "city"
8) "hongkong"
删除hash
127.0.0.1:6379> DEL 9527
(integer) 1
127.0.0.1:6379> EXISTS 9527
(integer) 0
消息队列
消息队列:把要传输的数据放在队列中,从而实现应用之间的数据交换
常用功能:可以实现多个应用系统之间的解耦,异步,削峰/限流等
常用的消息队列应用:Kafka,RabbitMQ,Redis

消息队列分为两种
- 生产者/消费者模式:Producer/Consumer
- 发布者/订阅者模式:Publisher/Subscriber
生产者/消费者模式
生产者消费者模式下,多个消费者同时监听一个频道(redis用队列实现),但是生产者产生的一个消息只能被最先抢到消息的一个消费者消费一次,队列中的消息由可以多个生产者写入,也可以有不同的消费者取出进行消费处理,此模式应用广泛


思来想去,只有 List 和 Streams 两种数据类型,可以实现消息队列的这些需求,当然,Redis 还提供了发布、订阅(pub/sub) 模式。
List 实现消息队列
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
所以常用来做异步队列使用。将需要延后处理的任务结构体序列化成字符串塞进 Redis 的列表,另一个线程从这个列表中轮询数据进行处理。
挑几个弹入、弹出的命令就可以组合出很多姿势
- LPUSH、RPOP 左进右出
- RPUSH、LPOP 右进左出

案例:
[root@localhost ~]# redis-cli
127.0.0.1:6379> LPUSH haizeiwang jishu1
(integer) 1
127.0.0.1:6379> LPUSH haizeiwang jishu2
(integer) 2
127.0.0.1:6379> LPUSH haizeiwang jishu3
(integer) 3
127.0.0.1:6379> LPUSH haizeiwang jishu4
(integer) 4
127.0.0.1:6379> LPUSH haizeiwang jishu5
(integer) 5
127.0.0.1:6379> LRANGE haizeiwang 0 -1
1) "jishu5"
2) "jishu4"
3) "jishu3"
4) "jishu2"
5) "jishu1"
127.0.0.1:6379> RPOP haizeiwang #基于实现消息队列的先进先出原则,从管道的右侧消费
"jishu1"
127.0.0.1:6379> RPOP haizeiwang
"jishu2"
127.0.0.1:6379> RPOP haizeiwang
"jishu3"
127.0.0.1:6379> RPOP haizeiwang
"jishu4"
127.0.0.1:6379> RPOP haizeiwang
"jishu5"
127.0.0.1:6379> RPOP haizeiwang
(nil)
127.0.0.1:6379> LRANGE haizeiwang 0 -1
(empty list or set)
即时消费问题
通过 LPUSH,RPOP 这样的方式,会存在一个性能风险点,就是消费者如果想要及时的处理数据,就要在程序中写个类似 while(true) 这样的逻辑,不停的去调用 RPOP 或 LPOP 命令,这就会给消费者程序带来些不必要的性能损失。
所以,Redis 还提供了 BLPOP、BRPOP 这种阻塞式读取的命令(带 B-Bloking的都是阻塞式),客户端在没有读到队列数据时,自动阻塞,直到有新的数据写入队列,再开始读取新数据。这种方式就节省了不必要的 CPU 开销。
- LPUSH、BRPOP 左进右阻塞出
- RPUSH、BLPOP 右进左阻塞出
127.0.0.1:6379> LPUSH haizeiwang jishu1 jishu2 jishu3 jishu4 jishu5
(integer) 5
127.0.0.1:6379> LRANGE haizeiwang 0 -1
1) "jishu5"
2) "jishu4"
3) "jishu3"
4) "jishu2"
5) "jishu1"
127.0.0.1:6379> BRPOP haizeiwang 10
1) "haizeiwang"
2) "jishu1"
127.0.0.1:6379> BRPOP haizeiwang 10
1) "haizeiwang"
2) "jishu2"
127.0.0.1:6379> BRPOP haizeiwang 10
1) "haizeiwang"
2) "jishu3"
127.0.0.1:6379> BRPOP haizeiwang 10
1) "haizeiwang"
2) "jishu4"
127.0.0.1:6379> BRPOP haizeiwang 10
1) "haizeiwang"
2) "jishu5"
127.0.0.1:6379> BRPOP haizeiwang 10 #如果将超时时间设置为 0 时,即可无限等待,直到弹出消息
1) "haizeiwang"
2) "jishu6"
(2.73s)
127.0.0.1:6379>
发布者/订阅者模式
在发布者订阅者Publisher/Subscriber模式下,发布者Publisher将消息发布到指定的频道channel,事先监听此channel的一个或多个订阅者Subscriber都会收到相同的消息。即一个消息可以由多个订阅者获取到,对于社交应用中的群聊、群发、群公告等场景适用于此模式

案例:
#窗口A:
[root@localhost ~]# redis-cli
127.0.0.1:6379> SUBSCRIBE douluodalu #订阅者事先订阅指定的频道,之后发布的消息才能收到
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "douluodalu"
3) (integer) 1
#窗口B:
127.0.0.1:6379> SUBSCRIBE douluodalu haizeiwang #订阅多个频道
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "douluodalu"
3) (integer) 1
1) "subscribe"
2) "haizeiwang"
3) (integer) 2
#窗口C:
127.0.0.1:6379> PUBLISH douluodalu fuhuoba,wodeairen
(integer) 2 <span class=\"token