尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Redis基础命令实战:从安装到分布式锁与缓存排障

Redis基础命令实战:从安装到分布式锁与缓存排障 “Redis装好了”和“会用 Redis”之间其实隔着一道墙。很多人第一步卡在环境上第二步卡在只会用set和get第三步就直接被面试题里的缓存穿透、分布式锁砸懵。这篇就专门用来拆这面墙我按实际干活和排障的顺序把 redis 基础常用命令串成一条线装好连上、把 string/hash/list/set/zset 用明白再补齐过期、持久化、锁、监控这些运维必踩的点。不管你是 Windows 下解压完 Redis 不知道怎么弄成服务的新手还是面试前想快速把命令体系过一遍的候选人这份笔记都能直接照着抄。顺便先说下我的使用习惯日常能用命令行解决的事我绝不开 GUI尤其是排查线上问题的时候redis-cli是唯一能让你冷静下来的工具。下面的命令我都基于 Redis 7.x 实测过6.x 也基本通用。1. 装好 Redis 并连上命令行环境这关先过去很多人学 Redis 不是死在命令上而是死在装不完、起不来、连不上这三件事上。我把 Windows、macOS、Linux、Docker 四条路的操作都过一遍你按自己的系统挑一条走就行。1.1 Windows / macOS / Linux 三条安装路径Windows 下官方其实没有原生维护版本社区有移植版可用。解压后目录里会有redis-server.exe和redis-cli.exe前者是服务端后者是命令行客户端。直接双击redis-server.exe就能看到服务跑起来默认监听 6379。想确认是否启动成功另开一个命令行窗口执行redis-cli.exe ping返回PONG就说明通了。这个版本启动后是前台运行的关掉窗口 Redis 就停了后面我会讲怎么注册成 Windows 服务。macOS 最简单的方式是走 Homebrewbrew install redis brew services start redis装完配置文件在/opt/homebrew/etc/redis.confIntel 芯片是/usr/local/etc/redis.conf。如果没有 Homebrew源码编译也不复杂Linux 上也推荐这套wget https://download.redis.io/releases/redis-7.2.4.tar.gz tar xzf redis-7.2.4.tar.gz cd redis-7.2.4 make make installLinux 发行版如果不想编译包管理器一条命令也能装Ubuntu/Debian 用apt install redis-serverCentOS/RHEL 用yum install redis。装完通过systemctl enable --now redis-server或service redis_6379 start启动。这里有个新手中的高频坑启动是成功了但外部机器连不上。优先检查三件事——bind是否只绑定了127.0.0.1protected-mode是否处于yes云服务器安全组/防火墙有没有放行 6379 端口。本地学习无所谓一旦要跨机器访问这三个点必须排查一遍。1.2 Docker 一条命令起服务主从模拟也顺手用 Docker 跑 Redis 是最省心的镜像即装即用版本随意切换docker pull redis:7.2-alpine docker run -d --name redis-local \ -p 6379:6379 \ -v /data/redis:/data \ redis:7.2-alpine \ redis-server --appendonly yes --requirepass yourpassword-v /data/redis:/data是把数据目录挂载到宿主机容器删了数据还在--appendonly yes开启 AOF保证重启不丢数据。这里要注意容器内的 Redis 默认不带配置文件所有参数只能靠命令行传想用配置文件就自己挂载一份进去docker run -d --name redis-local \ -p 6379:6379 \ -v /path/to/redis.conf:/usr/local/etc/redis/redis.conf \ redis:7.2-alpine \ redis-server /usr/local/etc/redis/redis.conf想用 Docker 模拟主从也很快。先起一个主库再起一个从库从库启动时通过--replicaof指定主库地址docker run -d --name redis-master -p 6379:6379 redis:7.2-alpine docker run -d --name redis-slave -p 6380:6379 redis:7.2-alpine \ redis-server --replicaof 172.17.0.2 6379从库的replicaof填的是主库容器的 IPdocker inspect redis-master可以查到。连到从库后执行info replication看到role:slave就说明生效了。我提醒一句容器重启后 IP 往往会变生产环境要玩主从要么固定容器 IP要么把replicaof写进配置文件而不是启动参数。1.3 redis-cli 连上并确认状态Redis 装好只是开始真正干活靠redis-cli。基础连接命令就几个redis-cli -h 127.0.0.1 -p 6379 redis-cli -h 127.0.0.1 -p 6379 -a yourpassword --no-auth-warning设了密码不加-a的话登录后执行命令会报NOAUTH Authentication required。登录成功后先跑两个命令确认状态ping # PONG info server # 看版本号、进程 ID、运行天数、端口等把 Redis 做成后台服务而不是前台进程是所有系统都要解决的事。Windows 上执行redis-server.exe --service-install redis.windows.conf --loglevel notice redis-server.exe --service-start redis-server.exe --service-stop redis-server.exe --service-uninstallLinux 上如果用的是apt/yum安装包systemctl已经帮你处理好了如果是源码编译utils/install_server.sh脚本可以一键注册成 service。macOS 用brew services start redis即可开机自启。这些操作做完Redis 就不会因为你关了个终端就“消失”了。2. 字符串命令Redis 里最常用也最容易用错的类型string是 Redis 最基本的类型也是大多数业务里用得最多的。命令看着简单但想把它们用对、用出效率还是有不少细节。2.1 读写、批量操作和原子写入最基本的set/get不提了直接看实际场景。批量操作mset/mget是我最推荐的一次网络往返搞定多个 key性能提升非常明显mset user:1:name zhangsan user:1:age 18 mget user:1:name user:1:agesetnx是“不存在才设置”这个命令是后面分布式锁的地基setnx lock:order 1setex则是设置时直接带上过期时间setex sms:code:13800138000 60 123456需要注意set命令本身也可以组合过期和NX条件这是更规范的写法set user:1:token abc123 EX 300 NX上面这段的意思是只有当user:1:token不存在时才设置并且 300 秒后过期。setnx加setex能做的它一条命令就搞定了还省了一次网络交互。老项目里常见的setnxexpire两步操作其实有原子性问题——两步之间进程一挂key 就成了永久锁后患无穷。能用一条命令尽量别拆两条。getset、append、strlen这三个命令平时用得少但偶尔能解燃眉之急。getset是“取旧值并写入新值”适合做计数器快照append做追加拼接strlen查字符串长度。字符串命令里我觉得实在不需要死记硬背把set/get/mset/mget/setnx/setex这几个高频命令练熟就够扛绝大多数场景了。2.2 incr/decr 计数命令与“incr 不准”的真相计数是字符串类型最典型的应用。incr、decr、incrby、decrby分别对应自增、自减、指定步长自增自减incr user:1:score incrby user:1:score 10 decr user:1:score网上很多人疑惑“redis incr 不准”这里必须澄清**Redis 是单线程模型incr本身是原子的不存在并发递增丢失的问题。**如果你发现计数和业务对不上问题基本出在业务代码而不是 Redis 本身。常见原因有三类读改写模式先get再set中间被并发覆盖正确做法是直接用incr一个命令完成。混用set随意覆盖计数 key之前递增的结果被清零或者被写回旧值。主从切换或者异步复制产生了少量丢失、短时间内计数不准。我见过一个真实案例用户用set每次请求都把某个计数写回 0结果第二天数据全乱。后来统一改成incr问题立刻消失。这里想提醒的是计数字段要自增就用自增命令不要用 get set 模拟这是新手最容易犯的错。2.3 过期时间为什么 ttl 返回 -1 和 -2字符串是缓存一定绕不开过期。与过期相关的命令有expire、ttl、pttl、persist。expire给已有 key 设置过期时间ttl查剩余秒数。这里有两个返回值经常让人懵ttl key返回-1键存在但没有设置过期时间也就是永久键。ttl key返回-2键不存在或已过期被删除。所以排查缓存问题的时候见到-1就知道是没设过期时间数据结构里漏了expire或是set时没带EX。persist则是移除过期时间把 key 变成永久键。我一般建议所有 key 都设置过期时间哪怕是 7 天或 30 天避免内存里堆满无效数据。ttl命令对于排查“缓存雪崩”特别有用你可以批量看 key 的剩余过期时间分布如果大量 key 同时接近 0就是典型的集中过期风险需要在写入时加随机偏移。3. 四种常用数据类型命令一次讲透面试题里“redis 数据类型”基本是必问项。这个部分我直接讲最常见 4 种类型的核心命令并且每个类型都给出典型业务场景。stream这类新类型也值得了解但日常用到概率没这么高基础阶段先不展开。3.1 Hash对象结构化的正确打开方式Hash 适合存对象比如用户信息、商品详情。用字符串存 JSON 虽然也能用但要改其中某个字段就得整个读出来再写回去Hash 则可以只操作单个字段。核心命令如下hset user:1 username zhangsan age 18 hget user:1 username hgetall user:1 hincrby user:1 age 1 hdel user:1 age hexists user:1 username hlen user:1关键命令说明命令作用注意事项HSET设置字段值一次可以写多个字段HGETALL获取全部字段大对象慎用可能阻塞HINCRBY字段自增适合做对象内计数器HDEL删除字段删除不存在的字段返回 0HEXISTS判断字段是否存在常用于接口幂等判断hgetall是新手最容易踩的坑线上一个大 Hash 有几万甚至几十万个字段一次hgetall会把 Redis 主线程卡住其他命令全部排队。我自己就遇到过用户头像字段放在一个超大 hash 里结果某次批量操作直接导致接口全线超时。生产环境建议用hscan分批拿而不是无脑hgetall。3.2 List消息队列和最新的 N 条List 是双向链表左边推右边推都可以。核心命令lpush mylist a b c # 从左边压入 rpush mylist a b c # 从右边压入 lrange mylist 0 2 # 取下标 0 到 2 的元素注意下标两边都包含 lpop mylist # 从左边弹出 rpop mylist # 从右边弹出 llen mylist # 获取长度 ltrim mylist 0 9 # 只保留 0 到 9其他删掉 blpop mylist 5 # 阻塞弹出最多等 5 秒List 最常见的两个用途是“最新列表”和“轻量队列”。最新列表用lpushltrim配合比如要保留最近 10 条操作记录每次lpush后执行ltrim 0 9老数据自动被裁掉非常简单。轻量队列则用lpush生产、brpop消费# 生产者 lpush task:queue task-1 # 消费者 brpop task:queue 0brpop是阻塞弹出队列为空时一直等待相比直接rpop轮询省 CPU。这个方案特别适合“消息量不大、不想引入 MQ 组件”的内部任务。要注意 List 队列没有消费确认机制消费者挂掉消息可能会丢真需要可靠投递还是要上专业 MQ。使用不当还会留下“长时间不消费导致 list 无限增长把内存打爆”的隐患所以最好配合llen监控长度。3.3 Set去重与交并差的组合拳Set 是无序去重集合常规命令sadd tags:user1 java python go smembers tags:user1 # 所有元素 sismember tags:user1 java # 判断是否存在 srem tags:user1 java # 删除元素 scard tags:user1 # 元素数量 spop tags:user1 # 随机弹出 srandmember tags:user1 # 随机取一个但不移除 sinter tag:a tag:b # 交集 sunion tag:a tag:b # 并集 sdiff tag:a tag:b # 差集Set 的实战场景非常实用。抽奖活动用spop随机弹出中奖用户天然去重、天然随机社交场景用sinter算两个用户的共同关注推荐系统里用sdiff找出“我关注的人里他还未关注的”。很多业务集合适用 Set 但被发现用了 List就会遇到重复数据、去重要在应用层做一遍的困境。记住需要去重、需要集合运算就优先想 Set。有个细节值得记住spop和srandmember虽然都是随机取元素但spop会从集合中移除该元素srandmember不会。抽奖逻辑里如果同一批奖品不能重复中出就要用spop如果是“推荐一个标签”这种纯展示场景用srandmember更合适。3.4 ZSet排行榜的标准答案ZSet 是有序集合每个成员带一个 score分数按分数排序。核心命令zadd ranking 100 user1 99 user2 zrange ranking 0 2 # 从小到大取前3 zrevrange ranking 0 2 # 从大到小取前3 zscore ranking user1 # 查某个成员的分数 zincrby ranking 5 user1 # 为成员加分 zrangebyscore ranking 90 100 # 按分数区间取 zrank ranking user1 # 从小到大排名 zrevrank ranking user1 # 从大到小排名 zcard ranking # 总数 zrem ranking user1 # 删除成员排行榜是 ZSet 最经典的场景没有之一。比如积分排行榜每次用户加积分执行一次zincrby要展示榜单直接zrevrange性能极佳。这里有个细节经常被忽略当多个成员分数相同时ZSet 会按成员名的字典序排列导致实际排名看起来“不对”。比如两个用户都是 100 分谁先到谁排在前面Redis 默认不是按插入时间而是按成员名的字母序。解决方式是把时间信息编码进 score比如实际 score 积分数 * 10000000000 某个基准时间戳 - 当前时间戳。这样积分相同时谁早完成谁排在前面。实现思路我试过多个最通用的是用timestamp做二级排序简单可靠但注意别让分数精度溢出用Long承载在大多数语言里没问题。4. Key 管理、过期淘汰与事务命令缓存治理的基本功很多“会 Redis”的人其实只是会用数据类型一旦涉及 key 的批量治理、过期淘汰策略、事务和分布式锁就露怯了。这章正是缓存治理面试点最密集的地方。4.1 Key 通用操作查找、删除、改名与异步删除通用的 key 命令有keys、scan、exists、del、unlink、type、rename、expire、ttl等。其中keys是新手最爱用、生产却最要命的命令keys user:*keys会遍历整个 keyspace在 key 数量大的情况下严重阻塞 Redis 主线程生产环境基本禁止直接使用。替代方案是scanscan 0 match user:* count 100scan返回两个结果第一个是下一次迭代的游标第二个是本次查到的 key 列表。需要注意scan是分批次遍历每次传回上一次返回的 cursor 直到返回 0。它是非阻塞的所以可以用来在超大 key 数量下慢慢清扫数据。如果你要做清理任务会写成类似“获取一批、处理一批、再获取下一批”的流程。删除 key 时del是同步删除数据量大时会阻塞unlink是异步删除主线程直接返回后台线程慢慢释放内存。线上删除大 key请务必用unlink。我处理过一个几百万条记录的 List一个del直接把 Redis 阻塞了几秒钟改为unlink后瞬间返回。这个坑我认为值得反复给周边同事强调。type命令用来确认 key 的类型排查“WRONGTYPE”错误时必用。rename给 key 改名randomkey随机返回一个 key生产环境慎用也会涉及全量扫描。4.2 过期删除与内存淘汰缓存雪崩、穿透、击穿的命令应对前面讲过期命令时提到ttl这里把整个过期机制串起来。Redis 删除过期 key 采用“惰性删除 定期删除”两种策略读取时发现过期就删掉后台定期抽样删除。这种设计避免了每秒钟全量扫描的 CPU 消耗但也意味着过期 key 不会瞬间被物理删除内存会延迟释放。当 Redis 内存超过maxmemory时触发内存淘汰策略。通过以下命令查看和设置config get maxmemory config set maxmemory 1gb config get maxmemory-policy config set maxmemory-policy allkeys-lrumaxmemory-policy有以下常用取值策略含义适用场景noeviction不淘汰写入报错默认值不推荐生产使用allkeys-lru所有 key 按 LRU 淘汰缓存场景最常用volatile-lru只淘汰设置了过期的 key有些 key 不能丢时用allkeys-lfu按访问频率淘汰访问热点集中的场景volatile-ttl淘汰剩余时间最短的很少用到缓存治理相关的高频问题——穿透、击穿、雪崩也都能用命令层面去缓解。缓存穿透请求的 key 在缓存和数据库里都不存在导致每次请求都打到数据库。解决思路缓存空值并设置短过期比如 60 秒用setnx做互斥保护。缓存击穿热点 key 过期瞬间大量请求打到数据库。解决的命令级做法是用setnx抢锁抢到锁的线程去查库回填缓存其他线程短暂等待。缓存雪崩大量 key 同一时间过期。解决办法是写入时给过期时间加随机值我实际用的就是expire时生成base_time random(0-300)秒。把这三个概念对应到命令你会发现面试官问的不只是概念考的还是你知不知道怎么用一条一条命令去防护。4.3 事务、管道和分布式锁的正确姿势Redis 事务相关的命令是multi、exec、discard、watch、unwatch。multi开启事务之后的命令不会立刻执行而是进入队列exec一次性提交discard取消事务multi set user:1 name zhangsan incr user:1:count exec这里有个容易误解的地方Redis 事务不保证“原子性”里的一致性执行过程中某个命令出错其他命令照样执行。真正的作用是“打包执行、避免被打断”不是传统数据库事务。如果你需要“先检查再更新”的 CAS 需求要用watchwatch user:1:count val get user:1:count multi set user:1:count (val1) execwatch监控 key如果在事务执行前其他客户端修改了这个 keyexec就会失败返回 nil。看门狗机制而已别把它想象成万能的并发方案。另一个命令pipeline可以在客户端一次性发送多条命令减少网络 RTT但要清楚它并不是事务中途某条失败不影响其他命令执行别把两者混为一谈。setnx能实现分布式锁但要注意规范。我给出的标准写法是基于set命令原子完成的set resource:lock unique_value NX PX 30000释放锁时要先检查 value 是不是自己设的再用 Lua 脚本删除不能简单delif redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 end这段脚本的逻辑是只有当前持有者才能释放锁防止误删别人的锁。分布式锁看着简单落地时最容易出问题的就是释放姿势不对、过期时间太短导致业务没执行完锁就没了。这个场景面试高频实际项目里也值得反复推敲。5. 运维必备持久化、安全与连接检查基础命令学完还要具备基本运维能力。Redis 用着用着崩溃了日志在哪看内存快满了怎么救这台机器到底谁连着这些问题下面几条命令就能回答。5.1 持久化命令RDB 与 AOF 怎么选、怎么查Redis 持久化有两种机制RDB快照和 AOF追加日志。两者可以共存重启时优先加载 AOF通常更完整。手动触发相关命令save # 同步生成 RDB阻塞主线程 bgsave # 后台异步生成 RDB推荐 lastsave # 查看最后一次成功保存的时间戳 bgrewriteaof # 后台重写 AOF压缩日志体积save是阻塞式的生产环境不要用日常备份、手动落盘用bgsave。AOF 默认是关闭的需要开启则要改配置appendonly yes。通过命令可以动态查看运行配置config get save # 查看自动 RDB 触发条件 config get appendonly # 查看 AOF 是否开启 config get appendfsync # 查看刷盘策略everysec 是常用折中值得一提的细节是bgsave期间如果有新的写命令Redis 会通过写时复制技术保证快照一致性和新数据不丢所以线上完全可以不加锁直接执行。RDB 适合做灾难恢复备份AOF 适合做崩溃恢复高可靠场景。如果是轻量内部服务我会选择appendonly yes再把appendfsync设为everysec折中安全和性能。5.2 安全配置、日志查看与客户端管理设置密码是上线前的第一件事。Windows 下可以在redis.windows.conf里找到requirepass字段填上明文密码然后重启服务生效运行时临时修改用config set requirepass yourpassword设置后任何新连接都需要先执行auth yourpassword查看哪些客户端连着可以用client list输出会包含地址、连接名、上次交互时间等。要踢掉某个异常连接用client kill 地址:端口。同时可以用config get maxclients查看最大连接数连接数打满时报错信息类似 “max number of clients reached”。日志相关命令集中在config get logfile和config get loglevelconfig get logfile config get loglevel默认logfile为空表示输出到标准输出Windows 下可能是控制台窗口。生产环境建议设置一个明确路径方便排障。里面有loglevel四个级别debug、verbose、notice、warning线上不要开 debug日志量会很恐怖。调试利器monitor命令可以直接看到 Redis 收到的每一条命令排查线上诡异问题时非常直观。但必须提醒monitor对性能影响不小只适合短时间排查别开着不管。慢查询命令也建议掌握slowlog get 10 slowlog len它会列出一段时间内执行时间超过阈值的命令。看到慢查询日志优先怀疑keys、hgetall、smembers这类全量操作和大 key 操作。5.3 可视化工具连接与序列化问题虽然我平时命令行为主但给同事演示数据、快速浏览类型时用可视化管理工具也比较高效。常用的客户端有 Redis Desktop Manager 和 Another Redis Desktop Manager配置连接时无非填三样host、port、password。如果连接不上对照排查密码不对报NOAUTH Authentication required。绑定了 127.0.0.1外部访问被拒。防火墙没放行像是本地 Docker 映射了-p 6379:6379但宿主机防火墙拦截了端口。Docker 容器之间互联注意容器 IP 和端口。工具连接之后看到一堆\xAC\xED\x00\x05t...这种乱码其实是序列化问题。默认某些客户端库保存对象用的是 Java 原生序列化数据不可读、体积还大。优化思路是改用 JSON 或更紧凑的 Protobuf/MessagePack 序列化不仅可视化工具看着舒服存储内存也能降不少。我习惯在工程项目里统一配置 JSON 序列化这样 Redis 里存的字符串直接能看懂排查问题心不累在做数据迁移和离线分析时也方便很多。6. 高频报错与排查实录遇到问题别再瞎猜最后这章我把工作中高频遇到报错和排查经验整理成速查表顺手回答几个面试必考题背后的命令依据。6.1 常见错误码速查报错信息含义解决思路NOAUTH Authentication required需要密码认证执行auth 密码或连接时加-aWRONGTYPE Operation against a key holding the wrong kind of value操作类型和 key 实际类型不匹配用type key确认类型ERR value is not an integer or out of rangeincr/decr操作了非整数用get key查看当前值OOM command not allowed when used memory maxmemory内存达到上限且策略是 noeviction调整maxmemory-policy或扩容READONLY You cant write against a read only replica当前连的是从库从库只读写入请求切换到主库MOVED/ASK集群模式下 key 不在当前节点用redis-cli -c自动重定向max number of clients reached客户端连接数打满优化连接池、调大maxclients每个错误背后都有一个业务故事。比如MOVED是初学者连集群时最常见的错误在不带-c参数的情况下连上集群节点访问不归本节点管的 key 就会报MOVED带上-c参数后客户端会自己处理重定向你才会感觉“集群好像没集群”。6.2 哨兵与集群命令端看到的关键差异“哨兵模式”和“集群模式”的区别也是经典面试题。从命令角度看很直观哨兵模式部署多个 Sentinel 进程监控主从节点主节点挂了自动把某个从节点提升为主节点。客户端连接的是 Sentinel 端口比如 26379执行sentinel get-master-addr-by-name mymaster获取当前真实主节点的地址。它的目标是高可用不解决数据分片。集群模式数据自动分片到多个主节点每个主节点带若干个从节点。客户端访问某个 key 时如果节点不对会收到MOVED错误redis-cli -c会自动跟随重定向。通过cluster info和cluster nodes查看拓扑。区分清楚了哨兵不改写数据分片集群才是分布式数据方案。面试时建议从“高可用 vs 水平扩展”这个核心差异去展开再通过info replication和cluster nodes的输出佐证比背书管用。6.3 一页纸速查最常用的基础命令表最后给一张浓缩版速查表适合贴在手边分类命令用途连接redis-cli -h host -p port -a pwd连接服务端通用ping/dbsize/info确认存活、数据量、服务详情字符串set/get/mset/mget/setnx/setex/incr/decr缓存、计数器、验证码哈希hset/hget/hgetall/hdel/hincrby对象存储列表lpush/rpush/lpop/rpop/lrange/ltrim/blpop队列、最新列表集合sadd/srem/smembers/sismember/sinter/sunio/sdiff去重、抽奖、共同关注有序集合zadd/zrange/zrevrange/zincrby/zrangebyscore排行榜、优先级队列key 管理scan/exists/type/del/unlink/expire/ttl查删、过期控制事务multi/exec/discard/watch打包执行、CAS持久化save/bgsave/lastsave/bgrewriteaofRDB/AOF 手动操作运维config/client list/slowlog/monitor配置查询、日志、慢查询这张表基本覆盖了 redis 基础常用命令 90% 的日常需求。剩下 10%等你真的遇到具体场景再去查文档也不迟关键是你得先知道“有这样一个命令能解决这件事”。我个人实操中最深刻的体会是Redis 的命令简单到一眼能懂但真正区分水平的是在什么场景选什么命令、怎么规避阻塞和丢数据风险。可视化工具适合看数据出问题的时候还是回到redis-cli一行行敲才踏实。你把上面的命令在本地跑一遍再对照速查表过一遍不出一个星期面试和上手业务都会稳很多。
返回列表