redis是一种基于键值对的来存储数据的nosql数据库
其中对应的value支持string,list,set,zset,bitmaps,hyperloglog,GEO地理信息数据
常用的有String,list,set,zset,hash
- String类型是redis最基本类型之一,其存储数据方式为key和value的方式,其中value即为String类型,常用方式get set获取对应的key
- list是一种数据结构(链表),可以将list当成栈,也可以当成队列,常用指令lpush ,rpush
- set类型的数据类型与List数据类型对最大的不同就是不允许存在相同的元素(无序,不重复集合),常用指令sadd,sdiff set2 set3(求set2和set的差集),sinter set2 set3(求set2和set3的交集),sunion set2 set3(求set2和set3的并集)
- Hash表类型可以理解成一个Map集合,即Value是一个Map集合;常用指令hset map1 name zhangsan(添加一个元素),批量添加(hmset map1 age 15 gender male)
- zset类型在set的基础上增加了一个值(这个值可以用来排序),可以把Zset看成是有序set;常用指令zadd zset1 1 zhangsan(增加一个排序score)
使用场景
string 用来缓存存储一些简单的数据,比如用户名,密码,token,session等;
hash键值对存储,hset user:1 name zhangsan age 18 gender male
list 存储一些需要按照顺序存储的数据,比如消息队列,日志,缓存等;
set 存储一些需要去重且无序的数据,比如用户访问过的页面,用户访问过的商品等(抽奖系统:SADD 添加参与者,SPOP 随机抽取中奖者;标签/好友关系:存储用户标签,通过 SINTER 查找共同关注;去重统计:记录用户点赞、签到等唯一行为);
sort set 元素关联分数,按分数排序;如游戏积分,排行榜
底层数据结构
hash底层的数据结构是压缩列表ziplist或hashtable,键值对个数超过512 会由ziplist转换为hashtable
set底层的数据结构是intset或hashtable,元素为整数且键值对个数小于512为intset,若超过或不符合则转换为hashtable
zset底层数据结构为压缩列表ziplist或skiplist+dict(跳表+字典),元素数量小于128且元素长度小于64;超过此阈值则转换为跳表+字典
list的底层数据结构为压缩列表ziplist或快速列表quicklist,元素数量小于512且元素长度小于64用ziplist;超过此阈值则转换为quicklist,由多个ziplist节点组成的双向链表,可以平衡内存与性能
List
Redis-List是一个双端队列(Deque),适合顺序、队列、流式消费 场景,但不适合随机访问和复杂聚合
使用场景
对可靠性要求不高的异步任务,我们会用 Redis List 做轻量队列。例如记录某用户访问过的页面,或者记录用户访问过的商品,或者记录用户点赞、签到等唯一行为。
redission存储最近浏览的100个商品示例:
1 | public void recordSkuView(long userId, long skuId) { |
Set
redis的set是一个无序、不重复元素集合;底层是个intset(整数集合)或者是hashtable(hash表);
zset是一个有序的集合,支持排序和范围查询的Set;底层是一个dict(字典)+skiplist(跳表)
set的使用场景
例如,黑白名单,幂等校验,在线用户,活跃用户,订阅关系等;另外它可以快速取两个集合的交集
1 | // 获取vip和kyc的交集 |
zset的使用场景
排行榜 / Top N
1
2
3
4
5
6RScoredSortedSet<String> rank =
redisson.getScoredSortedSet("rank");
rank.add(100, "user1");
Collection<String> top10 =
rank.valueRangeReversed(0, 9);延迟队列(交易所/风控高频)
1
2
3
4
5
6
7RScoredSortedSet<String> delayQueue =
redisson.getScoredSortedSet("delay_queue");
delayQueue.add(executeTime, orderId);
// 拉取到期任务
Collection<String> tasks = delayQueue.valueRange(0, true, now, true);实时排序 / 统计
1
rank.addScore(userId, delta);
滑动时间窗口,只关注窗口内的数据
1
rank.removeRangeByScore(0, true, now - windowMs, true);
bitmaps
Bitmap = 用bit(0 / 1)表示状态的紧凑型数据结构
Bitmap 是基于String 实现的bit操作
使用场景
- 用户签到 / 活跃统计
1
2
3
4
5public void sign(long userId, int day) {
String key = "user:sign:" + userId + ":2026-02";
RBitSet bitSet = redisson.getBitSet(key);
bitSet.set(day - 1, true);
} - DAU(日活)/WAU(周活) MAU(月活)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23//记录活跃
public void markActive(long userId) {
String key = "dau:2026-02-12";
redisson.getBitSet(key).set(userId);
}
//判断是否活跃
public boolean isActive(long userId) {
return redisson.getBitSet("dau:2026-02-12").get(userId);
}
//统计 DAU
public long dauCount() {
return redisson.getBitSet("dau:2026-02-12").cardinality();
}
//MAU(多天 OR)
public long mauCount(List<String> dayKeys) {
RBitSet result = redisson.getBitSet("tmp:mau");
for (String key : dayKeys) {
result.or(redisson.getBitSet(key));
}
return result.cardinality();
}
//or() 会修改当前 BitSet,生产中建议用临时 key + TTL - 黑名单 / 风控标记(布隆过滤器)
1
2
3
4
5
6
7
8
9
10
11//封禁 / 解封
public void ban(long userId) {
redisson.getBitSet("user:blacklist").set(userId, true);
}
public void unban(long userId) {
redisson.getBitSet("user:blacklist").set(userId, false);
}
//判断是否封禁(QPS 很高的接口)
public boolean isBanned(long userId) {
return redisson.getBitSet("user:blacklist").get(userId);
} - 去重(不要求精确对象内容)
1
2
3
4
5
6//判断是否已领取空投
public boolean hasClaimed(long userId, long airdropId) {
String key = "airdrop:claimed:" + airdropId;
return redisson.getBitSet(key).get(userId);
}
//高并发下需 Lua 保证 check + set 原子 - Web3 / 交易所场景(综合)
1
2
3
4
5
6
7//是否参与launchpad
public void markJoined(long userId, long launchpadId) {
redisson.getBitSet("launchpad:join:" + launchpadId).set(userId);
}
public boolean hasJoined(long userId, long launchpadId) {
return redisson.getBitSet("launchpad:join:" + launchpadId).get(userId);
}总结
bitmaps的offset设计是生死线,否则会导致内存浪费,Bitmap不适合极稀疏数据
RBitSet 的and /or 会改自身,使用是需注意;可以用临时key,或设置TTL,或使用redis层copy
Redisson 通过 RBitSet 封装 Redis Bitmap,适合签到、DAU、风控、空投去重等状态型场景,具备 O(1) 判断和极高的空间效率,但需要谨慎设计 offset,避免稀疏数据导致内存浪费