只要是正经用过 Redis 的人,List、Hash、Set、ZSet 这四个基础类型基本都能聊上几句。但面试官一旦把 Stream、Geospatial、HyperLogLog、Bitmaps、Bitfields 这几个名字摆到台面上,很多人就开始露怯了——平时生产环境很少碰,看文档又总觉得隔了一层,真到用的时候完全不知道选哪个。
这篇文章就专门把这五种高级数据类型掰开揉碎讲清楚。我会从它们各自解决的痛点出发,把底层原理、命令细节、适用场景和实际坑点都过一遍,最后给出一套可以照抄的实操案例。不管你是在准备 Redis 面试,还是想在项目里把 Redis 用得更深一点,这篇都能帮你省下不少翻文档的时间。
1. 内容整体设计与思路拆解
1.1 这五种类型到底解决了什么问题
很多人学 Redis 高级类型容易陷入一个误区:把命令背得滚瓜烂熟,却不知道它们是为了什么场景而生的。我建议换个思路,先看痛点,再看解决方案。
先看基础类型解决不了的事。List 可以当队列用,但它是"单消费者"模型,消息被一个消费者取走就没了,想搞多个消费者分组处理、各自维护消费进度,List 完全做不到。Pub/Sub 解决了广播问题,但它不持久化,消费者一掉线就错过消息。这两个痛点就是 Stream 出现的理由。
再看统计场景。要统计一个页面的独立访客数(UV),最直接的办法是用 Set 把每个用户 ID 存进去,然后 SCARD。但用户量一旦到千万级,这个 Set 占用的内存就非常可观了。HyperLogLog 用固定 12KB 内存就能统计任意数量级的基数,误差控制在 0.81% 左右,这就是典型的"用精度换内存"。
还有一类场景和位置相关。比如外卖 App 要查"附近 3 公里有哪些门店",普通做法是把所有门店坐标拉到应用层,自己算距离再排序。Geospatial 类型直接在 Redis 里完成了距离计算和范围检索,一条命令搞定。它的底层其实是一个 ZSet,只不过 score 存的是经纬度经过特殊编码后的值。
Bitmaps 和 Bitfields 则瞄准了"极致省内存"的方向。一个用户只有两种状态(在线/离线、签到/未签到),用字符串存太浪费,位图一个用户只占 1 bit。Bitfields 更进一步,允许你把多个小整数紧凑地塞进同一个字符串里,适合做计数器、评分这类数据。
1.2 方案选型:什么时候该用,什么时候别硬上
理解了各自解决的问题,还有一个更实际的问题:项目里到底该怎么选?这里我给一个我自己的判断框架,不一定权威,但实战中挺好用。
Stream 适合轻量级消息队列场景。团队不想引入 Kafka 或 RabbitMQ,或者消息量本身不大(每秒几千条以内),用 Stream 完全够。但如果你需要消息堆积能力特别强、要精确的分区顺序保证、要对接 Hadoop 生态,那还是老实上 Kafka。Redis Stream 不是要取代专业 MQ,它更像是"Redis 顺手把消息队列的活也干了"。
Geospatial 适合所有基于 LBS 的"附近"检索需求,不管是门店、车辆还是好友。不过要注意,它在跨大洲这种超远距离计算时精度会下降,而且不适合做复杂的多边形区域判断。真要做行政区划级的地理围栏,还是得用专业的 GIS 方案。
HyperLogLog 只做基数统计,不做精确统计。日活用户、独立 IP、页面 UV 这种"差几千个也无所谓"的场景完全没问题。但如果是财务对账、库存数量这种一分钱都不能差的数据,千万别碰它。
Bitmaps 适合状态类数据。签到、在线状态、用户性别这种非 0 即 1 的场景,用位图能省出惊人的内存。Bitfields 适合需要原子自增的小整数场景,比如计数器、限流令牌、等级积分。
一句话总结:高级类型不是越高级越好,而是"数据长什么样,就选什么结构去匹配它"。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 五种高级数据类型的核心原理与使用要点
2.1 Stream:消息队列的正确打开方式
Stream 是 Redis 5.0 引入的数据类型,本质是一个追加式的日志结构。每条消息有一个自增 ID,格式是"毫秒时间戳-序号",如果同一毫秒内有多条消息,序号从 0 开始递增。这个设计保证了 ID 是唯一且有序的,消费者可以按 ID 范围拉取消息。
Stream 最核心的优势是消费者组(Consumer Group)。同一个组里的多个消费者会分摊消息,每条消息只会被组内的一个消费者处理。而且 Stream 不像 List 那样取走就删,消息会一直留在内存里,直到你主动 XDEL,或者通过 XTRIM 裁剪旧消息。这就解决了消费者崩溃导致消息丢失的问题——处理完消息之后要主动 XACK,服务端才知道"这条我已经搞定了"。
不过要注意,Stream 的消息持久化依赖 Redis 本身的持久化配置。生产环境如果只开了 RDB 没开 AOF,Redis 一旦重启,最近几秒的 Stream 消息可能就没了。所以用 Stream 做消息队列,至少要把 appendfsync 设成 everysec。
另外,Stream 和常见的"stream disconnected before completion"这类报错没有关系。这个报错通常来自一些 API 客户端或网络中间层,是请求连接被异常中断导致的,不是 Redis Stream 数据类型的问题。排查时要分清网络层错误和数据层错误,别一看到 stream 单词就往数据类型上想。
表:Stream 与 List、Pub/Sub 的对比
| 能力 | List 队列 | Pub/Sub | Stream |
|---|---|---|---|
| 消息持久化 | 是 | 否 | 是 |
| 多消费者组 | 否 | 订阅模式 | 是 |
| 消费者确认机制 | 否 | 否 | 是(XACK) |
| 消息回溯 | 否 | 否 | 是 |
| 阻塞读取 | 是(BRPOP) | 是(SUBSCRIBE) | 是(XREAD BLOCK) |
2.2 Geospatial:地理位置检索
Geospatial 在 Redis 3.2 版本加入,底层实现是基于 ZSet 的。写入时,Redis 会把经纬度通过 GeoHash 算法编码成一个 52 位的整数作为 score,成员名作为 value。所以你在 GEOADD 之后会发现,用 ZRANGE 也能看到这些成员,只不过 score 是一串看不懂的大数字。
这个设计带来一个好处:ZSet 的所有能力都能复用。比如 ZREM 可以删除地理信息,ZRANGE 可以按顺序遍历所有地点。也带来一个限制:如果你想修改某个成员的经纬度,不能直接改,要先 ZREM 再重新 GEOADD。
精准度方面,GeoHash 编码本身的误差大概在米级。做"附近门店"这类查询完全够用,因为门店覆盖范围通常是几百米到几公里,一米的误差无伤大雅。但如果你要做精度要求极高的室内定位或者厘米级测量,Geospatial 就不是为这种场景设计的。
日常最常用的命令是 GEOSEARCH(6.2+ 版本)。比如查找某个坐标周围 5 公里内的门店,按距离从近到远排序,一条 GEOSEARCH 就能搞定。低版本用的是 GEORADIUS,功能类似,但 GEOSEARCH 更灵活,支持从另一个成员出发进行搜索,写法也更清晰。
2.3 HyperLogLog:基数统计的极限压缩
HyperLogLog 的原理可以和"抛硬币"联系起来。想象你在抛一枚硬币,记录第一次出现正面时已经抛了多少次。如果这个次数是 k,你会直觉地认为连续抛出 k-1 次反面的概率是 2 的负 k 次方,也就是说,k 越大,说明"之前的尝试次数"越可能多。HLL 把输入元素通过哈希函数映射到 16384 个桶里,每个桶记录一个"最大连续零前缀长度",最后用这些值估算总的基数。
这么说可能还有点抽象,你只需要记住三个关键数字:固定占用 12KB 内存、标准误差 0.81%、可以统计 2 的 64 次方个元素。不管你是往里面塞了 1 万个用户 ID 还是 10 亿个用户 ID,内存占用都是 12KB,这一点在超大 UV 统计场景里非常香。
HLL 有几个使用上的特点要特别注意。第一,它不存原始元素,只存哈希后的痕迹,所以你没法从一个 HLL 里反查出有哪些用户访问过。第二,PFCOUNT 返回的是估算值,不是精确值,如果要精确数据就别用。第三,多个 HLL 可以用 PFMERGE 合并,合并后的结果相当于把所有数据汇总后重新估算,这个特性在做分时段 UV 汇总、分机器统计合并时非常实用。
实际业务里最常见的就是"每日 UV 统计"。用一个以日期为 key 的 HLL,每次用户访问就 PFADD 一次,一天的独立访客量直接 PFCOUNT 出结果,内存开销几乎可以忽略。
2.4 Bitmaps:位图运算
Bitmaps 并不是一种全新的数据结构,它本质上就是把一个字符串当成一个由 bit 组成的数组来用。通过 SETBIT / GETBIT 命令,你可以单独操作某个位上的 0 或 1。一个用户对应一个偏移量,值为 1 表示"有/是/在线",值为 0 表示"无/否/离线"。
它的内存优势可以用一个简单计算说明:一个用户占 1 bit,一亿用户占 1 亿 bit,也就是大约 12.5MB。如果用 Set 存这一亿个用户 ID,就算每个 ID 只占 8 字节,也要 800MB 左右。差距是几十倍。这就是位图在用户状态类场景里不可替代的原因。
Bitmaps 还提供了一组位运算命令。BITCOUNT 统计整个位图里有多少个 1,可以配合偏移范围做分段统计。BITOP 可以对多个位图做 AND / OR / XOR / NOT 操作,结果存到新的 key 里。这个特性很方便——比如要统计"同时在线且是会员"的用户数,把在线位图和会员位图做一次 AND,再 BITCOUNT 就能得到结果。
使用上有个容易踩的坑:SETBIT 的偏移量最大到 2^32-1,超过会报错。而且如果设置了一个很大的偏移量,Redis 会把中间所有位都补 0,这个 key 会瞬间膨胀成一个很大的字符串。所以设计位图偏移量时,尽量从 0 开始连续编号,避免稀疏位图浪费内存。
2.5 Bitfields:紧凑整数存储
Bitfields 是 Redis 3.2 和 Geospatial 一起引入的能力,和其他类型最大的不同是:它让你在 Redis 里直接操作二进制位级别的整数。你可以把一个字符串按位划分为多个区域,每个区域存一个 1 到 64 位的整数,然后对这些整数进行读取、设置和自增操作。
举个例子,你可以定义一个 64 位的字符串,前 8 位存用户等级,中间 16 位存经验值,后 32 位存金币数。三个字段加起来只占 64 bit,也就是 8 字节。如果用三个独立的 Redis key 存储,光 key 的元数据开销就不止这点。
Bitfields 最常用的命令是 BITFIELD,它的特殊之处在于一条命令里可以带多个子命令。比如同时 GET 两个字段、SET 一个字段、再 INCRBY 一个字段,所有这些操作在一个命令内执行,是原子的。这个原子性在做计数器和限流器时非常关键,不需要额外的 Lua 脚本也能保证并发安全。
溢出控制是 Bitfields 的另一个亮点。默认的溢出策略是 WRAP,也就是回绕,比如一个 8 位无符号整数最大值是 255,再加 1 就变回 0,这个和 Java 里的 int 溢出行为一样。如果不希望出现这种问题,可以改成 SAT 模式,超过最大值就停在最大值;或者 FAIL 模式,直接返回错误。三种模式可以根据业务需要灵活切换。
3. 实操过程与核心环节实现
3.1 环境准备与基础约定
动手之前先确认环境。Stream 类型需要 Redis 5.0+,Geospatial 和 Bitfields 需要 3.2+,GEOSEARCH 命令需要 6.2+。如果要用生产级别的 Stream 能力,建议至少 Redis 7.0,因为 7.0 引入了 XAUTOCLAIM,处理消费者故障转移会方便很多。
下面的演示命令都在 Redis 7.0 上执行,客户端用 redis-cli。为了便于理解,我会把"业务背景"和"命令操作"分开写,方便你直接替换成自己的场景。
3.2 Stream 完整实现消息队列
我用一个"用户下单后发送通知"的场景演示 Stream。订单服务作为生产者,把订单消息写入 Stream;通知服务作为消费者,从 Stream 里读取并发送通知。
第一步,创建 Stream 并写入消息。XADD 命令的格式是 XADD 队列名 ID 字段1 值1 字段2 值2。ID 的位置写 * 表示让 Redis 自动生成时间戳-序号格式的 ID:
bash复制> XADD order:notify * order_id 10086 user_id 2001 amount 99.5
"1721000000000-0"
> XADD order:notify * order_id 10087 user_id 2002 amount 19.9
"1721000000001-0"
写入成功后返回了自动生成的 ID。然后创建消费者组,从 Stream 开头开始消费:
bash复制> XGROUP CREATE order:notify notify_group 0
OK
消费者组名为 notify_group,0 表示从头开始消费;如果传 $,则只消费创建消费者组之后新到达的消息,已经存在的历史消息会被忽略。这个参数的选择要根据业务来,一般"只处理新消息"用 $ 更多。
消费者读取消息:
bash复制> XREADGROUP GROUP notify_group consumer-1 COUNT 10 STREAMS order:notify >
1) 1) "order:notify"
2) 1) 1) "1721000000000-0"
2) 1) "order_id"
2) "10086"
3) "user_id"
4) "2001"
5) "amount"
6) "99.5"
> 是一个特殊符号,表示"只返回从未被投递给其他消费者的新消息"。消费完并处理成功后,要向服务端确认,否则消息会一直停留在 pending 列表里:
bash复制> XACK order:notify notify_group 1721000000000-0
(integer) 1
如果消费者处理消息时崩溃了,消息没有 XACK,会一直留在 pending 里。此时可以查看 pending 列表,找出哪些消息一直没被确认:
bash复制> XPENDING order:notify notify_group
1) (integer) 1
2) "1721000000001-0"
3) "1721000000001-0"
4) 1) 1) "consumer-1"
2) "1"
Redis 7.0 之后,可以用 XAUTOCLAIM 把超过一定时间的 pending 消息转移给当前消费者处理:
bash复制> XAUTOCLAIM order:notify notify_group consumer-2 60000 0
这个命令把 pending 超过 60 秒的消息转移给 consumer-2,避免消费者崩溃后消息被永远卡住。这是 7.0 之前比较难处理的一个问题,升级到 7.0 后简单了很多。
3.3 Geospatial 实现附近门店查询
我用一个"查找距离用户最近的 5 家门店"的场景。先把门店坐标写入 Redis。GEOADD 命令的格式是 GEOADD key 经度 纬度 成员:
bash复制> GEOADD shop:loc 116.397128 39.916527 "shop_a"
(integer) 1
> GEOADD shop:loc 116.410886 39.880977 "shop_b"
(integer) 1
> GEOADD shop:loc 116.393381 39.932022 "shop_c"
(integer) 1
注意:GEOADD 的坐标顺序是"经度 纬度",不是纬度经度。我见过不少同事这个顺序写反,结果数据全部跑偏。这是一个非常容易犯的低级错误。
查两个门店之间的距离:
bash复制> GEODIST shop:loc shop_a shop_b km
"4.3722"
查用户当前坐标(比如 116.403873 39.915119)周围 5 公里内的门店,按距离从近到远排:
bash复制> GEOSEARCH shop:loc FROMLONLAT 116.403873 39.915119 BYRADIUS 5 km ASC
1) "shop_a"
2) "shop_b"
如果想知道每个门店的距离,可以加 WITHCOORD 和 WITHDIST:
bash复制> GEOSEARCH shop:loc FROMLONLAT 116.403873 39.915119 BYRADIUS 5 km ASC WITHDIST
1) 1) "shop_a"
2) "0.6959"
2) 1) "shop_b"
2) "4.0334"
实际项目中,门店的坐标数据可以直接从数据库批量同步到 Redis,同步之后所有"附近查询"都走 Redis,应用的查询压力会大幅下降。门店坐标更新时,先 ZREM 删掉旧成员,再重新 GEOADD。
3.4 HyperLogLog 统计 UV
统计"今天一共有多少独立访客"。用日期做 key:
bash复制> PFADD uv:2025-06-01 user_1001 user_1002 user_1003
(integer) 1
> PFADD uv:2025-06-01 user_1002 user_1004
(integer) 1
> PFCOUNT uv:2025-06-01
(integer) 4
前两条命令添加了 4 个不重复的用户 ID,PFCOUNT 返回 4。如果继续添加更多用户,PFCOUNT 会给出一个误差在 0.81% 以内的估算值。
每个用户 ID 只需要在用户访问时执行一次 PFADD 即可,不用管是否重复,HLL 内部会自动去重。代码层面,对应的逻辑大概是:
python复制import redis
r = redis.Redis(host="127.0.0.1", port=6379, decode_responses=True)
def record_uv(user_id: str):
r.pfadd("uv:2025-06-01", user_id)
def get_uv() -> int:
return r.pfcount("uv:2025-06-01")
如果需要按周汇总,可以把一周七天的 HLL 合并成一个,再统计总量:
bash复制> PFADD uv:2025-06-01 user_1001
> PFADD uv:2025-06-02 user_1002 user_1001
> PFMERGE uv:week_2025_06_01_07 uv:2025-06-01 uv:2025-06-02
> PFCOUNT uv:week_2025_06_01_07
(integer) 2
注意,PFCOUNT 返回的是估算值,在做数据报表时建议明确标注口径是"近似值",避免业务方拿精确值来比对产生误会。
3.5 Bitmaps 实现签到统计
用 Bitmaps 实现"用户连续签到"功能。设计思路:每个用户每天一个 bit,key 的格式设计成"sign:用户ID:年月",偏移量用"当月的第几天减 1"。
举例,用户 1001 在 6 月 1 日签到:
bash复制> SETBIT sign:1001:202506 0 1
(integer) 0
6 月 2 日签到:
bash复制> SETBIT sign:1001:202506 1 1
(integer) 0
查询 6 月 1 日是否签到:
bash复制> GETBIT sign:1001:202506 0
(integer) 1
统计这个月一共签到了几天:
bash复制> BITCOUNT sign:1001:202506
(integer) 2
如果要批量设置或查询多位数据,可以用 BITFIELD 优化。比如一次查看 6 月前 8 天的签到情况,把这 8 天当成一个 u8 整数读出:
bash复制> BITFIELD sign:1001:202506 GET u8 0
1) (integer) 3
返回 3,二进制是 00000011,表示第 0 位和第 1 位是 1,也就是前两天签到了。连续的 bit 位组合可以一次性读出来,方便做连续签到判断。
如果业务需要清除某一天的签到记录,直接 SETBIT 设回 0 即可:
bash复制> SETBIT sign:1001:202506 1 0
(integer) 1
签到场景用 Bitmaps 的优势非常明显。假设平台有 1000 万日活用户,一天的签到状态只占 1000 万 bit,也就是约 1.2MB,一个月下来一个 key 也就十几 MB,对 Redis 来说毫无压力。
3.6 Bitfields 实现计数与限流
Bitfields 特别适合"多个小整数需要原子自增"的场景。比如做一个简单的接口限流器,限制每个用户每分钟最多调用 100 次。
设计思路:以"用户ID + 当前分钟"为 key,用一个 u16 整数作为计数器。每次请求到来时,执行一次原子自增,然后判断自增后的值是否超过阈值。超过就拒绝请求,没超过就放行。同时给 key 设置一个 60 秒的过期时间,到下一分钟自动重新计数。
bash复制> BITFIELD rate:user_1001:202506011200 INCRBY u16 0 1
1) (integer) 1
第一次执行返回 1,表示这一分钟已经调用了一次。第 101 次执行时返回 101,超过阈值 100,可以拒绝。
再举个例子:存储游戏玩家的属性。假设玩家的金币数、钻石数、等级都是整数,需要频繁增减。传统方式用三个 key 存三个字符串,每次增减都要先 GET 再 SET,还要考虑并发问题。用 Bitfield 可以把这三项存在一个 key 里:
bash复制# 偏移0开始,32位存金币;偏移32开始,32位存钻石;偏移64开始,16位存等级
> BITFIELD player:1001 SET u32 0 1000 SET u32 32 200 SET u16 64 5
1) (integer) 0
2) (integer) 0
3) (integer) 0
增加 100 金币:
bash复制> BITFIELD player:1001 INCRBY u32 0 100
1) (integer) 1100
这里要注意,BITFIELD 里的偏移量是按 bit 计算的,不是按字节。字段排布需要你提前算好每个字段的偏移,这和 C 语言里操作结构体有点类似。设计字段布局时建议画一个简单的位图示意,标注各字段的起止 bit 位置,避免算错。
BITFIELD 也支持在单条命令里同时执行多个操作,这在"多个计数器一起更新但必须保证原子性"的场景里非常有用。
4. 常见问题与排查技巧实录
4.1 高频踩坑问题速查表
先说几个我在实际项目和社区里见过的高频问题,整理成了一张速查表:
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| GEOSEARCH 查不到数据 | GEOADD 经纬度顺序写反 | 确认是"经度 纬度",纬度范围 -90~90,经度范围 -180~180 |
| Stream 消费者重启后有消息重复消费 | 处理完业务但没有 XACK | 在消息处理成功路径上调用 XACK,保证至少一次语义 |
| HLL 统计值比实际值偏小 | 同一元素在不同业务模块用了不同 key | 统一 PFADD 的 key 设计,或者用 PFMERGE 归并 |
| SETBIT 大偏移导致 key 很大 | 偏移量设计过于稀疏 | 重新设计偏移映射,让用户 ID 尽量连续编号 |
| BITFIELD INCRBY 结果出现负数或回绕 | 默认溢出策略是 WRAP | 追加 OVERFLOW SAT 或 FAIL 子命令 |
| Stream 消息丢失 | 持久化配置不全 | 生产环境开启 AOF 并设置 appendfsync everysec |
| 用 String 存用户状态内存暴涨 | 数据结构选型错误 | 换成 Bitmaps,一个用户 1 bit |
4.2 排查现场:一个 Stream 消息堆积的案例
之前我们有个项目用 Redis Stream 做短信通知队列,上线一段时间后突然出现消息大量堆积,消费者处理不过来。当时我第一反应是消费者线程挂了,看了日志却一切正常。
逐个排查后发现问题出在消费逻辑上:消费者从 Stream 读到了消息,但业务校验失败时直接抛异常退出了,没有走 XACK 逻辑。结果消息全部留在 pending 列表里,同时因为每次重新拉取都会优先拉 pending 中的消息,导致后面的消息永远排不上队。
这个问题的根因是"处理失败"和"消息确认"没解耦。后来我调整了处理逻辑:无论业务是否成功,消息都先 ACK,然后由业务侧单独记录失败原因和重试状态。因为 Redis Stream 本身不提供重试队列,你把消息 ACK 掉之后,要自己做失败重试机制,否则消息会被无限卡住。
从那之后我就养成了一个习惯:用 Stream 之前,先把消费流程图理清楚,特别是"读消息 -> 处理 -> ACK -> 异常处理"这条链路。读出来不 ACK 的,和 ACK 了但没处理成功的,是完全不同的两种情况,必须分开设计。
另外在实际运维中,我还见过有人把 stream disconnected before completion 这类客户端报错误以为和 Redis Stream 有关。这类报错大多数是 HTTP 或 API 调用层的连接中断,比如响应超时、服务端主动断开连接、负载过高导致请求被丢弃,需要在网络层和客户端配置里排查,而不是去检查 Redis 的 Stream 数据类型。概念不要混淆,排查方向错了会浪费很多时间。
4.3 几个值得记住的实操经验
关于内存和性能,再多说几句。HyperLogLog 的 12KB 是常量,不管塞多少数据都不变,这个特性在做海量 UV 统计时优势巨大。但如果你有多组 HLL 要合并,PFMERGE 会创建一个新的 HLL,合并过程会遍历所有源 key,数据量大的时候要注意耗时会有所增加。
Geospatial 底层是 ZSet,所以所有 ZSet 的命令都能用。但有一点很多人不知道:ZSet 是有序的,而 GeoHash 编码的 score 可以让地理位置相近的点在 ZSet 里也排在一起。这意味着你可以用 ZRANGEBYSCORE 做"某个矩形区域内"的粗略检索,虽然不如 GEOSEARCH 灵活,但有些高性能场景下可以作为一种优化手段。
Bitfields 的原子自增特性,我强烈建议在限流、计数、库存这类高并发场景优先考虑。它比"GET 再 SET"安全得多,比 Lua 脚本更简单,性能也足够好。
最后一个小技巧:无论用哪种高级类型,上线前建议先在测试环境压一遍,重点观察大数据量下的内存变化和命令耗时。尤其 Bitmaps 和 Bitfields,它们表面上是 O(1) 操作,但底层字符串一旦膨胀,每次写入都涉及内存分配和可能的复制操作,量变引起质变,压测能帮你提前发现性能拐点。
我自己用了这么多年 Redis,最深的体会是:高级数据类型不是炫技用的,它们每一个都对应着一类具体的存储需求和取舍逻辑。把数据形态想清楚,再选择对应的底层结构,绝大部分性能问题和内存问题都是可以避免的。希望这篇整理出来的东西,能帮你少走一些我走过的弯路。
