玩转 Redis · String 基础篇

7 阅读8分钟

玩转 Redis · String 基础篇

环境:Docker 容器 redis-learn(Redis 7.4.11,本机 127.0.0.1:6379) 本篇地位:五大类型之第一篇 + 全局通用指令(后续每篇都建立在它之上)


0. 一分钟回顾:Redis 是什么

  • 仓库(硬盘):大而慢,取一趟要翻半天
  • 便签(内存):小而快,伸手就拿到

Redis = 把所有数据都记在内存便签上的超快小本子程序。

一秒可处理十几万次读写;代价是断电可能丢数据(后续"持久化"篇章解决)。

为什么快(四层)

① 纯内存 ② 单线程处理命令 + 事件循环(会排队、不打架) ③ 高效数据结构 ④ 6.0 后网络读写也用了多线程,但命令执行仍是单线程(保住"天然原子"的优点)。


1. 练习环境备忘

docker exec -it redis-learn redis-cli --raw   # 进入"聊天模式"(--raw 中文正常显示)
docker exec redis-learn redis-cli <命令>       # 单发一条(写脚本用)
docker start redis-learn                       # 电脑重启后救活容器
  • PINGPONG:健康检查(确认网络通、Redis 活着)
  • 退出聊天模式:exitCtrl+D
  • 在 Claude Code 对话里可用 ! 前缀直接执行命令

2. 必懂概念

key / value

一条便签 = key(条目名) + value(内容)

key 与 value 的类型规则 ⭐

对象规则
key永远只有字符串一种。名字里写数字、中文都行,但统统按字符串对待。不存在"hash 型的 key"
value五大类型同时只能装一种形状(String/Hash/List/Set/ZSet 五选一)
TYPE key查的其实是 value 的类型,不是 key 的

拿错形状的命令操作会报错(实测,Redis 7.4):

HSET demo:t2 name "小明"1
GET demo:t2                   → WRONGTYPE Operation against a key holding the wrong kind of value
APPEND demo:t2 "x"            → WRONGTYPE(同样报错)

例外特权:SET 无视原类型,直接覆盖(官方文档原话:"If key already holds a value, it is overwritten, regardless of its type"):

SET demo:t2 "hello"           → OK      ← hash 被直接替换成 string,不报错!
TYPE demo:t2                  → string

⚠️ 版本差异警示:较老版本的 Redis 里 SET 也会报 WRONGTYPE(先删再建才能换形状)。行为跟着版本走,拿不准就当场实测 + 查官方文档

key 命名法(重要约定)

业务:对象ID:字段      例:user:1:agelikes:post:1sms:code
  • 冒号对 Redis 没有特殊含义,本质还是一条普通 key,纯人类约定
  • 好处:一眼看清归属;KEYS 'user:2:*' 通配符批量找;图形化工具显示成目录树
  • key 名禁止放空格:放了就得每条命令伺候引号 GET "rename key"
  • key 名区分大小写nameNameNAME(但命令不区分大小写,set = SET,大写只是习惯)

3. 通用指令(五种类型通吃 ⭐)

命令作用返回值
DEL k1 k2 …删除(可批量)真正删掉的条数(不存在的 0)
EXISTS key在不在1 在 / 0 不在
TTL key剩余寿命正数=剩余秒 / -1 永不过期 / -2 不存在
EXPIRE key 秒给已存在的 key 补定闹钟1 成功 / 0 失败(不存在)
PERSIST key撕掉闹钟,变永不过期1 成功 / 0 失败
RENAME 旧 新key 改名(内容不动)OK
TYPE key查看类型(查的是 value)string/hash/list/set/zset
KEYS '模式'按通配符列出 keykey 列表

⚠️ KEYS 会翻遍全库,正式环境大数据量禁用,用温和版 SCAN(后续讲)。 ⚠️ shell 里 * 会被展开,KEYS '*' 记得加引号。


4. String 专属指令

命令说明
SET key value存一条(同名 = 覆盖任意类型,这也是最常用的"改")
SET key value EX 秒存的同时定闹钟
MSET k1 v1 k2 v2 …批量存
SETNX key value位置空才贴(NX = not exists)。1=贴上 / 0=已存在没动 → 分布式锁的种子

命令说明
GET key取一条
MGET k1 k2 …批量取(一次跑腿带回多条)
STRLEN key内容的字节数(一个汉字 = 3 字节,英文 1 字符 = 1 字节)

命令说明
同名 SET直接覆盖
APPEND key "内容"尾部续写,返回新长度(字节)
GETSET key 新值换新值,同时返回旧值

DEL(见通用指令)

计数器(数字专属"改")⭐

命令说明
INCR key+1;key 不存在自动当 0 加,新 key 直接 INCR 会变成 1
DECR key−1
INCRBY key n / DECRBY key n±n

为什么不用"GET→算→SET"? 两人同时点赞会丢数据(都拿到旧值 3,都写回 4)。INCR 把读改存打包成一步,Redis 内部排队处理,绝不插队 → 这个性质叫原子性。点赞数、阅读量、库存扣减都靠它。

对非数字内容 INCRERR value is not an integer or out of range(报错是保护)。


5. 返回值心法

  1. 大多数命令用 1/0 当灯泡开关(EXISTS/SETNX/EXPIRE/PERSIST…)
  2. DEL 返回数量,不是 0/1
  3. TTL 特例-1 永生,-2 不存在(正数留给秒数)
  4. INCR 返回计算结果,不是标志
  5. 数字含义跟着命令走,不跟着数字走 → 忘了就 HELP 命令名 当场查

6. 实操流水

6.1 过期闹钟实验(第 1 次实操)

127.0.0.1:6379> set tip "tips 5" EX 5
OK
127.0.0.1:6379> get tip
tips 5                        ← 刚存完,还在
127.0.0.1:6379> get tip
                              ←(等 5 秒后)空白 = 已自动消失(raw 模式 nil 不显示)
127.0.0.1:6379> TTL tip
-2                            ← 已不存在
127.0.0.1:6379> get favorite-color
蓝色                           ← --raw 模式下中文正常显示

6.2 SETNX 占坑实验

127.0.0.1:6379> SETNX use:1:name "xiaohong"
1                             ← 贴上了(注:key 拼错 use,悄悄建了新 key,后已删)
127.0.0.1:6379> SETNX user:1:name "xiaohgon"
1                             ← key 不存在 → 贴上
127.0.0.1:6379> SETNX user:1:name "xiaohgon"
0                             ← key 已存在 → 拒绝
127.0.0.1:6379> GET user:1:name
xiaohgon                      ← 原值没被第二个 SETNX 动过

6.3 字节计数实验(STRLEN / APPEND)

127.0.0.1:6379> STRLEN user:1:name
8                             ← xiaohgon:8 个英文字符 = 8 字节
127.0.0.1:6379> SET user:1:name "xiaohong"
OK
127.0.0.1:6379> APPEND user:1:name "gogogo"
148 + 6 = 14 字节
127.0.0.1:6379> GET user:1:name
xiaohonggogogo

6.4 GETSET 换新取旧

127.0.0.1:6379> GETSET user:1:name "new name is xiaoming"
xiaohonggogogo                ← 返回的是旧值!
127.0.0.1:6379> GET user:1:name
new name is xiaoming          ← 新值已生效

6.5 RENAME 与"带空格的 key"翻车现场

127.0.0.1:6379> RENAME user:1:name "rename key"
OK                            ← 成功!新 key 的真名是「rename key」(中间带空格)
127.0.0.1:6379> GET user:1:name
                              ← 空白:旧名已经不存在了
127.0.0.1:6379> GET rename key
ERR wrong number of arguments for 'get' command
                              ← 空格把名字拆成两个参数
127.0.0.1:6379> GET "rename key"
new name is xiaoming          ← 引号包住才行!
127.0.0.1:6379> RENAME "rename key" user:1:name
OK                            ← 改回正常名字

6.6 批量查 MGET

127.0.0.1:6379> MGET user:1:age user:2:address
18
chengdu                       ← 两条按顺序返回

6.7 大小写验证

127.0.0.1:6379> get user:1:name     (小写命令)
new name is xiaoming          ← 命令不区分大小写 ✅
127.0.0.1:6379> GET User:1:Name
(nil)                         ← key 区分大小写:这是另一条不存在的 key
EXISTS User:1:Name0        ← 铁证

6.8 类型规则验证(WRONGTYPE 与 SET 的特权)

HSET demo:t2 name "小明"1
TYPE demo:t2                  → hash
GET demo:t2                   → WRONGTYPE Operation against a key holding the wrong kind of value
APPEND demo:t2 "x"            → WRONGTYPE
SET demo:t2 "hello"           → OK   ← SET 无视类型直接覆盖(官方文档确认)
TYPE demo:t2                  → string

6.9 返回值全家福(对照速查)

EXISTS no-such-key0
SETNX demo:nx "v1"1
SETNX demo:nx "v2"0
DEL no-such-key0
EXPIRE no-such-key 100key 不存在,闹钟定不上
TTL lesson:hello              → -1   ← 永不过期
TTL no-such-key               → -2   ← 不存在
INCR demo:cnt                 → 1    ← 新 key0 起步
INCR demo:cnt                 → 2
DEL demo:nx demo:cnt          → 2    ← 批量删,返回删掉的条数

6.10 连接诊断

redis-cli -p 9999 ping  →  Could not connect to Redis at 127.0.0.1:9999: Connection refused
redis-cli ping          →  PONG

7. 踩坑实录

现象教训
拼错 key 名GET favorit-color(nil)Redis 对不存在的 key 不报错;用 翻历史改,别重打
key 里带空格GET rename key → ERR 参数数量key 名禁空格;有的话必须 GET "rename key"
raw 模式取不存在的 key输出空白一行空白 = nil = 不存在;要明确判断用 EXISTS / TTL(-2)
GET 多带了参数GET k "v" → ERRGET 只收一个 key,粘贴时注意
多打了反斜杠Invalid argument(s)命令行里多出的字符会被当参数
两个终端同时操作前后查询结果"矛盾"Redis 是共享数据库,多客户端并发读写;判断状态要当场验证
Tab 补全没反应redis-cli 的 Tab 只补命令名Z+Tab),不补 key 名
记忆与版本不符以为 SET 会报 WRONGTYPE,实际 OK 覆盖行为跟着版本走:当场实测 + 查官方文档,别全信记忆

8. 自检清单(都能答上 = 本篇毕业 ✅)

  • Redis 为什么快?断电为什么可能丢数据?
  • SET fruit apple 谁是 key 谁是 value?
  • TTL 返回 5 / -1 / -2 分别是什么意思?
  • SETNX 什么时候返回 0?
  • "小明"的 STRLEN 是几?为什么?
  • 点赞为什么用 INCR 而不是 GET+SET?
  • user:1:age 这种命名好在哪?冒号对 Redis 有意义吗?
  • 命令大小写敏感吗?key 呢?
  • key 可以是什么类型?value 呢?
  • 拿 GET 去读一个 hash 类型的 key 会怎样?SET 呢?

9. 全图:五大类型地图(本篇已完成 1/5)

篇章形状典型用途招牌命令状态
📗 String一行字的纸条缓存、计数器SET GET INCR
📘 Hash小表格(字段↔值)对象、单字段修改HSET HGET⬅️ 下一站
📙 List两头进出的队伍消息队列、最新列表LPUSH RPOP
📕 Set不重复的一筐球去重、标签、共同好友SADD SINTER
📓 ZSet带分数自动排队排行榜、优先级ZADD ZRANGE

记忆锚点:专属指令首字母 = 类型名(H→Hash,L→List,S→Set,Z→ZSet)。