ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Redis命令实战手册:从五大类型到分布式锁与集群

Redis命令实战手册:从五大类型到分布式锁与集群 先问一个很常见的问题面试官让你说说 Redis 的常用命令或者你在排查一个线上缓存问题时脑子里能立刻反应出的到底有几个命令我见过不少开发同学日常业务里确实只用了set和get但一旦遇到 key 过期、缓存穿透、分布式锁、主从切换这些场景就完全不知道从哪个命令下手了。Redis 的命令体系并不复杂但它的覆盖范围和设计逻辑远不止“存一个值、取一个值”这么简单。这篇博文就是从“基础常用命令”出发把字符串、哈希、列表、集合、有序集合这五大类型以及过期、持久化、事务、管道、分布式锁、主从集群这些高频场景全部串一遍。每一条命令我都会标注它在实际项目中解决什么问题并把参数含义、返回值、注意事项也一并讲清楚。这篇文章的服务对象很明确刚接触 Redis 的新手可以把它当成一份带解释的命令手册写过一段时间 Redis 但只停留在set/get的开发者可以从里面的生产场景组合命令里找到“原来还能这么用”的启发准备面试的人也可以从这些命令背后的原理和坑里补一补底气。1. 先把 Redis 的“家底”摸清楚安装、启动、连接1.1 Redis 到底是什么为什么命令这么受重视Redis 的全称是 Remote Dictionary Server翻译过来就是“远程字典服务”。它本质上是一个基于内存的键值存储系统数据主要放在内存里所以读写速度非常快因此被广泛用在缓存、会话存储、消息队列、排行榜、分布式锁等场景。理解了“字典”这两个字Redis 命令的设计逻辑就很好懂了。你平时查字典核心就是“根据某个词找到对应的解释”Redis 也一样——大多数命令都是围绕key这个维度展开的先找到 key再针对 value 的数据类型做操作。正因为这种简单的设计它的命令不存在复杂的查询语法反而全部是动词开头的短命令好记、好用、好扩展。我常说学 Redis 命令不要死记你只需要记住“每个 key 背后有五种基本数据类型中的一种”然后每种类型都有一套对应的“增删改查”方法整体框架就清晰了。后面我会把这五套方法逐一展开你会发现它们的命名规律非常强。1.2 安装、启动的多种姿势与选择逻辑在工作环境里Redis 的安装方式大体有三种本地二进制安装、Docker 容器安装、云服务托管。我见过很多人在本地折腾半天装不好其实不是 Redis 本身难装而是没弄清楚自己的操作系统和版本差异。本地安装的通用流程在 Linux 和 macOS 上基本都是下载源码包或通过包管理器安装然后启动redis-server再用redis-cli连接。Windows 环境官方并没有正式支持现在常见的是使用 WSL 跑 Linux 子系统或者使用微软维护的 Redis 移植版本。我的建议是如果只是为了本地学习Docker 是最快的路径docker run -d --name redis-local -p 6379:6379 redis:7.2上面这条命令会下载官方 Redis 7.2 镜像把容器内部默认的 6379 端口映射到宿主机的 6379 端口然后以守护进程方式运行。启动之后你本机再用任意客户端连接localhost:6379就能直接使用了。容器方式的优势是干净、可重复不会污染你的系统环境也不需要自己处理那些依赖库的问题。安装完成后第一个要学的命令不是存数据而是验证服务是否正常。用redis-cli进入交互模式后第一件事就是执行ping如果返回PONG说明服务活着。这里有个细节经验ping在 Redis 里不只是测试连通性它也可以带参数比如ping hello会返回hello平时调试脚本时可以拿来快速验证连接是否通。1.3 连接、认证与日常状态检查生产环境里的 Redis 一般都会设置密码连接命令就不会是裸的redis-cli了。带密码连接有两种常见姿势一种是启动时直接带上-a参数另一种是进入交互模式后执行auth命令。redis-cli -h 127.0.0.1 -p 6379 -a yourpassword127.0.0.1:6379 auth yourpassword OK我比较推荐第二种因为-a配合进程列表时会有密码泄露到系统日志的风险虽然 Redis 自己会把命令里的密码做脱敏处理但能避免就避免。连接之后建议先把这几个状态命令过一遍info用来查看服务端的整体运行信息包括内存使用、连接数、持久化状态、复制信息等dbsize用来快速查看当前数据库有多少个 keyselect用来切换数据库编号client list用来查看当前有哪些客户端连着排查连接数打满时它派得上大用场。info命令的输出确实很长但不需要全看。日常我主要关注三个段落Memory段里的used_memory当前占用内存和maxmemory最大内存限制Persistence段里的rdb_last_save上一次 RDB 持久化的时间和aof_last_write_statusAOF 写入是否正常Replication段里的role当前节点身份是 master 还是 slave。这几个字段排查内存暴涨、数据丢时间、主从状态异常的时候都是最先要看的。2. 五种数据类型的命令全家桶2.1 String不只是 set 和 getString 是 Redis 里最基础、最灵活的类型value 可以是一个字符串、一个数字甚至是一张序列化后的图片。它的命令量大但规律清晰。首先是核心的set和get但我要强调set命令有很多扩展参数这是很多人忽略的SET key value [EX seconds] [PX milliseconds] [NX|XX]EX表示多少秒后过期NX表示只有当 key 不存在时才设置成功XX表示只有当 key 已存在时才设置成功。这个命令在分布式锁场景中是绝对主力后面我会单独展开。数字场景下String 的incr、decr、incrby、decrby也非常常见。它们不是简单的“读取修改写回”而是 Redis 内部执行的原子操作所以多个客户端同时对一个 key 做自增时不会出现互相覆盖的问题。典型的应用就是计数器、点赞数、库存扣减。SET product_stock 10 DECRBY product_stock 2这里有一个非常容易踩的坑incr操作时如果 key 的 value 不是数字Redis 会返回错误提示 value is not an integer。所以给 key 设计的时候要么保证这个 key 的值永远存数字要么在写入时用类型规范自我约束好。热词里有一个“redis incr不准”的问题大多数情况都不是命令本身的问题而是业务代码在读取库存后又做了本地计算再写回绕过了原子自增的逻辑导致并发场景下数据对不上。用incr/decr族命令就没有这个困扰。2.2 Hash最适合存“对象”的结构Hash 类型在 Redis 里是一个 string 类型的 field 和 value 的映射表特别适合存一个对象的多个属性。比如用户维度的数据如果拆成多个 String key每个都要独立管理过期时间非常零碎用一个 Hash key把整个用户对象都放进去field 就是属性名。常用命令如下HSET user:1001 name 张三 age 18 city 杭州 HGET user:1001 name HGETALL user:1001 HINCRBY user:1001 age 1HGETALL会把该 key 下所有 field-value 对都取出来适合调试时使用。但生产环境下我不建议频繁调用因为如果你的 Hash 里字段很多一次取全量数据会产生比较大的网络开销。更好用的命令是HMGET它支持一次获取多个指定 fieldHMGET user:1001 name age还有一个容易忽略的命令是HSCAN。当 Hash 里的字段数量巨大时HGETALL会把服务端阻塞住或者产生超大响应包HSCAN则支持分批迭代和后面的通用SCAN命令异曲同工适合线上安全排查。Hash 的底层存储有两种编码方式字段少的时候用 ziplist压缩列表字段多了以后会转成 hashtable。理解这个转换很重要因为它直接影响内存占用。在小对象场景下ziplist 的内存效率比 hashtable 高很多所以不要一上来就把 Hash 设计成几百个字段尽量让单个 Hash 的字段数量在一个可控范围内。2.3 List队列、栈、时间线List 是一个双向链表结构头部和尾部都可以进行快速操作。它是实现队列和栈的一把好手。最经典的生产命令是LPUSHRPOP的组合。LPUSH往列表左侧塞数据RPOP从右侧取数据两者配合就是一个先进先出的 FIFO 队列。反过来LPUSHLPOP就是一个栈后进先出。在实现业务消息队列时有人会直接拿它当简单的中间层但我要提醒一点List 原生命令并不支持“消费者确认”这回事消息被RPOP取出后如果处理失败消息就丢了。除非你只是处理不重要的通知类数据否则生产级队列还是老老实实用专业的消息队列组件或者自己在外层加确认机制。LRANGE是列表命令里用的频率最高的一个它用来获取区间内的元素LRANGE feed:user 0 9上面的命令取列表前 10 条数据非常契合“最近动态”“排行榜时间线”这类场景。要注意LRANGE的两个参数是闭区间所以 0 到 9 实际取了 10 条。这里还要提一个命令LLEN它返回列表的长度。很多人没意识到它还承担着一个业务层面的检查作用——在轮询队列时先看LLEN是否为 0为 0 说明队列空可以避免无意义的RPOP调用。2.4 Set去重、交集与随机事件Set 是无序、去重的字符串集合。它的存在价值不只是存储更是集合运算。日常命令里SADD和SMEMBERS最基础SADD user:1001:tags backend java redis SMEMBERS user:1001:tags这里我要重点说说集合运算命令。SINTER用于取交集SUNION用于取并集SDIFF用于取差集。举一个业务例子系统要找出“既在 A 活动里报名又在 B 活动里中奖”的用户那直接对两个 Set 取交集就行全在 Redis 里完成不需要把这些数据拉到内存中再手动算。SINTER activity:A:users activity:B:usersSISMEMBER判断某个元素是否在集合中时间复杂度是 O(1)。它非常适合“判断用户是否属于某个白名单”“判断用户是否已点赞过”这类场景。如果你用 String 去保存一个 ID 列表再反序列化判断性能和资源消耗都会很难看。还有一个有意思的命令是SPOP它从集合中随机移除一个或多个元素。以前做抽奖活动时我就靠它做“随机抽一个幸运用户”实现成本极低而且天然去重不需要担心同一个用户被抽两次。2.5 ZSet带权重的排序利器ZSet 和 Set 的区别就是每个元素额外带了一个分数scoreRedis 内部会按 score 排序。它是实现排行榜功能最强的数据结构没有之一。常用命令ZADD leaderboard 1000 user_a ZINCRBY leaderboard 50 user_a ZREVRANGE leaderboard 0 9 WITHSCORES第一个命令是初始化用户 A 的分数为 1000第二个命令是对用户 A 增加 50 分第三个命令是取分数最高的前 10 名。排行榜更新和查询都在这几个命令里完成了整个链路走 Redis 而不查数据库响应速度非常快。ZSet 和 List 的最大区别在于List 是物理顺序由插入位置决定ZSet 是逻辑顺序由分数决定且能动态调整。所以你做一个动态排序需求时优先考虑 ZSet而不是把 List 里的数据捞出来再排序。关于 ZSet 的底层编码也要提一句当元素数量小于 128 且每个元素的长度小于 64 字节时它使用 ziplist 编码查找效率是 O(N)当元素变多时会转为 skiplist查找效率更高。这个转换是 Redis 自动完成的你不需要干预但理解它有助于你想清楚为什么小规模 ZSet 的插入性能反而很快。3. Key 通用操作与过期、持久化命令3.1 通用 Key 操作命令不管 value 是什么类型key 本身有一组通用命令。EXISTS判断 key 是否存在DEL删除 keyTYPE查看 key 的数据类型TTL查看剩余过期时间RENAME重命名 key。这些命令在开发调试和排查问题时都极其常用。一条命令搞定多个 key 删除是这个组的亮点DEL cache:user:1001 cache:user:1002 cache:user:1003在实际运维里清理缓存时最容易踩的坑是用KEYS pattern去匹配删除。KEYS user:*这个命令在没有限制的情况下会扫描全部 key如果 key 数量达到百万级别它会长时间阻塞 Redis导致整个服务卡顿。线上严禁使用这是我反复强调的。正确的扫描姿势是用SCAN命令它每次只返回一批结果支持游标和平滑控制不会阻塞服务SCAN 0 MATCH user:* COUNT 100SCAN返回的第一个结果是下一次迭代要用的游标游标回到 0 表示遍历完了。写清理脚本时先给它封装一层再配合DEL做批量删除安全很多。3.2 过期时间与内存淘汰EXPIRE用来给一个已存在的 key 设置过期时间单位是秒PEXPIRE单位是毫秒TTL查看剩余时间。还有一个PERSIST命令可以把 key 的过期时间取消掉让它永久保留。过期时间在业务里最典型的应用就是“验证码五分钟内有效”“会话二十四小时有效”。命令一般是SET的时候直接带EX而不是另外再补一条EXPIRE因为两步操作之间如果服务突然宕机key 可能变成永不过期的脏数据。利用一个原子命令把“写入”和“过期时间设置”合二为一是最好的习惯。另一个被忽略的机制是“过期策略”。Redis 在访问某个 key 时会检查它的过期时间如果已过期就立即删除这是一种惰性删除。同时它也会周期性地扫描过期 key 并删除这是定期删除。两种策略配合既避免每次访问都做全量扫描也防止过期 key 一直躺着占内存。当内存达到maxmemory上限时Redis 会触发内存淘汰策略。常见的有volatile-lru只淘汰设置了过期时间的 key 中最近最少使用的、allkeys-lru全部 key 中最近最少使用的、noeviction不淘汰直接拒绝写请求。config get maxmemory-policy可以查看当前的淘汰策略这个参数在缓存命中率优化中非常关键。3.3 持久化命令与数据安全Redis 的数据持久化有两种方式RDB快照和AOF追加文件。RDB 是某个时间点的全量内存数据快照AOF 则是把所有写操作以日志形式追加到文件里恢复时重放日志。和持久化相关的命令有这几个SAVE会同步执行一次全量快照期间阻塞所有客户端线上千万别用BGSAVE是异步执行快照后台 fork 一个子进程去做不会阻塞主线程处理命令这个才是日常该用的BGREWRITEAOF用来重写 AOF 日志把日志里冗余的命令合并压缩成一个最小的命令集。BGSAVE BGREWRITEAOF通过info persistence可以看到rdb_bgsave_in_progress和aof_rewrite_in_progress两个字段它们能反映后台持久化任务是否正在执行。如果系统启动时开启了 AOFRedis 启动时会自动加载 AOF 文件这个加载期间的日志里能观察到“DB loaded from append only file”之类的信息。我强烈建议刚接触 Redis 的同学在自己做实验时养成先BGSAVE再关服务的习惯尤其是你改了一些重要配置之后先做一次快照避免进程异常退出后数据全丢。4. 事务、管道与 Lua批量操作的正确姿势4.1 事务命令和它的局限性Redis 的事务由四个命令组成MULTI开启事务EXEC执行事务DISCARD取消事务WATCH用于乐观锁监控。MULTI SET stock:001 100 DECRBY stock:001 10 EXEC在MULTI和EXEC之间的命令并不会立即执行而是排队。EXEC时按顺序执行期间不会穿插其他客户端的命令这就是 Redis 事务的基本隔离性。但是Redis 事务和关系型数据库事务有本质区别。它不支持回滚命令入队时不检查逻辑错误只有在真正执行时才报错。如果执行到某条命令发现语法或类型错误之前的命令已经执行的结果不会撤销。我把这个特点解读为“Redis 事务只保证原子性不保证一致性”是最贴切的。WATCH命令稍微复杂一点它能在事务执行前监听一个或多个 key。如果在EXEC前这些 key 被其他客户端修改了本次事务会直接失败返回nil。它提供了一种“乐观锁”机制适合做 CASCompare And Set类的操作。实际业务里我见用WATCH的频率不高因为复杂度不小而且替代方案也很多比如用 Lua 脚本实现原子化。4.2 Pipeline批量提交减少 RTTPipeline 并不是一条命令而是一种客户端操作模式。普通模式下你发 1000 条命令到 Redis每一条都要等待网络往返RTT非常慢。Pipeline 把多个命令一次性打包发送Redis 服务端处理完一次性返回所有结果网络开销大幅降低。它的使用方式是客户端代码层的事情Redis 服务端不需要特别配置。以 Java 的 Lettuce 客户端为例你可以在一个连接上批量发送命令再把结果按顺序组装。Redis 官方文档里给过一个数据单个命令一旦走 Pipeline吞吐量能有数量级的提升。Pipeline 有一个使用纪律它适合那些互不依赖的命令。如果你要执行第二条命令时必须依赖第一条命令的结果是不能放进 Pipeline 的。例如“先GET某个 key再根据结果SET另一个 key”这种有依赖关系的流程就别强行 Pipeline 了。4.3 Lua 脚本把多条命令变成原子操作Lua 脚本在 Redis 里的地位非常高它弥补了事务在“条件判断”和“逻辑控制”方面的不足。通过EVAL或SCRIPT LOAD把一段 Lua 代码交给 Redis 执行脚本内部的所有 Redis 命令都是原子执行的不会有其他客户端命令插入。if redis.call(GET, KEYS[1]) ARGV[1] then return redis.call(DEL, KEYS[1]) else return 0 end上面这段脚本是分布式锁释放的标准写法只有当 key 的值等于我传入的值时才删除否则直接返回。Redis 在 Lua 脚本执行期间会阻塞其他命令所以脚本必须短小精悍绝不能在里面做耗时的循环或调用不确定时间的外部接口。Lua 脚本的调试思路也很直接先用真实的 key 做实验确认返回值符合预期后再放到生产环境。我这里强调一句很多“并发数据不对”的问题用 Lua 脚本可以大大简化尤其适合库存扣减、限额控制、原子抢购这类场景。5. 生产场景命令组合缓存治理与分布式锁5.1 分布式锁的最简正确姿势分布式锁是 Redis 在微服务架构下最常见的应用之一。它的核心思路是多个进程之间用同一个 key 作为锁标志谁先设置成功谁就拿到了锁谁用完谁删除锁释放。现在推荐的锁命令是SET key value NX PX timeout这一条组合。NX保证 key 不存在时才能设置成功PX让 key 在指定毫秒后自动过期避免持锁进程崩溃导致死锁。它的完整步骤如下SET lock:order:1001 uuid_v1 NX PX 30000 -- 业务处理 -- 释放锁时要比较 value 是否等于自己的标识 if redis.call(GET, KEYS[1]) ARGV[1] then return redis.call(DEL, KEYS[1]) end锁的 value 要赋一个唯一的、发锁时生成的标识比如 UUID释放锁时先比较 value 是不是自己的再执行删除。为什么要这么麻烦因为如果你不判断就直接DEL有可能把一个还在被别的线程持着的锁误删导致两个线程同时进入临界区整个分布式锁就失效了。生产级的锁还会考虑结合 Redlock 算法部署多台独立 Redis 节点但核心命令仍然离不开SET NX PX。对于大多数业务单机 Redis 合理的过期时间 安全的解锁脚本已经能覆盖 95% 的场景。5.2 缓存穿透、击穿、雪崩的治理命令这三个问题在面试题里出现频率极高它们都和缓存的使用姿势有关。缓存穿透指的是查询一个根本不存在的数据缓存里没有数据库里也没有。每次请求都打到数据库数据库压力陡增。常用的命令级策略是“缓存空值”即使数据库里没有查到也在 Redis 里给这个 key 存一个空值或占位符并设置较短过期时间。另一个策略是布隆过滤器它能快速判断某个值是否在集合中前置在 Redis 查询之前把不存在的请求挡掉。缓存击穿指的是某个极热门的 key 在过期的一瞬间大量请求同时打到数据库。常见解法是互斥锁或者“逻辑过期”。“互斥锁”就是利用上面的SET NX只让一个请求去数据库重建缓存其他请求等待短时间后重试。“逻辑过期”指的是 key 没有实际的 TTL而是在 value 里存一个过期时间戳这样缓存一直存在重建过程由后台线程异步完成不会阻塞用户请求。缓存雪崩指的是大量 key 在同一时间过期或者 Redis 整体宕机。前者可以通过给 key 的过期时间加随机偏移来缓解后者则需要主从、哨兵、集群架构来保障高可用。5.3 主从复制、哨兵与集群的常用命令热词里出现了“docker 安装 redis 主从”“redis 哨兵模式和集群模式的区别”这说明大家确实被部署架构困扰过。从命令的角度主从复制有一个命令是SLAVEOF新版本叫REPLICAOF它用来指定当前节点从哪个主节点复制数据REPLICAOF 127.0.0.1 6379执行完这条命令当前节点就会变成目标节点的副本开始全量同步主节点的数据。这个命令在生产上可以直接动态修改不需要重启进程。想解除复制关系执行REPLICAOF NO ONE即可此时节点会保留自己的数据并晋升为主节点。哨兵Sentinel是 Redis 的 HA 方案它监控主节点的健康状态主节点挂了以后能在从节点中自动选出一个新的主节点。从客户端角度看连接哨兵和使用普通 Redis 没有本质区别但你查询具体状态时需要知道SENTINEL命令族。比如SENTINEL masters可以列出所有被监控的主节点SENTINEL get-master-addr-by-name mymaster能查到一个主节点当前的地址信息。这里是运维同学最常用命令的集中区。集群模式和哨兵模式的区别就是集群把数据分片到多个主节点上每个主节点又可以有从节点。集群环境下CLUSTER INFO查看集群整体状态CLUSTER NODES查看每个节点信息CLUSTER KEYSLOT key可以查某个 key 属于哪个槽位。理解槽位概念是理解集群的第一步Redis 集群一共有 16384 个槽位key 会通过 CRC16 算法计算出一个值再映射到某个槽位每个主节点负责一部分槽位。6. 高频问题速查与个人体会6.1 环境与连接层问题Windows 安装 Redis 有两种常见路径。一种是下载由 tporadowski 维护的 Windows 移植版可以像普通软件一样安装另一种是 Windows 10/11 自带或手动安装 WSL然后在 Linux 子系统里跑正式版 Redis。从长期维护角度看我更推荐 WSL 方案它既能体验真实 Linux 环境也能避免 Windows 移植版本功能滞后的问题。macOS 用户安装 Redis 通常用 Homebrew但安装后要记住一个细节brew services start redis会注册为后台服务并开机自启进入开发时这可能是一个惊喜也可能是负担如果只想临时跑一下直接用redis-server前台启动即可CTRLC 就能停掉。热词里还有“redis 启动如何加入到 windows 服务中”的问题。在 Windows 的移植版里可以用redis-server --service-install注册为 Windows 服务再通过redis-server --service-start启动。注意注册服务时配置文件的路径要用绝对路径否则服务启动时找不到配置会瞬间退出。6.2 以个人经验做收尾说了这么多命令最后还是要回到工程实践。我在排查 Redis 问题时有一个习惯先info再dbsize然后client list最后再决定是否SCAN。这个顺序能快速确定问题是出现在资源层面内存/连接数、数据规模层面key 数量还是客户端行为层面某客户端异常占用。等你对整个命令表有底之后排错的过程会特别快最讨厌的是拿着KEYS命令在那里扫屏扫半天还以为 Redis 卡了。还有一个经验是给 key 做合理的命名规范。我见过太多生产环境里 key 全是a、b、c这种毫无辨识度的名字出问题的时候人根本不知道哪些 key 是谁的。给 key 加业务前缀例如user:1001:profile、order:20240101:detail一眼就能看出用途排查日志时非常有帮助。如果你刚接触 Redis别急着把每个命令都背一遍。抓住五大数据类型的核心命令再把SET NX PX、SCAN、INFO、BGSAVE这几个生产场景常客练熟就已经能覆盖工作中 90% 的 Redis 操作场景了。剩下的命令等真正遇到对应问题时再去查、去试比死记硬背高效得多。
返回列表