1. 从一次线上连接超时说起上周帮一位做后端的朋友排查问题他本地写了个小脚本往缓存里塞数据结果一直报连接超时。我让他把连接命令贴过来一看redis-cli -h 127.0.0.1 -p 6379写成了redis-cli -h 127.0.0.1 -p 6378端口号差了一位。这种低级错误在新手阶段特别常见但排查起来往往要绕一大圈。缓存这东西入门第一关从来不是那些花哨的数据结构而是能不能连上、连上之后怎么确认自己操作的是对的库、键到底存进去没有。这篇内容就围绕缓存服务的连接方式和最基础的两类数据操作展开连接建立与键命令、字符串命令。不管你是刚接触缓存的新手还是从关系型数据库转过来的开发者这两块都是每天都要打交道的。我会把连接参数怎么选、键命令在什么场景下用、字符串类型的边界在哪里结合我实际踩过的坑一条条讲清楚。看完你至少能做到拿到一台缓存服务知道怎么连、怎么验证、怎么安全地操作键和字符串而不是对着命令行瞎敲。2. 连接方式与参数选择别只会默认端口2.1 三种常见连接入口的适用场景缓存服务的连接方式看起来简单但不同入口背后对应的使用场景差别很大。我一般把它们分成三类命令行客户端适合调试、临时查数据、执行一次性命令。它的优势是即时反馈敲完就能看到结果排查问题时最顺手。程序内嵌客户端业务代码里通过官方或第三方库建立连接池适合高频读写。这里的关键不是“怎么连”而是“怎么管好连接”。图形化工具适合可视化浏览键值、批量操作、监控内存占用。新手用图形化工具能快速建立对数据结构的直观认知但生产环境慎用容易误删。我见过不少新手一上来就装图形化工具结果连基本的SELECT切换库都不会换台机器就抓瞎。所以我的建议是先把命令行客户端用熟再上图形化工具。命令行是根图形化只是叶子。2.2 连接命令的完整参数拆解以最常见的命令行客户端为例一条完整的连接命令通常包含这些部分redis-cli -h 主机地址 -p 端口 -a 密码 -n 数据库编号逐个说-h主机地址。本地就是127.0.0.1远程填实际地址。这里有个坑有些环境同时支持 IPv4 和 IPv6如果127.0.0.1连不上可以试试::1反过来也一样。-p端口。默认 6379但生产环境几乎不会用默认端口改端口是最基础的防护手段之一。-a密码。注意在命令行直接写密码会留在历史记录里不安全。更好的做法是连上之后用AUTH命令输入或者用环境变量传递。-n数据库编号。默认是 0 号库范围通常是 0 到 15。很多新手连上之后发现数据“不见了”八成是切到了别的库。提示如果连接时提示NOAUTH Authentication required说明服务端设置了密码但你没提供。反过来如果提示ERR Client sent AUTH, but no password is set说明你多此一举传了密码服务端根本没设。2.3 连接池程序里最容易被忽视的一环业务代码里连接缓存最怕的不是连不上而是连接泄漏。我见过一个服务跑了两天突然大面积超时排查半天发现是每次请求都新建连接从不释放把服务端的连接数打满了。连接池的核心参数就几个参数作用常见取值参考最大连接数池子里最多放多少连接根据并发量一般 8~32最小空闲连接保持多少个随时可用2~4最大空闲连接空闲时最多保留多少与最大连接数持平或略低获取连接超时拿不到连接等多久200ms~2s空闲连接检测多久检查一次空闲连接30s~60s这些值没有标准答案但有个原则最大连接数不是越大越好。连接本身占内存服务端也有上限盲目调大只会让问题更隐蔽。我一般从 8 开始压测时观察等待队列再逐步往上加。3. 键命令数据管理的总开关3.1 键的命名规范与生命周期键命令是操作缓存数据的入口但很多人忽略了键名本身就是一种设计。我见过用用户 ID 直接当键名的也见过用一长串哈希当键名的前者容易冲突后者可读性差。比较稳妥的命名方式是业务名:对象名:ID比如user:profile:10086、order:detail:20240101。冒号分隔的好处是层级清晰图形化工具里会自动折叠成树状找起来快。另外键名不要太长虽然缓存对键名长度不敏感但几 KB 的键名在大量键的情况下会明显吃掉内存。键的生命周期管理靠过期时间。这里有个经典误区给键设了过期时间但每次读写又把它续上了结果这个键永远不过期内存慢慢被撑爆。所以设过期时间之前要想清楚这个数据是临时缓存还是需要长期保留。3.2 最常用的键命令速查下面这些命令我几乎每天都会用到按使用频率排KEYS pattern按模式查找键。生产环境禁用因为它会阻塞服务直到遍历完所有键。我吃过一次亏在几十万键的实例上执行KEYS *直接把服务卡了十几秒。替代方案是SCAN它分批返回不阻塞。EXISTS key判断键是否存在。返回 1 或 0。注意它支持一次判断多个键返回的是存在的个数。DEL key删除键。同样支持批量返回实际删除的个数。EXPIRE key seconds设置过期时间单位秒。PEXPIRE是毫秒版。TTL key查看剩余过期时间。返回 -1 表示永不过期-2 表示键不存在。这个命令在排查“数据怎么突然没了”时特别有用。TYPE key查看键的数据类型。当你忘了这个键存的是什么结构时先TYPE一下再决定用什么命令。RENAME key newkey重命名。如果新键名已存在会覆盖它慎用。注意DEL删除大键比如存了几万个元素的列表时会阻塞因为它是同步释放内存。这种情况用UNLINK它把删除操作放到后台线程不阻塞主线程。3.3 键命令的批量操作与性能边界批量操作能减少网络往返但批量大小要控制。我一般建议单次批量不超过 100 个键。原因有两个一是命令本身会占用服务端的处理时间二是网络包太大会增加传输延迟。MGET和MSET是字符串类型的批量命令键命令里没有直接的批量EXISTS但EXISTS本身支持多参数。DEL也支持多参数但同样要注意大键问题。还有一个容易被忽视的点键命令的时间复杂度。KEYS是 O(N)DEL是 O(1) 但删除大键时实际开销远不止EXPIRE是 O(1)。理解这些复杂度才能在数据量上来之后不慌。4. 字符串类型最基础也最灵活4.1 字符串的存储边界与编码方式字符串类型是缓存里最基础的结构但它的边界很多人不清楚。一个字符串键最大能存 512MB这个数字很大但实际使用中我建议单个值不要超过 10KB。原因有三一是大值在网络传输时延迟明显二是大值在内存里容易产生碎片三是大值在持久化时拖慢速度。字符串的底层编码会根据内容自动切换。短字符串用int或embstr长字符串用raw。这个细节平时不用管但当你用OBJECT ENCODING key查看时能帮你判断这个键是不是“变重”了。比如一个原本是int的计数器你往里塞了个字符串编码就变成raw内存占用会上升。4.2 字符串命令的四大类用法字符串命令按用途可以分成四类我分开说第一类读写与追加SET key value最基础的写入。它有个隐藏行为会覆盖键原有的值和过期时间。如果你只想改值不想动过期时间用SET key value KEEPTTL。GET key读取。键不存在返回 nil。APPEND key value追加内容。如果键不存在相当于SET。这个命令在拼接日志、累积字符串时有用但要注意追加次数多了之后字符串会不断重新分配内存。第二类原子计数INCR key、DECR key自增自减 1。INCRBY key n、DECRBY key n按指定步长增减。INCRBYFLOAT key n浮点数增减。这类命令是原子操作多个客户端同时执行不会丢计数。我经常用它做限流器、统计在线人数。但有个坑如果键存的值不是数字INCR会直接报错而且不会自动转换。所以用之前最好确认键的类型。第三类批量与条件操作MSET key1 value1 key2 value2 ...批量写入。MGET key1 key2 ...批量读取。SETNX key value键不存在时才写入。这是实现分布式锁的经典命令但要注意它不处理过期时间锁忘了释放就死锁了。更好的选择是SET key value NX EX seconds。SETEX key seconds value写入并设过期时间一步到位。第四类范围与位操作GETRANGE key start end截取子串。SETRANGE key offset value从指定位置开始覆盖。SETBIT key offset value、GETBIT key offset位操作。这两个命令在统计签到、用户标签时特别高效一个 bit 存一个状态1 亿用户也才占十几 MB。4.3 字符串命令的典型应用场景字符串类型不只是“存个值”这么简单它的几个特性决定了它在很多场景下是最优解缓存对象把对象序列化成 JSON 字符串存进去读的时候反序列化。简单直接但更新时需要整体覆盖。计数器利用INCR的原子性做文章阅读数、接口调用次数。注意计数器要设过期时间否则会无限增长。分布式锁SET key value NX EX secondsvalue 用唯一标识释放时校验标识再删。位图统计SETBIT做用户签到、活跃统计省内存。限流INCR配合EXPIRE实现固定窗口限流。提示用字符串存对象时如果对象很大考虑拆成多个键或者换用哈希类型。哈希类型在更新单个字段时更有优势。5. 实操过程从连接到数据操作全流程5.1 连接建立与验证假设你拿到一台缓存服务地址是10.0.0.5端口6380密码mysecret要操作 2 号库。完整流程如下# 第一步连接不直接在命令行写密码 redis-cli -h 10.0.0.5 -p 6380 # 第二步认证 AUTH mysecret # 第三步切换数据库 SELECT 2 # 第四步验证连接 PING # 返回 PONG 表示连接正常如果PING返回PONG说明连接、认证、切库都成功了。这一步看似简单但先验证再操作能避免很多“数据写错库”的问题。5.2 键操作实操记录连上之后先做几个键操作# 写入一个键 SET user:name:1001 张三 # 返回 OK # 查看键类型 TYPE user:name:1001 # 返回 string # 设置过期时间60 秒 EXPIRE user:name:1001 60 # 返回 1表示设置成功 # 查看剩余时间 TTL user:name:1001 # 返回一个小于等于 60 的数字 # 判断键是否存在 EXISTS user:name:1001 # 返回 1 # 查看所有键仅限测试环境 KEYS user:* # 返回匹配的键列表这里有个细节EXPIRE返回 1 表示设置成功返回 0 表示键不存在。如果你对一个不存在的键设过期时间它会返回 0但不会报错。这个行为容易让人误以为设置成功了。5.3 字符串操作实操记录接着操作字符串# 批量写入 MSET counter:page:1 0 counter:page:2 0 # 返回 OK # 自增 INCR counter:page:1 # 返回 1 # 再自增 INCR counter:page:1 # 返回 2 # 按步长增加 INCRBY counter:page:1 10 # 返回 12 # 批量读取 MGET counter:page:1 counter:page:2 # 返回 12 和 0 # 条件写入键不存在时才写 SETNX lock:order:1001 locked # 返回 1表示写入成功 # 再次尝试键已存在 SETNX lock:order:1001 locked # 返回 0表示未写入 # 带过期时间的条件写入 SET lock:order:1002 locked NX EX 30 # 返回 OK30 秒后自动释放这套流程走下来基本覆盖了日常最常用的操作。我建议新手把这几条命令敲三遍以上形成肌肉记忆。5.4 参数选择与计算过程以限流场景为例假设你要限制某个接口每分钟最多 100 次调用。用字符串实现# 键名设计rate:接口名:时间窗口 # 时间窗口用分钟时间戳比如 202401011200 # 第一次调用 INCR rate:api:order:202401011200 # 返回 1 # 设置过期时间120 秒比窗口多 60 秒防止边界问题 EXPIRE rate:api:order:202401011200 120 # 后续每次调用 INCR rate:api:order:202401011200 # 返回当前次数超过 100 就拒绝这里的关键参数是过期时间。设 60 秒的话如果请求刚好在窗口切换时进来可能前一秒的计数还没过期导致误判。设 120 秒留出缓冲代价是内存里多留一分钟的数据可以接受。6. 常见问题与排查技巧实录6.1 连接类问题速查现象可能原因排查方法连接超时地址或端口错误、网络不通先用ping测主机再用telnet测端口认证失败密码错误或未提供检查AUTH命令确认密码无误连接被拒绝服务未启动或最大连接数满查看服务状态检查连接数配置切库后数据不见切到了错误的库用SELECT确认当前库编号连接数暴涨连接泄漏检查代码是否每次新建连接未释放6.2 键操作类问题速查现象可能原因排查方法KEYS卡住服务键数量太大改用SCAN分批遍历键莫名消失过期时间到了或被删除用TTL查看检查是否有清理逻辑DEL阻塞删除的是大键改用UNLINK异步删除键名冲突命名不规范统一用业务:对象:ID格式内存持续增长键没有过期时间检查TTL给临时数据设过期6.3 字符串操作类问题速查现象可能原因排查方法INCR报错值不是数字用GET查看当前值确认类型值被覆盖SET覆盖了原有值和过期时间用SET KEEPTTL保留过期时间内存占用高存了大字符串用STRLEN查看长度考虑拆分批量操作慢单次批量太大控制在 100 个键以内位操作结果不对offset 计算错误确认 offset 从 0 开始单位是 bit6.4 我踩过的三个坑第一个坑KEYS *把测试环境搞挂了。当时测试环境有几十万个键我图省事直接KEYS *结果服务卡了十几秒其他同事的请求全部超时。后来改用SCAN 0 MATCH user:* COUNT 100分批遍历再也没出过问题。第二个坑SET覆盖了过期时间。有个缓存键设了 1 小时过期我更新值时用SET直接覆盖结果过期时间被清掉了这个键永远留在了内存里。后来改用SET key value KEEPTTL问题解决。第三个坑INCR用在非数字键上。有个键本来存的是字符串我误以为它是计数器直接INCR结果报错。排查时才发现这个键被别的逻辑改过。教训是用INCR之前先TYPE确认或者用GET看一眼当前值。6.5 独家避坑技巧连接池不要设太大我一般从 8 开始压测时观察等待队列逐步加到 16 或 32。盲目设 100 只会让问题更隐蔽。键名统一规范团队里约定好业务:对象:ID格式代码审查时检查能避免大量键名冲突。过期时间留缓冲限流、缓存这类场景过期时间比实际需要多留 30~60 秒防止边界问题。批量操作控制大小单次批量不超过 100 个键网络包太大反而慢。生产环境禁用KEYS用SCAN替代虽然写起来麻烦一点但不会阻塞服务。大键用UNLINK删除DEL是同步的大键会阻塞UNLINK放到后台线程安全得多。7. 从入门到熟练的下一步连接和键、字符串命令是缓存操作的基石但真正把缓存用好还得理解它的内存模型、持久化机制和集群模式。我个人的学习路径是先把单机版的连接、键、字符串、哈希、列表、集合、有序集合这七类命令全部敲熟再去看持久化和主从复制最后才是集群。如果你现在刚入门我的建议是每天花 15 分钟在测试环境敲命令不要只看文档。缓存这东西看十遍不如敲一遍。敲的过程中遇到报错把报错信息记下来查清楚原因这些报错就是最好的老师。另外图形化工具可以用但不要依赖。我见过太多人离开图形化工具就不会操作缓存了换台机器就抓瞎。命令行才是基本功图形化只是辅助。等你把命令行用熟了再用图形化工具会发现效率反而更高因为你知道每个操作背后发生了什么。
阅读完成 · 觉得有帮助?