先聊个面试场景。简历上写着“熟悉Redis”面试官多半会从“数据类型有哪些”开始问然后一路追到过期策略、缓存穿透、持久化。如果连最基础的命令都讲不出细节那后面全是空谈。Redis这个工具表面看就是个能存东西的KV软件核心就俩操作存进去、取出来。可真把它用好要从数据类型、过期机制、持久化、连接配置一路说到业务落地每一步都有讲究。这篇内容面向刚接触Redis的开发者也适合那些被“连接超时”“数据序列化之后读出来是一堆乱码”这类问题卡住的人。我会从安装部署开始把五大核心数据类型、通用命令、业务里的常见用法和连接排查全部过一遍全程用命令行实操说话不扯虚的。看完你至少能完成从“会用redis-cli”到“在项目里正确用Redis做缓存”的跨越。1. 先买票再上车Redis从哪里来、装在哪里1.1 Redis到底是什么一个住在内存里的字典Redis最准确的定义是“内存中的数据结构服务器”。它和MySQL这类磁盘数据库最大的区别在于数据主要存放在内存里读写走内存而不是硬盘因此单实例QPS可以轻松跑到十万级别。把Redis想象成一个全局的、线程安全的字典你往里面放了一个键值对所有连接同一个Redis服务的程序都能立刻读到他。这里有个容易被新手忽略的点Redis虽然快但不是万能数据库。它的定位是“加速层”和“粘合层”不是“核心存储层”。内存价格比磁盘贵得多你不可能把所有业务数据都塞进去。合理的做法是只把热点数据、短期有效的数据、需要跨服务共享的中间状态放进Redis。它解决的核心问题只有一个——延迟。数据库查询走磁盘动不动几十毫秒Redis读内存本地测试通常一毫秒以内。用空间换时间用内存换速度这就是Redis存在的最大意义。1.2 安装Redis的三种方式Windows、Linux、Docker很多人踩的第一个坑就是Windows上装Redis。官方不提供Windows版本的Redis但开发时需要本地调试绕不开怎么办常见三种路径使用微软归档的开源Windows移植版本旧不推荐版本低。使用Memurai这类兼容Redis的Windows服务商业授权个人开发还凑合。最推荐的方式用Docker跑一个Redis容器。Linux下安装很简单以Debian/Ubuntu为例sudo apt update sudo apt install redis-server sudo systemctl start redis-server sudo systemctl enable redis-serverCentOS系的用dnf或者yum包名一般是redis装完启动之后验证一下redis-cli ping # 返回 PONG 说明服务正常Docker方式是最省心的一条命令搞定docker run -d \ --name redis \ -p 6379:6379 \ -v /myredis/data:/data \ --restartalways \ redis:7.0这条命令里-v把容器内的数据目录挂载到宿主机防止容器一删数据全丢--restartalways保证服务器重启后容器自动拉起。需要密码就加一个--requirepass yourpassword。很多教程推荐用-it交互模式跑这在实验环境没问题但生产上不要这么干容器应该后台运行依赖常驻守护和自动重启。1.3 启动、连接与基础配置检查Redis装好之后先别急着写代码用命令行试一把。redis-cli # 如果配了密码 redis-cli -a yourpassword 127.0.0.1:6379 ping PONG刚上手的时候我特别建议先把redis.conf里这几个参数看一遍再动手daemonize yesLinux下后台运行。port 6379监听端口默认6379。bind 127.0.0.1默认只允许本机访问。想允许局域网访问改成0.0.0.0或者指定网卡IP。requirepass访问密码。生产环境必须配不配等于把数据裸奔在公网上。maxmemory最大内存。不设置的话Redis会一直吃内存直到系统内存耗尽。logfile日志路径。排查问题全靠它。这几个参数改完记得重启服务。最常见的连接失败问题十有八九就是bind没放开、密码没配对或者防火墙挡了端口后面的排查章节我会专门展开。2. 绕不开的五大核心数据类型与命令操作2.1 String最简单的KV但家家户户都在用String是Redis里最基础的数据类型底层是动态字符串不仅支持纯字符串还支持数值自增自减。常用的命令就这么几个# 存一个键值对 SET user:name zhangsan # 取一个键的值 GET user:name # 批量设置 MSET user:id 10001 user:name zhangsan # 批量获取 MGET user:id user:name # 数值自增自减用于计数器 SET counter 10 INCR counter DECR counter INCRBY counter 5 # 设置过期时间为 300 秒 SETEX user:token abc123 300String最典型的使用场景缓存。比如把用户基本信息以JSON字符串形式塞进Redis查询时先读缓存缓存没有再去数据库。2.2 List双向链表天然适合做队列List底层是双向链表可以在头部和尾部同时push/pop操作方向灵活。核心命令# 右侧推入两项尾部追加 RPUSH task:queue job1 job2 # 左侧弹出从头部消费 LPOP task:queue # 查看范围内元素 LRANGE task:queue 0 -1 # 获取长度 LLEN task:queue # 阻塞式弹出没有数据时最多等10秒 BLPOP task:queue 10List最常见的用途就是“简易消息队列”。服务端发布任务多worker客户端同时从Redis里抢任务用BLPOP阻塞获取没有任务时客户端就挂起等待不空转占CPU。新手容易犯的错是把List当成数组来取随机索引。LINDEX虽然能取指定下标的元素但List是链表结构随机访问是O(N)操作数据一多效率就崩。需要按下标快速访问的场景应该用后面的Sorted Set或者直接存Hash。2.3 Hash描述一个对象的最佳姿势Hash类型类似Java里的MapString, String适合存一个对象的所有字段。比如一个用户有id、name、age、gender与其把整个对象拼成一个JSON字符串放进String不如拆开存进Hash改一个字段只更新一个字段省流量又好维护。常用命令# 设置单个字段 HSET user:info:10001 name zhangsan HSET user:info:10001 age 25 # 批量设置 HMSET user:info:10001 name zhangsan age 25 gender M # 取单字段 HGET user:info:10001 name # 取所有字段 HGETALL user:info:10001 # 字段数值自增 HINCRBY user:info:10001 score 10 # 删除字段 HDEL user:info:10001 gender我自己的习惯是一看到“某个对象的多个属性且属性会单独变更”就直接选Hash典型场景有购物车、用户资料、订单状态。要注意HGETALL在小规模数据时很方便但字段特别多的对象频繁全量获取会浪费带宽性能要求高时就该改用HGET按需取字段。2.4 Set无序去重集合关系运算利器Set是无序不重复集合底层是哈希表因此查询和去重都非常快。最有价值的是交集、并集、差集运算# 添加元素 SADD user:tags:10001 java redis mysql # 查看所有元素 SMEMBERS user:tags:10001 # 判断是否存在 SISMEMBER user:tags:10001 java # 移除元素 SREM user:tags:10001 mysql # 交集两个人共同关注的标签 SINTER user:tags:10001 user:tags:10002 # 并集 SUNION user:tags:10001 user:tags:10002 # 差集A有B没有 SDIFF user:tags:10001 user:tags:10002Set的经典场景很多抽奖去重、已读用户记录、好友列表交集推荐等。用Set做去重的时候要小心一个习惯数据量不大时无所谓但Set底层维护哈希表有固定内存开销存海量小字符串时不一定是内存最优解这个后面在内存治理部分再详细说。2.5 ZSet带分数的排序集合排行榜就靠它ZSet和Set的区别在于每个元素多了一个double类型的分数scoreRedis按score对元素排序分数相同则按字典序排列。这是实现排行榜最省心的数据结构也是面试官最喜欢追问“排行榜怎么实现”的标准答案。# 添加成员和分数 ZADD ranking 100 player1 ZADD ranking 90 player2 # 按分数升序取全部 ZRANGE ranking 0 -1 # 按分数降序取前10名 ZREVRANGE ranking 0 9 # 查询某个成员当前分数 ZSCORE ranking player1 # 给成员加分 ZINCRBY ranking 5 player1 # 查询某个成员的排名从0开始 ZRANK ranking player1除了排行榜ZSet还可以做延时队列——把任务执行时间戳作为score用ZRANGEBYSCORE取到期任务也可以做滑动窗口限流——用当前时间戳作为score窗口内计数。ZSet有个坑ZADD命令在元素已存在时只更新score不会重复加成员但如果你用的是ZADD ... XX或者NX变体行为又不同一定要确认清楚自己用的Redis版本对命令的语义是什么。2.6 类型选型速查表数据类型底层结构适合场景典型业务例子String动态字符串缓存、计数、分布式ID用户token、访问次数List双向链表消息队列、时间轴待处理任务、最新动态Hash哈希表对象存储、字段频繁更新购物车、用户资料Set哈希集合去重、关系运算已读标记、抽奖集合ZSet跳表哈希表排序、排行榜、延时任务积分排行、定时任务队列3. 通用命令、过期策略与命名规范3.1 键的通用操作生产环境千万别用KEYS先记住这几个操作所有类型都适用的命令# 判断键是否存在 EXISTS user:name # 查看键的类型 TYPE user:name # 删除键 DEL user:name # 异步删除4.0不阻塞主线程 UNLINK user:name # 给键改名 RENAME user:name user:old重点警告KEYS *这个命令在生产环境绝对不能乱用。Redis是单线程执行命令的KEYS *会遍历全库所有键数据量一大命令执行期间整个Redis会被卡死所有读写请求全部排队。常见的替代方案是SCAN它通过游标分批遍历每次返回少量数据不阻塞RedisSCAN 0 MATCH user:* COUNT 100SCAN第一次用可能有点绕游标从0开始每次返回一个游标和一批键下一次把返回的游标作为新游标继续扫直到真的返回0为止。3.2 过期时间缓存必须带TTL缓存的命根子是过期时间不然缓存永远不失效内存早晚被撑爆。# 设置键 60 秒后过期 EXPIRE user:info:10001 60 # 设置键在某时间戳过期毫秒 PEXPIREAT user:info:10001 1735689600000 # 查看剩余秒数 TTL user:info:10001 # 查看剩余毫秒数 PTTL user:info:10001 # 取消过期 PERSIST user:info:10001过期删除机制主要有两种惰性删除访问键时检查是否过期过期就删和定期删除后台每隔一段事件抽样删除部分过期键。这就是为什么实际中偶发“键明明没到时间却没了”“明明该过期了还偶尔在”的诡异现象都是这两种机制互相配合的正常表现不用慌。给缓存设TTL一般建议根据业务容忍度来定会话token可以设30分钟用户基础数据的缓存可以设12小时热点新闻可以设5分钟。统一标准是“能容忍多久读到旧数据TTL就不超过这个时间”。3.3 生产环境键的命名规范Redis的键没有目录结构但我们可以用冒号分隔符模拟出层级关系这是行业通用惯例格式业务名:实体名:唯一标识[:属性名]好例子user:info:10001order:detail:8866product:stock:SKU99876坏例子zhangsan谁知道这是用户还是订单info-10001分隔符不统一容易混乱命名规范看似小事实际排查问题时能救命。集群环境里键前缀还能决定哈希槽分布规范统一的前缀方便定位数据归属和做批量管理。3.4 可视化客户端怎么选命令行用久了眼睛疼图形化客户端是调试利器。市面主流就两个方向第一是Redis Desktop ManagerRDM很知名的老牌客户端。特点是界面简洁连接管理友好但新版很多高级功能需要付费授权。第二是Another Redis Desktop ManagerARDMGitHub开源跨平台免费支持树状键列表、命令行终端、慢日志查看。我目前主力用这个日常操作、查看内存占用、刷新数据都够用。连接配置时记住三要素IP、端口、密码。填错任一项都连不上日志里常见的NOAUTH Authentication required就是密码没配对。4. 基础应用场景Redis做中间件时的正确打开方式4.1 先用代码说话Java里怎么连RedisJava生态里最常碰到的两个客户端是Jedis和Lettuce。Jedis是老牌BIO模型客户端逻辑直观Lettuce基于Netty连接复用好、线程安全Spring Boot 2.x之后默认用它。先看一段最基础的使用方式StringRedisTemplate template new StringRedisTemplate( new LettuceConnectionFactory( new RedisStandaloneConfiguration(127.0.0.1, 6379) ) ); template.opsForValue().set(user:info:10001, zhangsan, 60, TimeUnit.SECONDS); String name template.opsForValue().get(user:info:10001);实际项目里越是老手越强调两件事第一连接必须走连接池第二RedisTemplate的序列化器要显式指定。生产环境连接配置建议用连接池spring: redis: host: 127.0.0.1 port: 6379 password: yourpassword lettuce: pool: max-active: 16 max-idle: 8 min-idle: 2 max-wait: 3000ms连接池的意义在于复用TCP连接避免每次操作都重新建连握手这在请求量上来之后影响非常大。max-active别贪大一般按应用并发峰的1/4到1/2预估连接数太多反而增加Redis端上下文切换成本。4.2 缓存读写模式与序列化业务里最常见的缓存模式叫Cache Aside旁路缓存模式逻辑分三步读数据先查Redis命中直接返回。缓存未命中查数据库结果写回Redis并设置TTL。写数据先更新数据库然后删除Redis里的旧缓存。这里最经典的坑是序列化。用Spring Boot自带的RedisTemplate而不指定序列化器默认使用JdkSerializationRedisSerializer存进去的Key和Value都会变成一串奇怪的二进制用redis-cli看全是乱码业务代码里拿字符串也容易出错。我的建议是Key一律用StringRedisSerializer。Value用JSON序列化器比如GenericJackson2JsonRedisSerializer这样存进去的Value是正常JSON字符串可读性好跨语言调试也方便。最彻底的做法是直接用StringRedisTemplate把对象自己转成JSON字符串再存读出来再自己反序列化完全绕开默认序列化的坑。4.3 为什么Redis能做中间件单线程模型与原子操作Redis能承担“中间件”角色靠的是两个底层能力。第一是线程IO模型。Redis的网络IO多路复用让单线程能同时处理大量并发连接配合内存性存储在内存中做数据操作读写的极低延迟就来自这里。很多人误以为“单线程一定慢”实际恰恰相反对于纯内存操作单线程避免了上下文切换和数据竞争的开销吞吐反而更高。第二是命令的原子性。Redis在同一时间只执行一条命令像INCR、LPOP、SETNX天然是原子操作不需要加锁。这是“Redis做分布式锁”能成立的基础——SET key value NX EX 30这类命令保证只有一个客户端能写入成功拿到锁的客户端在执行完业务后主动释放。这些都是基础的逻辑等你把数据结构和命令用熟了再深入分布式锁、主从复制、集群分片会顺畅很多。5. 新手最容易踩的坑连接排查、缓存治理与持久化5.1 连接不上Redis与command timed out的排查思路很多人第一次在项目里连Redis就遇到报错常见信息之一是Redis command timed out; nested exception is io.lettuce.core.RedisCommandTimeoutException翻译过来就是Lettuce客户端发了一条命令超时了。排查这个问题的思路要按顺序来第一步先在本机用redis-cli直连Redis确认服务真的活着。第二步检查防火墙和安全组。服务器是云主机的话光改bind不够安全组规则也得放通6379端口。第三步确认代码里的host、port、password都正确。黑马点评这类学习项目最经典的情况就是把IP填成了localhost而Redis装在远程服务器或者密码写在了配置里但Redis没设密码。第四步看连接池是否耗尽。如果你的max-active太小高并发下所有连接都在等待超时就会集中爆发。临时调大max-wait和max-active可以缓解根治得好还是分析流量、加索引、减少慢操作。第五步检查Redis端是否在执行慢命令。一个KEYS *、一个ZRANGE到期大Key都会瞬间把单线程卡住所有请求超时。5.2 缓存穿透、缓存击穿与缓存雪崩这三个词是面试必背也是生产事故最常见的三个缓存问题。缓存穿透是指请求一个缓存和数据库里都不存在的数据。比如拿一个不存在的用户ID反复刷接口每次都打到数据库Redis形同虚设。解决办法有两个主流方向一是把空值也缓存起来设置短TTL二是用布隆过滤器在访问Redis前先判断数据是否可能存在。第一个办法简单直接适合大多数业务。缓存击穿是指一个热点数据过期的一瞬间大量并发请求同时打到数据库。解决办法是对热点数据加互斥锁只有一个请求去查数据库重建缓存其他请求等待或者给热点数据设置永不过期后台定期刷新。缓存雪崩是指在某个时间窗口内大量缓存同时过期所有请求一起打到数据库。解决办法是设置过期时间时加上随机抖动比如在基础TTL上加上随机0~300秒避免同一时刻集体过期。5.3 持久化选型AOF与RDB的配合重启Redis后数据还在不在取决于持久化配置。Redis提供两种持久化RDB是定时把内存快照写入磁盘优点是文件紧凑、恢复快缺点是可能丢失最近一次快照之后的数据。AOF是以日志形式记录每次写命令重启时通过重放命令恢复数据。AOF数据安全性高但文件体积大恢复速度慢。两者可以同时开启生产上最稳妥的组合是# RDB save 900 1 save 300 10 save 60 10000 # AOF appendonly yes appendfsync everysecappendfsync everysec意思是每秒同步一次AOF日志最多丢一秒数据对绝大多数业务够用了。极端要强的场景可以改成always但性能下降明显一般不建议。很多新手遇到“Redis重启了数据全没了”多半是根本没开持久化默认配置下那只是当缓存用的临时内存进程一结束就全没了。5.4 日志与慢查询定位Redis的日志和慢查询是排查故障的两把钥匙。日志位置在配置文件里用logfile指定默认可能是空由系统日志管理。生产环境最好显式指定日志文件logfile /var/log/redis/redis-server.log慢查询阈值用slowlog-log-slower-than设置单位是微秒默认值是1000010毫秒生产环境建议调到20002毫秒左右# 查看最近10条慢查询 SLOWLOG GET 10 # 查看慢查询阈值 CONFIG GET slowlog-log-slower-than出现慢查询先别急着调参数先确认是不是大Key——比如一个List有几百万个元素、一个Hash有上百万字段任何遍历操作都可能拖垮单线程。治理方向是拆分大Key或者改用不同的数据结构。最后补一句个人的实操体会我刚开始用Redis的时候所有注意力都放在命令记背上真正写项目才明白命令只是表层的输入法你怎么设计键的粒度、怎么选数据类型、怎么设过期策略、怎么规划连接池这些才决定运行稳不稳。多看慢日志、多盯大Key比背再多命令都有用。如果你刚起步先把这五种数据类型的命令敲一遍再顺手把TTL、连接池和持久化配好基础这一关就算真正过了。
阅读完成 · 觉得有帮助?