Redis APPEND 命令详细教程
APPEND 用于将给定值追加到指定 Key 现有字符串值的末尾。如果 Key 不存在,APPEND 会先以空字符串创建该 Key,再执行追加,效果等同于一次 SET。命令返回追加操作完成后字符串的总长度。它常用于原子地拼接文本片段、构建审计日志、累积遥测数据等场景。
本文基于 Redis String 数据类型介绍
APPEND。Redis 命令本身不区分大小写,因此APPEND、append和Append的效果相同;文档统一使用大写形式。APPEND自 Redis 2.0.0 起可用。
资料合集:https://pan.quark.cn/s/10e98d308913、https://pan.quark.cn/s/f56bc69c5338
一、命令概览
1. 基本语法
redis
APPEND key value
参数说明:
| 参数 | 说明 |
|---|---|
key |
目标 Key 名称 |
value |
要追加到末尾的字符串内容,二进制安全,可包含任意字节 |
返回值:
- 返回追加完成后字符串的总长度(整数)。
- Key 不存在时,
APPEND创建 Key 并写入value,返回value的长度。
2. 最简单的示例
对不存在的 Key 执行 APPEND,相当于创建它:
redis
APPEND mykey "Hello"
返回:
text
(integer) 5
继续追加内容:
redis
APPEND mykey " World"
返回:
text
(integer) 11
查看最终值:
redis
GET mykey
返回:
text
"Hello World"
3. 命令特性
APPEND只作用于 String 类型的 Key,对其他类型返回WRONGTYPE错误。- 时间复杂度为 O(1),追加本身不复制整个已有字符串(SDS 动态字符串预分配策略摊薄了成本)。
APPEND是原子命令:多个客户端并发追加同一 Key 时,Redis 按到达顺序串行执行,不会丢失任何一次追加。APPEND修改值但保留 Key 已有的过期时间,不会像不带选项的SET那样清除 TTL。- 单个 String 值的最大长度为 512 MB。
二、APPEND 的返回结果
1. Key 不存在:创建并返回新值长度
redis
APPEND greeting "Hi"
返回:
text
(integer) 2
Key 被创建,值为 "Hi"。此行为与 SET greeting "Hi" 的写入结果一致。
2. Key 存在:追加并返回总长度
redis
SET greeting "Hello"
APPEND greeting " World"
返回:
text
(integer) 11
值为 "Hello World",长度 11。返回值是追加后的总长度,不是本次追加部分的长度。
3. 追加空字符串
redis
SET greeting "Hello"
APPEND greeting ""
返回:
text
(integer) 5
追加空字符串不改变值和长度,命令正常执行并返回当前长度。
4. Key 存在但类型不是 String
redis
RPUSH mylist a b c
APPEND mylist "d"
返回:
text
(error) WRONGTYPE Operation against a key holding the wrong kind of value
APPEND 不支持 List 类型。需要向列表尾部添加元素应使用 RPUSH;两者还会产生完全不同的数据类型(String 与 List)。
5. 已过期的 Key
redis
SET temp-key "old" EX 1
等待超过 1 秒后执行:
redis
APPEND temp-key "new"
返回:
text
(integer) 3
已过期的 Key 被视为不存在,APPEND 以 "new" 重新创建 Key,长度为 3。新 Key 没有过期时间。
6. 二进制安全
APPEND 可以追加包含 \x00 等任意字节的二进制数据:
redis
DEL bin-key
APPEND bin-key "a"
APPEND bin-key "\x00b"
STRLEN bin-key
STRLEN 返回 3,说明 \x00 作为普通字节存储,不截断字符串。
三、APPEND 与过期时间
APPEND 属于"修改现有值"的命令,它不会清除 Key 已有的 TTL:
redis
SET log:job:1 "start" EX 100
TTL log:job:1
APPEND log:job:1 " - step1"
TTL log:job:1
追加后 TTL 继续递减(如返回 97),说明过期时间被保留。
这与不带过期选项的 SET 形成对比:
redis
SET log:job:1 "overwrite"
TTL log:job:1
普通 SET 覆盖值后 TTL 被清除,TTL 返回 -1。
需要注意:Key 不存在时 APPEND 创建的新 Key 永不过期,需要 TTL 时在创建后显式执行 EXPIRE,或改用 SET key value EX seconds。
四、与相关命令的区别
1. APPEND 与 SET
SET 整体覆盖值,APPEND 在末尾追加:
redis
SET msg "Hello"
SET msg "Hi" # 值变为 "Hi",TTL 被清除
APPEND msg "!" # 值变为 "Hi!"
| 对比项 | SET |
APPEND |
|---|---|---|
| 对现有值的影响 | 整体覆盖 | 末尾追加 |
| Key 不存在时 | 创建 | 创建(等效写入 value) |
| 对 TTL 的影响 | 不带选项时清除 | 保留 |
| 返回值 | OK |
追加后的总长度 |
2. APPEND 与 SETRANGE
SETRANGE 从指定偏移量开始覆盖写入,APPEND 只在末尾追加:
redis
SET target "Hello World"
SETRANGE target 6 "Redis" # 值变为 "Hello Redis"
APPEND target "!" # 值变为 "Hello Redis!"
SETRANGE 的偏移量超过当前长度时会以 \x00 填充中间空隙,APPEND 不会产生填充字节。需要在中间或指定位置改写内容用 SETRANGE,纯尾部拼接用 APPEND。
3. APPEND 与 GETRANGE / STRLEN
GETRANGE 读取字符串的指定区间,STRLEN 返回总长度。它们是 APPEND 的天然配套读命令:
redis
APPEND events "a,b"
APPEND events ",c"
STRLEN events # (integer) 4
GETRANGE events 0 -1 # "a,b,c"
APPEND 返回的长度也可直接替代一次 STRLEN 查询。
4. APPEND 与 RPUSH
两者都有"尾部添加"的语义,但数据类型不同:
| 对比项 | APPEND |
RPUSH |
|---|---|---|
| 数据类型 | String | List |
| Key 不存在时 | 创建 String | 创建 List |
| 返回值 | 追加后总长度 | 追加后列表长度 |
| 读取方式 | GET / GETRANGE |
LRANGE / RPOP |
| 适合场景 | 固定格式文本拼接、整体读取 | 无界增长、逐条消费 |
如果追加的内容将来需要逐条取出,应使用 RPUSH;只有整体读取时 String 拼接才有意义。
5. APPEND 与 INCR
INCR 对整数字符串做原子自增,APPEND 做字符串拼接,语义完全不同:
redis
SET counter 1
INCR counter # (integer) 2,数值语义
APPEND counter "0" # (integer) 2,值变为 "20",字符串语义
对 "2" 执行 APPEND counter "0" 后值是字符串 "20" 而非数值加 10。计数场景使用 INCR/INCRBY,不要用 APPEND 拼接数字。
五、事务、Pipeline 与并发
1. 原子性
APPEND 是单条命令,服务端原子执行。多客户端并发对同一 Key 追加时,Redis 单线程按命令到达顺序串行处理,每次追加都不会丢失,这是 APPEND 相比"客户端 GET 拼接后 SET 回去"的核心优势:
text
GET key -> 客户端拼接 -> SET key newvalue # 非原子,并发下会互相覆盖
APPEND key value # 原子,无丢失
2. 在事务中使用
redis
MULTI
APPEND log:1 "a"
APPEND log:1 "b"
STRLEN log:1
EXEC
EXEC 返回各命令结果:1、2、2。
3. Pipeline 批量追加
需要拼接大量片段时,用 Pipeline 减少网络往返:
text
APPEND buffer "part-1;"
APPEND buffer "part-2;"
APPEND buffer "part-3;"
Pipeline 中命令仍按顺序串行执行,追加顺序与发送顺序一致。
4. Lua 脚本中的条件拼接
需要"满足条件才追加"的原子逻辑时,使用 Lua 脚本:
redis
EVAL "if redis.call('STRLEN', KEYS[1]) + #ARGV[1] <= tonumber(ARGV[2]) then return redis.call('APPEND', KEYS[1], ARGV[1]) else return -1 end" 1 buffer "chunk" 100
该脚本在追加后总长度不超过上限(100)时才执行追加,否则返回 -1,可用来限制字符串无限增长。
六、常见客户端用法
1. redis-cli
bash
redis-cli APPEND mykey "Hello"
redis-cli APPEND mykey " World"
redis-cli STRLEN mykey
redis-cli GET mykey
两次 APPEND 分别返回 5 和 11,STRLEN 返回 11。
2. Python(redis-py)
python
import redis
client = redis.Redis(host="localhost", port=6379, decode_responses=True)
print(client.append("mykey", "Hello")) # 5
print(client.append("mykey", " World")) # 11
print(client.get("mykey")) # Hello World
3. Node.js(node-redis)
javascript
import { createClient } from "redis";
const client = createClient();
await client.connect();
console.log(await client.append("mykey", "Hello")); // 5
console.log(await client.append("mykey", " World")); // 11
console.log(await client.get("mykey")); // Hello World
await client.quit();
4. Java(Jedis)
java
import redis.clients.jedis.Jedis;
try (Jedis jedis = new Jedis("localhost", 6379)) {
System.out.println(jedis.append("mykey", "Hello")); // 5
System.out.println(jedis.append("mykey", " World")); // 11
System.out.println(jedis.get("mykey")); // Hello World
}
各客户端的 append 方法均返回追加后的总长度(整数)。
七、典型业务场景
1. 审计与操作日志
把关键操作以定界符拼接到一个 Key 中,天然保持先后顺序:
redis
APPEND audit:user:1001 "2026-09-10 login|"
APPEND audit:user:1001 "2026-09-10 change-password|"
读取时整体 GET 后按定界符拆分。日志量可能很大的场景应改用 List 或 Stream。
2. 递增构建大文本
分块生成报表、导出文件内容时逐块追加,避免客户端拼出超大字符串后再一次性 SET:
text
APPEND report:2026-09 "header;"
APPEND report:2026-09 "row-1;"
APPEND report:2026-09 "row-2;"
3. 遥测与轨迹片段累积
设备周期性上报位置片段,服务端原子累积:
redis
APPEND track:device:7 "116.40,39.90;"
并发上报也不会丢失片段。
4. 一次性令牌或验证码的多次拼接
极少数需要分段生成的令牌(如先写前缀、再补随机段)可用 APPEND 完成,注意最终消费仍需配合 GETDEL 等命令。
5. 简单限定的消息缓冲
在单 Key 上聚合短消息,配合 TTL 定期失效,形成一个轻量的临时缓冲区:
redis
APPEND buffer:5min "msg-1;"
EXPIRE buffer:5min 300
八、性能与使用建议
- 时间复杂度为 O(1):SDS 预分配机制使追加不需要每次整体复制,但注意总内存仍随追加增长。
- 单值上限 512 MB :
APPEND不会截断超限数据,超过上限会返回错误,务必在应用层限制追加总量。 - 警惕大 Key :无限追加会形成巨型 String,拖慢
GET、DEL,复制和网络传输成本也高。无界增长的数据应改用 List、Stream 等结构。 APPEND保留 TTL,若期望"追加即永久",无需额外处理;若期望刷新 TTL,需显式EXPIRE。- 高频追加同一 Key 时建议使用 Pipeline 批量发送,减少网络往返。
- 大 Key 清理使用
UNLINK而非DEL,避免阻塞主线程。 APPEND属于写命令(ACL 类别@write、@string、@fast),会写入 AOF 并复制到从节点,高频追加会放大持久化与复制流量。- 追加内容若将来要逐条消费,从设计阶段就选择 List/Stream,避免后期迁移大 Key。
九、常见问题排查
问题 1:返回的长度比预期大
检查历史追加是否残留旧内容:
redis
STRLEN key
GETRANGE key 0 50
APPEND 不会清空已有值。需要全新内容时先 DEL/UNLINK,或直接用 SET 覆盖。
问题 2:报 WRONGTYPE 错误
redis
TYPE key
目标 Key 是 List、Hash 等非 String 类型。确认业务应使用 RPUSH/HSET 等对应命令,还是先删除旧 Key 再用 APPEND 重建。
问题 3:追加后 Key 意外"永不过期"
Key 不存在时 APPEND 创建的新 Key 没有 TTL;存在时 APPEND 保留旧 TTL。若发现 Key 长期残留,排查是否曾被不带选项的 SET 覆盖或被 PERSIST,并重新设置 EXPIRE。
问题 4:并发追加导致内容交错混乱
APPEND 本身不丢失数据,但多个客户端追加的多字节内容可能交错。每个客户端的片段应一次性完整追加,不要把一个逻辑片段拆成多次 APPEND。
问题 5:Key 越来越大、读取变慢
用 STRLEN 监控增长,超过阈值(如 1 MB)时评估:
- 改用 List/Stream 分片存储;
- 定期读取后清理(
GETDEL或UNLINK); - 在 Lua 脚本中加入长度上限拒绝追加(见第五章示例)。
问题 6:二进制内容显示异常
APPEND 二进制安全,客户端读取显示乱码通常是编码问题而非数据损坏。用 STRLEN 确认长度,按原始字节处理。
十、完整练习
下面的示例覆盖创建、追加、长度、空串、类型错误、TTL 保留、二进制安全与并发对比。请在测试数据库执行;FLUSHDB 会清空当前逻辑数据库中的全部 Key。
redis
FLUSHDB
# 1. Key 不存在时创建
APPEND demo:greet "Hello"
# 2. 继续追加并观察总长度
APPEND demo:greet " World"
STRLEN demo:greet
GET demo:greet
# 3. 追加空字符串
APPEND demo:greet ""
# 4. 类型错误
RPUSH demo:list a b
APPEND demo:list "c"
# 5. TTL 保留
SET demo:log "start" EX 100
TTL demo:log
APPEND demo:log " - step1"
TTL demo:log
GET demo:log
# 6. SET 覆盖后 TTL 被清除(对比)
SET demo:log "overwrite"
TTL demo:log
# 7. 过期后 APPEND 重建
SET demo:temp "old" EX 1
# 等待 1 秒以上后执行:
# APPEND demo:temp "new"
# TTL demo:temp
# 8. 二进制安全
DEL demo:bin
APPEND demo:bin "a"
APPEND demo:bin "\x00b"
STRLEN demo:bin
# 9. 与 SETRANGE 对比
SET demo:text "Hello World"
SETRANGE demo:text 6 "Redis"
GET demo:text
APPEND demo:text "!"
GET demo:text
# 10. 与 INCR 对比(注意语义差异)
SET demo:num 1
INCR demo:num
GET demo:num
APPEND demo:num "0"
GET demo:num
# 11. Pipeline 模拟多次追加
APPEND demo:pipe "p1;"
APPEND demo:pipe "p2;"
APPEND demo:pipe "p3;"
GET demo:pipe
预期结果:
APPEND demo:greet "Hello"返回(integer) 5。APPEND demo:greet " World"返回(integer) 11,STRLEN为11,值为"Hello World"。- 追加空字符串返回
(integer) 11,长度不变。 - 对 List 执行
APPEND返回(error) WRONGTYPE ...。 - 追加前
TTL demo:log约100,追加后继续递减(如97),值为"start - step1"。 SET覆盖后TTL demo:log返回-1。demo:temp过期后APPEND重建,返回(integer) 3,TTL返回-1。demo:bin的STRLEN返回3。SETRANGE后值为"Hello Redis",再APPEND "!"后为"Hello Redis!"。INCR后值为"2";APPEND "0"后值为"20"。demo:pipe的值为"p1;p2;p3;"。
十一、命令速查表
| 需求 | 命令 |
|---|---|
| 末尾追加内容 | APPEND key value |
| 整体覆盖写入 | SET key value |
| 指定偏移覆盖 | SETRANGE key offset value |
| 读取整个值 | GET key |
| 读取区间 | GETRANGE key start end |
| 查询长度 | STRLEN key |
| 列表尾部追加(List 类型) | RPUSH key value [value ...] |
| 数值自增 | INCR key / INCRBY key n |
| 读取并删除 | GETDEL key |
| 异步删除大 Key | UNLINK key |
| 设置过期时间 | EXPIRE key seconds |
总结
APPEND 的核心作用是将值原子地追加到 String Key 的末尾,Key 不存在时创建它,基本语法为:
redis
APPEND key value
使用时重点注意五点:
- 返回值是追加后的总长度;Key 不存在时等效
SET创建。 - 只作用于 String 类型,其他类型返回
WRONGTYPE;需要逐条消费的数据应使用RPUSH(List)。 APPEND是原子命令,并发追加不丢失数据,远优于"GET 后 SET 回写"的非原子流程。- 追加保留已有 TTL,但新建的 Key 永不过期;单值上限 512 MB,谨防大 Key。
- 与
SET/SETRANGE/INCR的语义差异要分清:覆盖、定位改写、数值自增都不等同于尾部拼接。