Redis数据类型深度解析
Redis数据类型深度解析引言RedisRemote Dictionary Server是一个开源的、基于内存的数据结构存储系统广泛应用于缓存、消息队列、计数器、实时分析等场景。Redis支持多种数据类型包括字符串String、哈希Hash、列表List、集合Set、有序集合Sorted Set等还包括地理位置Geo、位图Bitmap、HyperLogLog等高级数据类型。深入理解这些数据类型及其适用场景是掌握Redis的关键。String类型1.1 基础操作String是Redis最基本的数据类型可以存储字符串、整数或浮点数。import org.springframework.data.redis.core.RedisTemplate; import org.springframework.beans.factory.annotation.Autowired; import java.util.concurrent.TimeUnit; public class RedisStringOperations { Autowired private RedisTemplateString, String redisTemplate; public void stringOperations() { String key user:1001; // 设置值 redisTemplate.opsForValue().set(key, John); // 设置值并指定过期时间 redisTemplate.opsForValue().set(token:12345, abc123, 30, TimeUnit.MINUTES); // 设置值仅当key不存在 redisTemplate.opsForValue().setIfAbsent(key, John, 10, TimeUnit.MINUTES); // 设置值仅当key存在 redisTemplate.opsForValue().setIfPresent(key, Jane); // 获取值 String value redisTemplate.opsForValue().get(key); // 批量设置 redisTemplate.opsForValue().multiSet( Map.of(key1, value1, key2, value2)); // 批量获取 ListString values redisTemplate.opsForValue().multiGet( Arrays.asList(key1, key2)); // 设置新值并返回旧值 String oldValue redisTemplate.opsForValue().getAndSet(key, NewJohn); // 追加字符串 redisTemplate.opsForValue().append(key, Doe); // 获取字符串长度 Long length redisTemplate.opsForValue().size(key); // 获取子字符串 String subString redisTemplate.opsForValue().get(key, 0, 3); System.out.println(Value: value); } }1.2 数值操作public class RedisNumericOperations { Autowired private RedisTemplateString, String redisTemplate; public void numericOperations() { String counterKey page:views:20240515; // 设置数值 redisTemplate.opsForValue().set(counterKey, 0); // 递增 Long increment redisTemplate.opsForValue().increment(counterKey); Long incrementBy redisTemplate.opsForValue().increment(counterKey, 5); // 递减 Long decrement redisTemplate.opsForValue().decrement(counterKey); Long decrementBy redisTemplate.opsForValue().decrement(counterKey, 3); // 浮点数递增 Double incrementFloat redisTemplate.opsForValue() .increment(price:item:001, 0.5); System.out.println(Counter value: redisTemplate.opsForValue().get(counterKey)); } }1.3 适用场景public class StringUseCases { // 1. 缓存用户信息 public void cacheUserProfile(String userId, UserProfile profile) { String key user:profile: userId; String value JSON.toJSONString(profile); redisTemplate.opsForValue().set(key, value, 30, TimeUnit.MINUTES); } public UserProfile getCachedUserProfile(String userId) { String key user:profile: userId; String value redisTemplate.opsForValue().get(key); return value ! null ? JSON.parseObject(value, UserProfile.class) : null; } // 2. 分布式锁 public boolean acquireLock(String resourceId, String lockValue, long expireTime) { String key lock: resourceId; Boolean acquired redisTemplate.opsForValue() .setIfAbsent(key, lockValue, expireTime, TimeUnit.SECONDS); return Boolean.TRUE.equals(acquired); } public void releaseLock(String resourceId, String lockValue) { String key lock: resourceId; String currentValue redisTemplate.opsForValue().get(key); if (lockValue.equals(currentValue)) { redisTemplate.delete(key); } } // 3. Session存储 public void storeSession(String sessionId, MapString, Object sessionData) { String key session: sessionId; String value JSON.toJSONString(sessionData); redisTemplate.opsForValue().set(key, value, 24, TimeUnit.HOURS); } // 4. 验证码存储 public void storeVerificationCode(String phone, String code) { String key verify:code: phone; redisTemplate.opsForValue().set(key, code, 5, TimeUnit.MINUTES); } public boolean verifyCode(String phone, String code) { String key verify:code: phone; String storedCode redisTemplate.opsForValue().get(key); return code.equals(storedCode); } }Hash类型2.1 基础操作Hash是一个键值对集合适合存储对象。public class RedisHashOperations { Autowired private RedisTemplateString, Object redisTemplate; public void hashOperations() { String key user:1001; // 设置单个字段 redisTemplate.opsForHash().put(key, name, John); redisTemplate.opsForHash().put(key, email, johnexample.com); redisTemplate.opsForHash().put(key, age, 30); // 批量设置字段 MapString, Object fields new HashMap(); fields.put(city, Beijing); fields.put(country, China); redisTemplate.opsForHash().putAll(key, fields); // 获取单个字段值 Object name redisTemplate.opsForHash().get(key, name); // 获取所有字段值 MapObject, Object allFields redisTemplate.opsForHash().entries(key); // 获取所有字段名 SetObject keys redisTemplate.opsForHash().keys(key); // 获取所有值 ListObject values redisTemplate.opsForHash().values(key); // 获取字段数量 Long size redisTemplate.opsForHash().size(key); // 判断字段是否存在 Boolean hasField redisTemplate.opsForHash().hasKey(key, name); // 删除字段 redisTemplate.opsForHash().delete(key, country); // 自增字段值 redisTemplate.opsForHash().increment(key, age, 1); redisTemplate.opsForHash().increment(key, score, 10.5); // 获取多个字段 ListObject fieldValues redisTemplate.opsForHash() .multiGet(key, Arrays.asList(name, email)); System.out.println(User data: allFields); } }2.2 适用场景public class HashUseCases { // 1. 存储用户对象 public void saveUser(User user) { String key user: user.getId(); MapString, Object userMap new HashMap(); userMap.put(id, user.getId()); userMap.put(name, user.getName()); userMap.put(email, user.getEmail()); userMap.put(createdAt, user.getCreatedAt().toString()); redisTemplate.opsForHash().putAll(key, userMap); } public User getUser(Long userId) { String key user: userId; MapObject, Object userMap redisTemplate.opsForHash().entries(key); if (userMap.isEmpty()) { return null; } User user new User(); user.setId((String) userMap.get(id)); user.setName((String) userMap.get(name)); user.setEmail((String) userMap.get(email)); return user; } // 2. 购物车存储 public void addToCart(String userId, String productId, int quantity) { String key cart: userId; // 商品不存在则设置数量存在则增加 if (quantity 0) { redisTemplate.opsForHash().put(key, productId, String.valueOf(quantity)); } else { redisTemplate.opsForHash().delete(key, productId); } } public MapString, Integer getCart(String userId) { String key cart: userId; MapObject, Object cart redisTemplate.opsForHash().entries(key); MapString, Integer result new HashMap(); cart.forEach((k, v) - result.put((String) k, Integer.parseInt((String) v))); return result; } public void clearCart(String userId) { String key cart: userId; redisTemplate.delete(key); } // 3. 计数器存储 public void incrementCounter(String date, String metric, long delta) { String key stats: date; redisTemplate.opsForHash().increment(key, metric, delta); } public Long getCounter(String date, String metric) { String key stats: date; Object value redisTemplate.opsForHash().get(key, metric); return value ! null ? Long.parseLong((String) value) : 0L; } }List类型3.1 基础操作List是一个有序的字符串列表支持在头部或尾部添加元素。public class RedisListOperations { Autowired private RedisTemplateString, String redisTemplate; public void listOperations() { String key mylist; // 头部添加 redisTemplate.opsForList().leftPush(key, a); redisTemplate.opsForList().leftPush(key, b); // 尾部添加 redisTemplate.opsForList().rightPush(key, c); redisTemplate.opsForList().rightPush(key, d); // 批量添加 redisTemplate.opsForList().rightPushAll(key, Arrays.asList(e, f, g)); // 获取列表长度 Long size redisTemplate.opsForList().size(key); // 获取指定范围的元素 ListString range redisTemplate.opsForList().range(key, 0, -1); // 获取单个元素 String element redisTemplate.opsForList().index(key, 0); // 头部弹出 String leftPop redisTemplate.opsForList().leftPop(key); // 尾部弹出 String rightPop redisTemplate.opsForList().rightPop(key); // 带超时的阻塞弹出 String blockingPop redisTemplate.opsForList() .leftPop(key, 10, TimeUnit.SECONDS); // 设置指定位置的值 redisTemplate.opsForList().set(key, 0, new_a); // 裁剪列表 redisTemplate.opsForList().trim(key, 0, 4); // 获取列表指定位置的元素 ListString subList redisTemplate.opsForList().range(key, 1, 3); // 列表间移动 redisTemplate.opsForList().rightPopAndLeftPush( sourceList, targetList); System.out.println(List content: range); } }3.2 适用场景public class ListUseCases { // 1. 最新消息列表 public void addMessage(String userId, String message) { String key user: userId :messages; // 添加到列表头部 redisTemplate.opsForList().leftPush(key, message); // 保持只保留最新100条消息 redisTemplate.opsForList().trim(key, 0, 99); } public ListString getLatestMessages(String userId, int count) { String key user: userId :messages; return redisTemplate.opsForList().range(key, 0, count - 1); } // 2. 任务队列 public void enqueueTask(String queueName, String taskData) { String key queue: queueName; redisTemplate.opsForList().rightPush(key, taskData); } public String dequeueTask(String queueName) { String key queue: queueName; return redisTemplate.opsForList().leftPop(key); } // 3. 关注列表 public void followUser(String userId, String targetUserId) { String key following: userId; redisTemplate.opsForList().rightPush(key, targetUserId); } public ListString getFollowing(String userId) { String key following: userId; return redisTemplate.opsForList().range(key, 0, -1); } // 4. 消息队列实现 public class MessageQueue { public void produce(String queueName, String message) { String key mq: queueName; redisTemplate.opsForList().rightPush(key, message); } public String consume(String queueName) { String key mq: queueName; return redisTemplate.opsForList().leftPop(key); } public String blockingConsume(String queueName, long timeout, TimeUnit unit) { String key mq: queueName; return redisTemplate.opsForList().leftPop(key, timeout, unit); } } }Set类型4.1 基础操作Set是一个无序且不重复的字符串集合。public class RedisSetOperations { Autowired private RedisTemplateString, String redisTemplate; public void setOperations() { String key myset; // 添加成员 redisTemplate.opsForSet().add(key, a, b, c, d); // 批量添加 redisTemplate.opsForSet().add(key, new String[]{e, f, g}); // 获取集合所有成员 SetString members redisTemplate.opsForSet().members(key); // 判断是否为成员 Boolean isMember redisTemplate.opsForSet().isMember(key, a); // 获取集合大小 Long size redisTemplate.opsForSet().size(key); // 随机获取成员 String randomMember redisTemplate.opsForSet().randomMember(key); // 随机获取多个不重复成员 SetString randomMembers redisTemplate.opsForSet().distinctRandomMembers(key, 3); // 随机获取多个可能重复的成员 ListString randomMembersWithRepeat redisTemplate.opsForSet().randomMembers(key, 3); // 移除成员 redisTemplate.opsForSet().remove(key, a, b); // 获取两个集合的差集 SetString diff redisTemplate.opsForSet().difference( set1, set2); // 差集并存储 redisTemplate.opsForSet().differenceAndStore( set1, set2, set3); // 获取两个集合的交集 SetString intersect redisTemplate.opsForSet().intersect(set1, set2); // 交集并存储 redisTemplate.opsForSet().intersectAndStore( set1, set2, set4); // 获取两个集合的并集 SetString union redisTemplate.opsForSet().union(set1, set2); // 并集并存储 redisTemplate.opsForSet().unionAndStore( set1, set2, set5); // 移动成员到另一个集合 redisTemplate.opsForSet().move(source, destination, member); System.out.println(Set members: members); } }4.2 适用场景public class SetUseCases { // 1. 标签系统 public void addTags(String entityId, String... tags) { String key entity:tags: entityId; redisTemplate.opsForSet().add(key, tags); } public SetString getTags(String entityId) { String key entity:tags: entityId; return redisTemplate.opsForSet().members(key); } public void removeTag(String entityId, String tag) { String key entity:tags: entityId; redisTemplate.opsForSet().remove(key, tag); } // 2. 共同关注 public SetString getCommonFollowing(String user1, String user2) { String key1 following: user1; String key2 following: user2; return redisTemplate.opsForSet().intersect(key1, key2); } // 3. 推荐关注 public SetString recommendFollow(String userId) { String userFollowing following: userId; SetString allUsers redisTemplate.opsForSet().members(all:users); SetString following redisTemplate.opsForSet().members(userFollowing); allUsers.removeAll(following); return allUsers; } // 4. UV统计 public void recordUV(String date, String visitorId) { String key uv: date; redisTemplate.opsForSet().add(key, visitorId); } public long getUVCount(String date) { String key uv: date; return redisTemplate.opsForSet().size(key); } // 5. 抽奖系统 public void addParticipants(String activityId, String... userIds) { String key lottery: activityId; redisTemplate.opsForSet().add(key, userIds); } public String drawWinner(String activityId) { String key lottery: activityId; return redisTemplate.opsForSet().randomMember(key); } public SetString drawWinners(String activityId, int count) { String key lottery: activityId; return redisTemplate.opsForSet().distinctRandomMembers(key, count); } }Sorted Set类型5.1 基础操作Sorted Set是一个带分数的有序集合每个元素都有对应的分数用于排序。public class RedisZSetOperations { Autowired private RedisTemplateString, String redisTemplate; public void zsetOperations() { String key myzset; // 添加成员 redisTemplate.opsForZSet().add(key, member1, 100); redisTemplate.opsForZSet().add(key, member2, 200); redisTemplate.opsForZSet().add(key, member3, 150); // 批量添加 SetZSetOperations.TypedTupleString tuples new HashSet(); tuples.add(ZSetOperations.TypedTuple.of(member4, 180.0)); tuples.add(ZSetOperations.TypedTuple.of(member5, 250.0)); redisTemplate.opsForZSet().add(key, tuples); // 获取成员分数 Double score redisTemplate.opsForZSet().score(key, member1); // 获取成员排名从小到大0开始 Long rank redisTemplate.opsForZSet().rank(key, member1); // 获取成员排名从大到小 Long reverseRank redisTemplate.opsForZSet().reverseRank(key, member1); // 获取指定分数范围的成员 SetString rangeByScore redisTemplate.opsForZSet() .rangeByScore(key, 100, 200); // 获取指定分数范围的成员及分数 SetZSetOperations.TypedTupleString rangeWithScores redisTemplate.opsForZSet().rangeByScoreWithScores( key, 100, 200); // 获取指定索引范围的成员 SetString rangeByRank redisTemplate.opsForZSet().range(key, 0, 9); // 获取指定索引范围的成员及分数 SetZSetOperations.TypedTupleString rangeWithScoresByRank redisTemplate.opsForZSet().rangeWithScores(key, 0, 9); // 获取成员数量 Long size redisTemplate.opsForZSet().size(key); // 获取指定分数范围的成员数量 Long count redisTemplate.opsForZSet().count(key, 100, 200); // 递增分数 redisTemplate.opsForZSet().incrementScore(key, member1, 10); // 移除成员 redisTemplate.opsForZSet().remove(key, member1, member2); // 移除指定排名范围的成员 redisTemplate.opsForZSet().removeRange(key, 0, 9); // 移除指定分数范围的成员 redisTemplate.opsForZSet().removeRangeByScore(key, 0, 99); System.out.println(Members: rangeByScore); } }5.2 适用场景public class ZSetUseCases { // 1. 排行榜系统 public void updateScore(String leaderboardKey, String userId, double score) { redisTemplate.opsForZSet().add(leaderboardKey, userId, score); } public Long getUserRank(String leaderboardKey, String userId) { return redisTemplate.opsForZSet().reverseRank(leaderboardKey, userId); } public Double getUserScore(String leaderboardKey, String userId) { return redisTemplate.opsForZSet().score(leaderboardKey, userId); } public ListMapString, Object getTopN(String leaderboardKey, int n) { SetZSetOperations.TypedTupleString topN redisTemplate.opsForZSet().reverseRangeWithScores( leaderboardKey, 0, n - 1); ListMapString, Object result new ArrayList(); int rank 1; for (ZSetOperations.TypedTupleString tuple : topN) { MapString, Object item new HashMap(); item.put(rank, rank); item.put(userId, tuple.getValue()); item.put(score, tuple.getScore()); result.add(item); } return result; } // 2. 带权重的任务调度 public void scheduleTask(String taskId, long priority, long timestamp) { String key task:scheduled: timestamp; double score priority * 10000000000L (Long.MAX_VALUE - timestamp); redisTemplate.opsForZSet().add(key, taskId, score); } public String pollTask() { SetString tasks redisTemplate.opsForZSet() .range(task:scheduled:*, 0, 0); if (!tasks.isEmpty()) { String taskId tasks.iterator().next(); redisTemplate.opsForZSet().remove(task:scheduled:*, taskId); return taskId; } return null; } // 3. 延时队列 public void addDelayTask(String taskId, long delayMs) { String key delay:queue; long executeTime System.currentTimeMillis() delayMs; redisTemplate.opsForZSet().add(key, taskId, executeTime); } public String pollDelayTask() { String key delay:queue; long now System.currentTimeMillis(); SetString tasks redisTemplate.opsForZSet() .rangeByScore(key, 0, now); if (!tasks.isEmpty()) { String taskId tasks.iterator().next(); redisTemplate.opsForZSet().remove(key, taskId); return taskId; } return null; } // 4. 用户最近访问记录按时间排序 public void recordAccess(String userId, String resourceId) { String key access: userId; double timestamp System.currentTimeMillis(); redisTemplate.opsForZSet().add(key, resourceId, timestamp); // 只保留最近100条记录 redisTemplate.opsForZSet().removeRange(key, 0, -101); } public ListString getRecentAccess(String userId, int limit) { String key access: userId; SetString accesses redisTemplate.opsForZSet() .reverseRange(key, 0, limit - 1); return accesses ! null ? new ArrayList(accesses) : Collections.emptyList(); } }高级数据类型6.1 BitMappublic class RedisBitmapOperations { Autowired private RedisTemplateString, String redisTemplate; public void bitmapOperations() { String key user:online; // 设置某位为1 redisTemplate.opsForValue().setBit(key, 1001, true); redisTemplate.opsForValue().setBit(key, 1002, true); redisTemplate.opsForValue().setBit(key, 1003, true); // 设置某位为0 redisTemplate.opsForValue().setBit(key, 1002, false); // 获取某位的值 Boolean bit redisTemplate.opsForValue().getBit(key, 1001); // 统计1的个数 Long count redisTemplate.opsForValue().bitCount(key); // 统计指定范围的1的个数 Long rangeCount redisTemplate.opsForValue() .bitCount(key, 0, 1000); // 位运算AND redisTemplate.opsForValue().bitOp( RedisStringCommands.BitOperation.AND, result, key1, key2); // 位运算OR redisTemplate.opsForValue().bitOp( RedisStringCommands.BitOperation.OR, result, key1, key2); // 获取位运算结果 Long bitCount redisTemplate.opsForValue().bitCount(result); // 获取第一个为1的位位置 Long firstSetBit redisTemplate.opsForValue().getBit(key, 0); } // 用户签到统计 public void signIn(String userId, String date) { String key sign: userId : date.substring(0, 6); int day Integer.parseInt(date.substring(6)); redisTemplate.opsForValue().setBit(key, day - 1, true); } public boolean checkSignIn(String userId, String date) { String key sign: userId : date.substring(0, 6); int day Integer.parseInt(date.substring(6)); return Boolean.TRUE.equals( redisTemplate.opsForValue().getBit(key, day - 1)); } public long getSignInCount(String userId, String yearMonth) { String key sign: userId : yearMonth; return redisTemplate.opsForValue().bitCount(key); } }6.2 HyperLogLogpublic class RedisHyperLogLogOperations { Autowired private RedisTemplateString, String redisTemplate; public void hyperLogLogOperations() { String key uv:hll; // 添加元素 redisTemplate.opsForHyperLogLog().add(key, user1, user2, user3); redisTemplate.opsForHyperLogLog().add(key, user4, user5); // 获取基数估算值 Long size redisTemplate.opsForHyperLogLog().size(key); // 合并多个HyperLogLog String key2 uv:hll:2; redisTemplate.opsForHyperLogLog().add(key2, user6, user7); redisTemplate.opsForHyperLogLog().union(merged, key, key2); Long mergedSize redisTemplate.opsForHyperLogLog().size(merged); System.out.println(Estimated cardinality: size); } // 每日UV统计 public void recordUV(String date, String visitorId) { String key uv: date; redisTemplate.opsForHyperLogLog().add(key, visitorId); } public long getUV(String date) { String key uv: date; return redisTemplate.opsForHyperLogLog().size(key); } public long getRangeUV(String startDate, String endDate) { ListString keys new ArrayList(); for (String date startDate; date.compareTo(endDate) 0; date nextDate(date)) { keys.add(uv: date); } String mergedKey uv: startDate _ endDate; redisTemplate.opsForHyperLogLog().union( mergedKey, keys.toArray(new String[0])); return redisTemplate.opsForHyperLogLog().size(mergedKey); } private String nextDate(String date) { LocalDate d LocalDate.parse(date, DateTimeFormatter.ofPattern(yyyyMMdd)); return d.plusDays(1).format( DateTimeFormatter.ofPattern(yyyyMMdd)); } }总结Redis提供了丰富的数据类型每种类型都有其独特的特性和适用场景。深入理解这些数据类型并结合业务场景合理使用能够充分发挥Redis的性能优势。在实际应用中需要根据数据的访问模式、一致性要求和性能要求选择最合适的数据类型和操作方式。