
Redis的命令看起来多真要分门别类记下来常用到的基本不超过三十条。搞后端这些年我给不少新人讲过Redis最深的感受是大多数人学命令的方式不对一上来就背命令行背完就忘。这篇我打算换个思路从自己平时实际使用、排查、面试的角度把redis基础常用命令重新捋一遍从环境安装、连接自检到五大数据类型怎么推导命令再到过期持久化、线上排查、分布式锁这些实操场景。适合刚接触Redis的入门读者也适合用了一段时间但命令体系还比较乱的同学。1. 装完Redis先别急着敲命令环境准备和连接自检1.1 Windowszip解压版比安装版好用在哪里很多人在Windows上装Redis第一反应是去官网找exe安装包。实际上Redis官方并不提供Windows版本大家常用的Windows版是开源社区维护的移植版。我的建议是直接用zip解压版别折腾exe安装版。操作不复杂下载zip压缩包后解压到某个目录比如D:\redis目录下就能看到redis-server.exe和redis-cli.exe这两个关键文件。启动服务就直接执行redis-server.exe默认监听6379端口跑起来就完事。新开一个终端窗口用redis-cli.exe连上去redis-cli.exe -h 127.0.0.1 -p 6379能看到127.0.0.1:6379这个提示符说明连接成功。这里有个经验测试环境我一般直接redis-server.exe redis.windows.conf指定配置文件启动生产环境则很少用Windows跑Redis基本都是Linux服务器或者Docker容器Windows上更多是本地开发调试用。1.2 Linux和macOS一行命令装好Linux上主流发行版用包管理器装很快# Debian/Ubuntu系 apt install redis-server -y # CentOS/RHEL系 yum install redis -ymacOS用户推荐用Homebrewbrew install redis装完以后先用redis-server --version看下版本确认装的是不是太老的版本。我遇到过一些老服务器上自带的Redis版本还是3.x很多新命令和特性用不了这时候建议直接升级或者用Docker跑新版本。1.3 Docker一条命令拉起一个干净实例现在我最推荐的是Docker方式尤其是想快速起一个隔离环境做测试的时候docker run -d --name myredis -p 6379:6379 redis:7要带密码就加个启动参数docker run -d --name myredis -p 6379:6379 redis:7 redis-server --requirepass 123456连容器里的redis-clidocker exec -it myredis redis-cli -a 123456为什么推荐Docker因为它把配置、数据、版本都隔离得干干净净删了重建也就几秒钟。网上那些问docker安装redis主从的说到底也是基于同一套思路先起多个容器再设置主从关系后面配置一个replicaof就行。1.4 连接后的三个自检命令连上Redis之后先别急着敲业务命令用三条命令确认环境健康# 1. 服务存活 ping # 返回 PONG # 2. 服务基础信息 info server # 能看到 redis_version、运行端口、进程ID等 # 3. 当前库有多少key dbsizeping返回PONG说明服务正常info server让你确认版本和配置而不是靠猜dbsize是看当前库是否真的有数据如果是空库后面排查问题时要先想到是不是压根没写入这个方向。这三条命令加起来花不了两秒钟但能给后面的操作定个基准线。2. 命令这么多先抓住五大数据类型这根主线2.1 五大数据类型到底差在哪Redis基础常用命令看着多根源就在于它有五种数据类型。类型不同命令的前缀就不同。先把类型差异记清楚命令表基本就记住一大半了。这个道理就像家里有五个抽屉分别放钥匙、杂物、文件、票据、卡片你要拿东西之前脑子里先反应的是从哪个抽屉拿而不是记每个物品的精确位置。Redis的类型就是这些抽屉。数据类型底层存储结构最大元素数量典型场景String二进制安全的字符串/整数512MB个值缓存、计数器、分布式锁Hash字段-值映射表2^32 - 1 个字段对象存储、购物车List双向链表2^32 - 1 个元素消息队列、时间线Set无序不重复集合2^32 - 1 个成员去重、共同好友ZSet带分数的有序集合跳表哈希表2^32 - 1 个成员排行榜、限流权重2.2 从数据类型推导出命令的关键规律五大数据类型对应的命令前缀非常有规律String对应s开头的操作set、getHash对应h开头hset、hgetList对应l开头lpush、lpopSet对应s开头sadd、sremZSet对应z开头zadd、zrange。这里有个小坑Set和ZSet都是s系列但ZSet固定是z开头看到zadd就知道是带排序的集合看到sadd就是普通集合。String也是s开头不过String的命令一般更多是set/get这种两两配对和集合的sadd/srem不冲突多敲几次就能自然分开。2.3 一个实用记忆框架先想业务再想命令我给新人的建议是不要按字母序去背命令要按场景去问自己这几个问题存储一个简单值用String。存储一个对象/字典用Hash。存储有顺序的列表用List。存储不重复的集合用Set。存储需要按分数排序的集合用ZSet。想清楚数据类型后再动手查这个类型下的具体命令比如插入是lpush还是rpush读取是lrange还是zrange全部有迹可循。这就是我记Redis命令的核心方法后面所有具体命令都建立在这个框架上。3. 通用命令不管存什么都绕不开的那几条3.1 keys的坑和scan的正确姿势新手最容易上来就敲keys *想看看库里有啥。公司环境里千万别这么干keys *在key数量大时会阻塞整个Redis服务因为它是全量扫描。线上Redis阻塞几秒钟是什么后果做过高并发接口的人都懂。正确做法是scan它是游标式迭代每次返回一批key和下一个游标位置# 从游标0开始每次最多返回20个key scan 0 match user:* count 20 # 返回结果里第一部分是下一次的游标第二部分才是key列表scan的核心特点是分批返回不会一口气把所有key都翻一遍所以对服务影响小。实际排查问题的时候我更倾向于直接用scan 0 match user:*这种带pattern的方式也比keys user:*放心得多。3.2 生命周期四件套exists、del、expire、ttl这四个命令是排查问题的老搭档exists key # 1 存在 / 0 不存在 del key1 key2 # 删除成功后返回删除数量 expire key 300 # 设置key 300秒后过期 ttl key # 查看剩余过期时间-1表示永久-2表示key已不存在ttl返回-2和返回-1经常让新人懵。记住-1是key还在但没有过期时间-2是key根本没找到。排查问题时这俩差别很大一个是这个key不会自动消失一个是这个key压根没写进去。再补一个很实用的set命令本身就能带过期时间比如set token abc123 ex 7200。很多人不知道这个简化写法习惯用set再接着expire其实一步就能完成还能避免两条命令之间服务崩溃导致过期时间没设置成功的隐患。3.3 确认类型的type和object encoding不知道某个key是什么类型不要瞎猜直接敲type key # string / hash / list / set / zsettype只回答数据类型本身想知道更多内部编码方式用object encoding key它会告诉你这个key底层到底用了什么编码比如string类型的key有的用int编码有的用embstr有的用raw。这个命令在排查内存问题时很有用能帮你看清楚是不是某些key因为过大而转换了编码导致内存翻倍。3.4 flushdb和flushall这种危险命令要心里有数flushdb清空当前库所有keyflushall清空所有库。这俩命令一旦执行数据直接没了。我在测试环境用它们清理数据没问题但在生产环境除非你心里很清楚马上会发生什么否则别碰。真要用至少给它们加个safe意识先搞清楚自己连的是哪台机器哪个库用info keyspace确认一下别连错了再执行。有些团队会在配置里直接禁用flushdb和flushall这也是行业里很常见的做法。4. String和Hash项目里最频繁的两类命令4.1 set/get/mset/mget别忽略批量版本String最基础的一组命令set user:name zhangsan get user:name但如果要操作多个key我更推荐批量命令mset user:name zhangsan user:age 30 user:city beijing mget user:name user:age user:city # 返回 zhangsan 30 beijing为什么推荐批量Redis是单线程模型每条命令都有网络往返开销。批量操作把多次网络往返合并成一次性能提升非常明显。举个例子循环100次get和1次mget后者的耗时可能只有前者的十分之一。这个优化在接口性能瓶颈排查时经常用到。set命令本身的参数也要掌握特别是这两组# NX: 仅当key不存在时设置成功 set coupon:2024001 used nx # EX: 设置过期时间单位秒 set session:token abc123 ex 1800nx和ex组合使用就是后面讲分布式锁的基础。4.2 计数器incr/decr和过期时间的组合String的整数值可以直接做自增自减incr article:read:1001 decr article:read:1001 incrby article:read:1001 10 decrby article:read:1001 5这个设计太常用了。点赞数、播放量、库存扣减全部可以用一个incr解决。注意两点incr对一个不存在的key会自动从0开始所以不需要先初始化但incr只能操作能被解析为整数的字符串如果你存了abc再去incr会报value is not an integer or out of range错误。计数器经常跟过期时间搭配比如每篇文章的日阅读量可以用expire设置当天结束自动清零第二天重新计数省去定时任务的麻烦。4.3 setnx分布式锁最初的样子setnx key value的全称是SET if Not eXists只有key不存在时才能设置成功这个特性天然适合做互斥锁setnx lock:order uuid-1234 # 返回1拿到锁返回0没拿到锁但这里有个经典坑早期分布式锁的实现都是setnx加expire两条命令如果setnx成功后还没来得及设过期时间服务就挂了锁永远不会释放后面所有请求全部卡死。所以现在正确的姿势是用一条命令完成加锁和续期这个放到第8章详细说。4.4 Hash操作对象缓存的最佳选择Hash适合存对象一个key对应一个对象的多个字段hset user:1001 name lisi age 28 city shanghai hget user:1001 name # 返回 lisi hmget user:1001 name age hgetall user:1001 hdel user:1001 city hexists user:1001 name hincrby user:1001 score 50为什么对象缓存用Hash不用String拼接比如把整个用户对象序列化成一个大JSON存到String里每次只改其中某个字段就得整个JSON读出来反序列化再写回去浪费资源。用Hash改年龄就hset user:1001 age 29改积分就hincrby user:1001 score 10简单直接性能也好得多。实际项目中我很常用hgetall但要注意字段特别多且频繁访问时hgetall返回的数据量不小可能出现大key问题。线上如果哈希很大要么只取需要的字段用hmget要么把hash拆成多个小key。5. List、Set、ZSet按业务场景去记命令最省脑力5.1 List就是消息队列和最新列表List底层是双向链表左进右出、右进左出都很顺手# 左端写入、右端读取 队列 lpush queue:task task-a task-b task-c rpop queue:task # 左端写入、左端读取 栈 lpush stack:op undo1 lpop stack:op做队列时有个注意点如果列表空了你还在rpop返回的是nil不是说错了是表示当前没数据。高并发环境下很多人会写个循环去rpop空列表白白消耗CPU更合理的做法是用brpop阻塞读取列表一旦有数据就立刻返回brpop queue:task 0 # 0表示永久等待这个差异在消息队列场景里很关键。List还有几个常配组合llen查看队列长度lrange key 0 -1取全部元素ltrim key 0 99只保留前100个实现最近100条记录这类功能非常合适。5.2 Set负责去重和集合关系运算Set的命令套路很清晰主要分两类# 基本增删查 sadd user:tags java redis mysql srem user:tags mysql smembers user:tags sismember user:tags redis scard user:tags# 集合关系运算 sinter key1 key2 # 交集 sunion key1 key2 # 并集 sdiff key1 key2 # 差集集合运算在业务里非常常用比如共同关注好友就是用sinter算两个关注列表的交集推荐给A但B已经关注过的人用sdiff。抽奖系统里用sadd把每个用户ID塞进集合中奖时用spop随机弹出一个天然去重还不用自己写随机逻辑。5.3 ZSet是排行榜的唯一选择ZSet Set 分数排序。每个成员都带一个scoreRedis会根据score自动排序zadd rank:game 1000 player_1 zadd rank:game 950 player_2 zadd rank:game 1200 player_3 # 按分数升序取成员 zrange rank:game 0 -1 withscores # 按分数降序取成员排行榜常用 zrevrange rank:game 0 2 withscores # 查看某个成员分数 zscore rank:game player_1 # 增加分数 zincrby rank:game 50 player_1排行榜分页也方便zrevrange rank:game 0 9 withscores就是取前10名zrevrange rank:game 10 19 withscores就是取第11到20名。ZSet还有一个很少被提到但很好用的点zrangebyscore按分数范围取成员适合做时间线或者按分数筛选的排名。比如订单金额排行里筛选出分数在1000到5000之间的所有会员一个命令就查出来MySQL可能还得写一堆条件。5.4 三种结构在缓存里的典型用法搞缓存架构的时候这三种类型的发挥空间特别大。List适合存时序型数据比如用户的操作日志、消息通知列表Set适合存标签、白名单、关注关系ZSet适合存带有权重或时间戳的数据比如热门文章按浏览量排名、直播间活跃用户按在线时长排序。我之前做过一个推荐系统用户看了哪些视频用Set存视频热度用ZSet按播放量排序用户最近观看记录用List存最近50条。查询推荐结果时zrevrange拿热门榜再sdiff掉用户已看过的一个请求就能组装出推荐列表这套方案比频繁查库轻太多。6. 数据安全相关命令过期、持久化和恢复6.1 key过期机制主动过期和惰性过期Redis的过期删除策略是两种结合惰性删除 定期主动删除。惰性删除指每次访问key时检查是否过期过期才删除主动删除指周期性地从过期字典里抽样删除一部分已过期的key。这两种机制组合下来保证了过期key不会一直占着内存但也不会实时消失。这就解释了一个现象ttl key明明已经过期了但dbsize里它还在内存也没立刻释放。别慌等下一轮主动删除或者下次访问这个key时它就会消失。理解这个机制排查内存问题时才不会误判。6.2 save、bgsave和RDB快照Redis默认启用了RDB持久化把某个时间点的数据全量快照到磁盘上的dump.rdb文件。手动触发有两种方式# 阻塞式保存不推荐生产环境用 save # 后台保存fork子进程去写快照 bgsavesave会阻塞Redis主线程数据量大时服务直接卡住生产环境千万别手动执行。bgsave在Redis里很常见执行后可以用lastsave查看最近一次成功保存的时间戳lastsaveRedis还可以配置自动快照比如save 900 1表示900秒内至少有1次写入就执行bgsave。RDB的优点是恢复速度快缺点是两次快照之间的数据可能丢失。理解这个特性你就知道为什么有些场景必须依赖AOF了。6.3 AOF持久化和bgrewriteaofAOFAppend Only File记录的是每一条写命令本身类似MySQL的binlog。Redis重启时把这些命令重放一遍就能最大化地恢复数据。相关配置和命令# 开启AOF同时设置同步策略 appendonly yes # 同步策略可以选 everysec / always / no常见用 everysec # 手动触发AOF文件重写 bgrewriteaofAOF文件会随着写入不断增加所以需要重写压缩bgrewriteaof就是干这个的。它会把内存中的状态重新生成一份最小的命令集合写进新文件避免AOF无限膨胀。实际经验是不要让AOF文件长得太大才去重写可以配auto-aof-rewrite-percentage和auto-aof-rewrite-min-size自动触发。持久化方式恢复速度数据丢失风险文件大小RDB快两次快照间可能丢小二进制压缩AOF慢一些最多丢一秒everysec大文本日志6.4 我自己验证持久化恢复的一次实操很多教程讲持久化讲得很玄其实验证起来很简单。我当时的步骤是先启动一个开了AOF的Redis实例写入几个key然后用kill -9直接杀掉进程模拟断电再重新启动Redis看数据还在不在。流程是这样的redis-cli set user:1001 zhangsan set user:1002 lisi # 强行杀掉进程 kill -9 PID # 重新启动Redis redis-server redis.conf # 再查数据 redis-cli get user:1001 # zhangsan如果AOF配置正确重启后数据能恢复如果只开了RDB且最后一次快照发生在写入之前数据就丢了。这种实操成本很低建议大家都亲手试一次比背十遍配置都记得牢。7. 线上运维必须会看的命令info、slowlog和client7.1 info输出怎么看内存、连接数、命中率info命令可以说是Redis的体检报告直接执行info会输出一大段分段信息常见分段有server、clients、memory、persistence、stats、replication、keyspace。我排查问题时重点看这几个指标# 内存 info memory # 注意看 used_memory_human 和 maxmemory # 客户端连接 info clients # connected_clients 要是异常高就要查是不是有连接泄漏 # 命中率 info stats # keyspace_hits 和 keyspace_misses两者一起看命中率的计算是hits / (hits misses)这个值低于90%就要审视缓存策略设计是否合理。很多团队报缓存命中率低我第一件事都是让他们先跑一下info stats命中率数据一目了然。7.2 config命令运行中修改参数的正确姿势config命令可以在不重启Redis的情况下查看和修改运行参数config get maxmemory config set maxmemory 512mb config get appendonlyconfig set适合临时调整比如内存告警时先紧急调大maxmemory。但要注意并非所有参数都支持config set热修改部分参数改了也不会立刻生效或者根本不支持。调整完之后建议执行config rewrite这个命令会把当前运行配置写回配置文件保证Redis重启后新配置依然生效。否则等进程一重启你又回到旧配置之前的操作等于白做。7.3 slowlog定位慢命令Redis是单线程的一旦某个命令执行很慢后面的请求全得排队。定位慢命令用slowlog# 查看最近的10条慢命令 slowlog get 10 # 慢命令的数量 slowlog len # 清空慢日志 slowlog reset慢命令的阈值由slowlog-log-slower-than配置默认是10000微秒执行超过10毫秒的命令就会被记录。生产环境建议调低一点比如5000微秒5毫秒更敏感地发现问题。拿到慢命令后常见元凶是keys *、hgetall大hash、smembers大set解决方案一般是改用scan分批查或把大key拆分。7.4 client list和monitor的排查作用连接数异常、有客户端卡死时用client list看所有客户端连接client list # 会显示每个连接的地址、状态、执行的命令等发现问题连接可以用client kill ip:port杀掉。monitor命令更厉害它会把Redis收到每条命令实时打印出来相当于把服务内部的操作全部暴露给你看。但正因如此生产环境在高流量下跑monitor会极大消耗性能我只在问题复现场景临时开一小会儿用完立刻关闭。这里顺带说一句很多人问Redis端口通不通怎么看如果不想开客户端工具直接telnet ip 6379试试能通则说明端口通。或者redis-cli -h ip -p 6379 ping返回PONG就说明服务可达。这个排查思路和client list结合起来能快速区分是网络问题还是服务端连接数问题。8. 面试和实战常考的命令组合分布式锁、缓存模式和限流8.1 分布式锁从setnxexpire到set nx px分布式锁几乎是Redis面试必问的点也是实际开发里绕不开的场景。前面提过setnx加expire的经典写法有锁永不释放的隐患。现在正规做法是用一条命令同时完成加锁和过期set lock:order uuid-1234 nx ex 30 # 返回OK表示加锁成功返回nil表示加锁失败加锁没问题了释放锁也要注意不能直接del因为你可能把自己的锁删掉了别人的业务执行超过30秒锁自动过期别人拿到新锁这时你执行del会把别人的锁删掉。释放锁要用Lua脚本验证value是不是自己的再删除if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 end这套组合理解透了面试时可以顺手点出锁的过期时间要大于业务最大执行时间避免业务没跑完锁就失效这个细节比只背命令强得多。8.2 缓存穿透、击穿、雪崩对应的命令姿势这三个缓存的经典问题本质都靠命令组合来缓解缓存穿透查询一个一定不存在的key每次都打到DB。解决姿势是缓存空值用set key ex 300给空结果也加缓存。缓存击穿某个热点key过期瞬间大量请求打到DB。解决姿势是互斥锁用set lock:key 1 nx ex 10抢到锁的线程去查DB回填缓存没抢到的先等一会儿再查缓存。缓存雪崩大量key同一时间过期。解决姿势是过期时间加随机值比如expire key 3600 random(0,300)让过期时间错开。这些方案每个都建立在基础命令之上没有额外的中间件。8.3 用increxpire做简单限流限流是另一个高频场景用Redis实现最简版本只需要两条命令# 每来一个请求就自增 incr api:limit:user:1001 # 如果是第一次请求设置过期时间固定窗口 expire api:limit:user:1001 60逻辑就是同一个用户ID在60秒内incr返回的值超过阈值就拒绝请求。这个方案简单归简单但它是个固定窗口限流临界点可能出现双倍流量比如第59秒和第61秒交界处各放行一批。真要做的严谨可以用ZSet的zremrangebyscore和zrangebyscore记录时间戳实现滑动窗口命令复杂度会高一些但对多数业务场景固定窗口的increxpire已经够用。9. 安全相关的最后一块auth密码和危险命令防护9.1 设置密码与auth命令Redis默认没有密码任何能连到6379端口的人都能操作你的数据这非常危险。设置密码有两种方式# 方式一配置文件里写 requirepass requirepass yourpassword # 方式二运行时设置 config set requirepass yourpassword设置了密码后每次连接都需要认证redis-cli -a yourpassword # 或者在交互窗口里 auth yourpassword这里有个我踩过的坑config set requirepass之后当前连接不会立刻退出而是下次执行命令时才发现没认证。如果你正在排查生产环境改完密码要记着重新认证不然排查途中所有命令突然报NOAUTH容易吓一跳。9.2 重命名或禁用危险命令生产环境的Redis安全防护除了密码还建议在配置里把危险命令改名或禁用。比如flushall、flushdb这类可能清空数据的命令keys这类可能阻塞服务的命令以及config这类可能篡改配置的命令rename-command flushall rename-command flushdb rename-command keys deny_keys rename-command config deny_config注意rename-command不能同时设置成空字符串和同名配置混淆实际用的团队要么直接禁用要么改成只有运维知道的自定义名字例如echo测试命令保留flushall禁用。另外Redis 6.0以后引入了ACLAccess Control List可以给不同用户分配不同权限比全局一个密码更精细比如只让某些客户端读、不许写有需要可以进一步了解。最后分享一个我自己的习惯平时用redis-cli交互绝大多数时间反复敲的其实就set、get、expire、ttl、incr、hgetall、zrange那么十来个命令。Redis命令体系看起来庞大但落到日常开发和排查核心路径很短。这篇是按我自己实际使用顺序整理的装好Redis之后建议你也照着这个顺序在本地跑一遍遇到需要深挖的命令再用command info 命令名去查官方语义。命令这东西用得多了自然就熟了关键是第一步先把数据类型这条主线建立起来。