Redis五种数据类型与应用场景 Redis 的五种数据结构各有特色,用对了才能发挥它的优势。很多人只用到了 String 和 Hash,却不知道 List、Set、ZSet 在特定场景下更合适。本文从应用场景出发,讲什么时候用什么类型。
一、String(字符串) #
1.1 基本特点
最基本的数据类型,一个key对应一个value
最大可存储512MB
可以存储字符串、整数、浮点数
#
1.2 常用命令 SET key value GET key DEL key EXISTS key MSET key1 v1 key2 v2 MGET key1 key2 INCR key DECR key INCRBY key 5 SETEX key 60 value SET key value EX 60 TTL key
#
1.3 应用场景 场景一:缓存
String userJson = redis.get("user:1001" );if (userJson == null ) { User user = userMapper.findById(1001L ); redis.setex("user:1001" , 3600 , JSON.toJSONString(user)); return user; } return JSON.parseObject(userJson, User.class);
场景二:计数器
redis.incr("article:read:10001" ); String key = "rate_limit:" + userId;Long count = redis.incr(key);if (count == 1 ) { redis.expire(key, 60 ); } if (count > 100 ) { throw new RateLimitException ("请求过于频繁" ); }
场景三:分布式锁
String key = "lock:order:10001" ;String value = UUID.randomUUID().toString();Boolean locked = redis.set(key, value, "NX" , "EX" , 30 );if (locked) { try { } finally { String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " + "return redis.call('del', KEYS[1]) else return 0 end" ; redis.eval(script, Collections.singletonList(key), Collections.singletonList(value)); } }
场景四:Session存储
redis.setex("session:" + sessionId, 1800 , userId);
二、Hash(哈希) #
2.1 基本特点
键值对的集合,适合存储对象
每个Hash可存储约40亿个键值对
相比String序列化,更节省空间,支持字段级操作
#
2.2 常用命令 HSET user:1001 name "Alice" HGET user:1001 name HMSET user:1001 name "Alice" age 25 city "Beijing" HGETALL user:1001 HDEL user:1001 city HLEN user:1001 HINCRBY user:1001 age 1 HEXISTS user:1001 name HKEYS user:1001 HVALS user:1001
#
2.3 应用场景 场景一:存储对象
Map<String, String> userMap = new HashMap <>(); userMap.put("name" , "Alice" ); userMap.put("age" , "25" ); userMap.put("city" , "Beijing" ); redis.hsetAll("user:1001" , userMap); String name = redis.hget("user:1001" , "name" );redis.hset("user:1001" , "age" , "26" );
场景二:购物车
redis.hset("cart:user:1001" , "sku:2001" , "2" ); redis.hset("cart:user:1001" , "sku:2002" , "1" ); redis.hincrBy("cart:user:1001" , "sku:2001" , 1 ); Map<String, String> cart = redis.hgetAll("cart:user:1001" ); redis.hdel("cart:user:1001" , "sku:2001" );
场景三:配置信息
redis.hset("config:app" , "max_upload_size" , "10485760" ); redis.hset("config:app" , "default_timeout" , "30" ); redis.hset("config:app" , "retry_times" , "3" ); String timeout = redis.hget("config:app" , "default_timeout" );
String vs Hash存储对象对比 :
方式
优点
缺点
String(JSON)
序列化简单
修改需整体更新,空间占用大
Hash
字段级操作,节省空间
复杂对象不便存储
三、List(列表) #
3.1 基本特点
双向链表,有序可重复
两端插入和删除都是O(1)
支持阻塞操作
#
3.2 常用命令 LPUSH list value RPUSH list value LPOP list RPOP list LRANGE list 0 -1 LLEN list LINDEX list 0 LREM list 1 value LTRIM list 0 99 BLPOP list 30 BRPOP list1 list2 30
#
3.3 应用场景 场景一:消息队列
redis.lpush("queue:email" , emailJson); while (running) { List<String> result = redis.brpop(30 , "queue:email" ); if (result != null ) { String emailJson = result.get(1 ); processEmail(emailJson); } }
场景二:最新列表
redis.lpush("articles:latest" , articleId); redis.ltrim("articles:latest" , 0 , 99 ); List<String> latestArticles = redis.lrange("articles:latest" , 0 , 9 );
场景三:时间线/动态
redis.lpush("timeline:user:1001" , momentJson); redis.ltrim("timeline:user:1001" , 0 , 999 ); List<String> moments = redis.lrange("timeline:user:1001" , 0 , 20 );
场景四:栈和队列
redis.lpush("stack" , value); redis.lpop("stack" ); redis.rpush("queue" , value); redis.lpop("queue" );
四、Set(集合) #
4.1 基本特点
无序不重复集合
支持集合运算(交、并、差)
判断元素是否存在O(1)
#
4.2 常用命令 SADD set value SREM set value SMEMBERS set SISMEMBER set value SCARD set SPOP set SRANDMEMBER set 3 SINTER set1 set2 SUNION set1 set2 SDIFF set1 set2 SINTERSTORE result set1 set2
#
4.3 应用场景 场景一:标签系统
redis.sadd("article:tags:10001" , "Java" , "Redis" , "缓存" ); Set<String> tags = redis.smembers("article:tags:10001" ); redis.sinter("tag:Java" , "tag:Redis" );
场景二:共同好友
redis.sadd("friends:user:1001" , "2001" , "2002" , "2003" ); redis.sadd("friends:user:1002" , "2002" , "2003" , "2004" ); Set<String> common = redis.sinter("friends:user:1001" , "friends:user:1002" ); Set<String> suggest = redis.sdiff("friends:user:1002" , "friends:user:1001" );
场景三:抽奖/随机
redis.sadd("lottery:activity:1" , "user1" , "user2" , "user3" , ...); Set<String> winners = redis.srandmember("lottery:activity:1" , 3 ); List<String> winners = new ArrayList <>(); for (int i = 0 ; i < 3 ; i++) { String winner = redis.spop("lottery:activity:1" ); winners.add(winner); }
场景四:黑名单/白名单
redis.sadd("blacklist:ip" , "192.168.1.100" , "10.0.0.50" ); Boolean blocked = redis.sismember("blacklist:ip" , clientIp);
五、ZSet(有序集合) #
5.1 基本特点
每个元素关联一个分数(score)
按分数排序,分数可重复
支持按分数范围查询
#
5.2 常用命令 ZADD zset score member ZREM zset member ZSCORE zset member ZRANGE zset 0 -1 ZREVRANGE zset 0 -1 ZRANGEBYSCORE zset 0 100 ZCARD zset ZCOUNT zset 0 100 ZINCRBY zset 1 member ZRANK zset member ZREVRANK zset member ZREMRANGEBYRANK zset 0 99 ZREMRANGEBYSCORE zset 0 100
#
5.3 应用场景 场景一:排行榜
redis.zincrby("leaderboard:weekly" , 10 , "user:1001" ); Set<Tuple> top10 = redis.zrevrangeWithScores("leaderboard:weekly" , 0 , 9 ); Long rank = redis.zrevrank("leaderboard:weekly" , "user:1001" );Double score = redis.zscore("leaderboard:weekly" , "user:1001" );
场景二:延时队列
redis.zadd("delay:queue" , System.currentTimeMillis() + 60000 , taskJson); while (running) { Set<String> tasks = redis.zrangeByScore("delay:queue" , 0 , System.currentTimeMillis(), 0 , 10 ); for (String task : tasks) { Long removed = redis.zrem("delay:queue" , task); if (removed > 0 ) { processTask(task); } } Thread.sleep(1000 ); }
场景三:滑动窗口限流
public boolean rateLimit (String userId, int maxRequests, int windowSeconds) { String key = "rate_limit:" + userId; long now = System.currentTimeMillis(); long windowStart = now - windowSeconds * 1000 ; redis.zremrangeByScore(key, 0 , windowStart); Long count = redis.zcard(key); if (count >= maxRequests) { return false ; } redis.zadd(key, now, String.valueOf(now)); redis.expire(key, windowSeconds); return true ; }
场景四:热门文章/商品
double score = System.currentTimeMillis() / 1000.0 ;redis.zadd("articles:hot" , score, "article:10001" ); redis.zremrangeByRank("articles:hot" , 0 , -1001 ); Set<String> hotArticles = redis.zrevrange("articles:hot" , 0 , 9 );
六、数据类型选择指南
需求
推荐类型
原因
简单键值缓存
String
最简单
存储对象
Hash
字段级操作,节省空间
队列/栈
List
双向链表,天然支持
去重/集合运算
Set
无序不重复
排序/排名
ZSet
按分数排序
计数器
String
INCR原子操作
分布式锁
String
SET NX EX
最新消息
List
LPUSH + LTRIM
共同关注
Set
SINTER
排行榜
ZSet
自动排序
七、总结 Redis五种数据类型各有特点:
类型
底层结构
特点
典型应用
String
SDS
简单高效
缓存、计数器、锁
Hash
ziplist/hashtable
字段级操作
对象存储、购物车
List
quicklist
双向链表
队列、时间线
Set
intset/hashtable
无序不重复
标签、共同好友
ZSet
ziplist/skiplist
有序集合
排行榜、延时队列
理解每种数据类型的特性和适用场景,才能在实际开发中做出正确的选择,发挥Redis的最大价值。
核心要点
String:简单的键值对,适合缓存、计数器
Hash:存储对象属性,适合用户信息、配置
List:有序列表,适合消息队列、最新列表
Set:无序去重,适合共同好友、抽奖
ZSet:有序集合,适合排行榜、积分系统
总结 选择合适的数据结构是使用 Redis 的关键。在实际项目中,根据业务需求选择合适的类型,可以提升性能和开发效率。