【主流技术】一文掌握 Redis 在 Java 中的 5 大基本结构应用
前言
Redis 是目前互联网后端的热门中间件之一,在许多方面都有深度的应用,作为后端开发熟练掌握该技术是十分有必要的。
Redis 的五种数据类型是:1、String(字符串);2、Hash(哈希);3、List(列表);4、Set(集合);5、Sort Set (有序集合)。其余的用的比较少,本文暂不涉及。
其中,String(字符串)是 Redis 中最基本的数据类型,一个 Key 对应一个 Value。其它的几个常用结构如下简图所示:
关于 Redis 的安装搭建和在 Linux 中的原生命令操作,可以见我的另一篇文章:https://www.cnblogs.com/Apluemxa/p/16465276.html
注:以下内容都是基于 RedisTemplate 在项目中的实际使用进行说明。
-
引入 pom 依赖
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
-
新建序列化配置类
@Configuration public class RedisTemplateConfiguration { //需要单独声明该 Bean 的 name,否则使用 JDK 自带的序列化配置会导致显示乱码 @Bean(name = "redisTemplate") public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<String , Object> template = new RedisTemplate<>(); RedisSerializer<String> redisSerializer = new StringRedisSerializer(); template.setConnectionFactory(redisConnectionFactory); //key 序列化 template.setKeySerializer(redisSerializer); //value 序列化 template.setValueSerializer(redisSerializer); //value 的 hash 序列化 template.setHashValueSerializer(redisSerializer); //key 的 hash 序列化 template.setHashKeySerializer(redisSerializer); return template; } }
-
新建 RedisTemplate 工具类
作用是封装一些常用的方法(只给两个简单的方法示例,直接注入 RedisTemplate 效果也一样),同时在方法里进行一些判空、异常捕获、输出日志等操作。
@Slf4j @Component public class RedisTemplateUtils { @Resource private RedisTemplate<String, Object> redisTemplate; /** * 放入普通缓存,并设置过期时间 * @param key 键 * @param value 值 * @param expireTime 过期时间(秒) ,time 要大于0,如果小于等于0,将设置无限期 * @return true 成功,false 失败 */ public Boolean set(String key, Object value, long expireTime) { try { if (expireTime > 0) { redisTemplate.opsForValue().set(key, value, expireTime, TimeUnit.SECONDS); } else { set(key, value); } return Boolean.TRUE; } catch (Exception e) { log.error("exception when set Redis key {}. ", key, e); return Boolean.FALSE; } } /** * 获取普通缓存 * @param key 键 * @return Object 类型的值 */ public Object get(String key) { return Optional.ofNullable(redisTemplate.opsForValue().get(key)).orElse(null); } }
一、String 类型
字符串类型是 Redis 中最基本的数据存储类型,它是一个由字节组成的序列,在 Rediss 中是二进制安全的。这意味着该类型可以接受任何格式数据,如 JPEG 图像链接数据和 Json 对象格式的信息等等。
它是标准的 key-value,通常用于存储字符串、整数和浮点。其中单个 value 内可容纳最高达512MB的数据。
由于所有数据都在单个对象中,Redis 中的字符串操作速度非常快。基本的
Redis 命令(如 SET
、GET
和 DEL
)允许对字符串值执行一些基本操作:
SET 键值
– 设置指定键的值。GET 键
– 检索指定键的值。DEL 键
– 删除给定键的值。
@Resource
private RedisTemplateUtils redisTemplateUtils;
/**
* String 类型的存/取
* @return
*/
@Override
public String testStringType() {
String redisStr = "";
//设置键、值以及过期时间30分钟
if (redisTemplateUtils.set(SysConstant.TEST_REDIS_KEY, UUIDUtils.generateUUID(), 1800)){
//由于获得的值是 Object 类型的,所以需要强转成 String 类型
redisStr = (String) redisTemplateUtils.get(SysConstant.TEST_REDIS_KEY);
System.out.println(redisStr);
}
return redisStr;
}
注:其中需要先注入 RedisTemplateUtils 来保证引入 RedisTemplate。
Redis 的可视化客户端(我用的是Another Redis Desktop Manager,开源免费)中的数据可以看到已经写入了,如下图1-1所示:
常见的应用场景:存储用户 token 信息、缓存热点关键数据、统计站点访问量、计算当前在线人数等。
二、List 类型
Redis 列表是简单的字符串列表,按照插入顺序排序。我们可以添加一个元素到列表的头部(左边)或者尾部(右边)。
Redis 的列表允许用户从列表的两端推入或者弹出元素,列表由多个字符串值组成的有序可重复的序列,是一个链表的结构,所以向列表两端添加元素的时间复杂度为O(1),获取越接近两端的元素速度就越快。
这意味着,即使有数以千万计的元素列表,也可以极快地速度获得10条在头部或者尾部的记录。
该种类型的字符串链表可以执行一些常见的基本操作,例如:
leftPushAll
– 将值从左边推送到列表(倒序)。rightPushAll
– 将值从右边推送到列表(顺序)。RANGE
– 根据 key 获取 value。LPOP/RPOP
– 用于显示和删除列表两端的值。LINDEX
– 获取列表中指定位置(下标)的值。
@Resource
private StudyMapper studyMapper;
//为了方便举例,这里直接用 RedisTemplate 实现,就不用自己封装的工具类了,效果是一样的
@Resource
private RedisTemplate<String, String> redisTemplate;
/**
* 测试 List 类型
* @return
*/
@Override
public List<String> testListType() {
LambdaQueryWrapper<Study> wrapper = new LambdaQueryWrapper<>();
//先将数据库的实体类型列表转换为 String 类型
List<String> stringList = studyMapper.selectList(wrapper).stream()
.map(val -> val.convertExt(String.class))
.collect(Collectors.toList());
//数据先存后取
if (redisTemplate.opsForList().leftPushAll(SysConstant.TEST_REDIS_LIST_KEY, stringList) > NumberUtils.LONG_ZERO){
//参数为0和-1,代表取出所有值
List<String> list = redisTemplate.opsForList().range(SysConstant.TEST_REDIS_LIST_KEY,0,-1);
return list;
}
return null;
}
在 Redis 可视化客户端的数据如下图2-1所示:
常见的应用场景:xx最新排行榜前十;消息队列(订阅/发布模式)等。
三、Hash 类型
Redis hash 是一个键值对(其中 key 数大于等于 value 数)的集合。Redis hash 是一个 field 和 value 都为 String 类型的映射表,hash 特别适合用于存储集合对象。
Redis的Hash结构可以使你像在数据库中 update 一个属性那样,只修改某一项属性值。和 String 有点像,但 value 中存放的是一张表,一般用于多个个体的详细事项排列,String 也可以做到,但要比 hash 麻烦许多。
以下是该结构相关的一些方法,允许更改单个或多个字段:
HSET
– 根据键向哈希表放入数据。HGET
–根据 key 获取各个值。HGETALL
– 获取整个哈希表的内容。HDEL
– 从哈希中删除现有的键值对。
@Resource
private ShoppingCarMapper shoppingCarMapper;
/**
* 测试 Hash 类型
* @return
*/
@Override
public Map<Object, Object> testHashType() {
LambdaQueryWrapper<ShoppingCar> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ShoppingCar::getUserId,"1656698374114156635");
//组装一个 key 为 商品Id、value 为该用户购物车信息 list 的 Map 对象
Map<String, String> hashMap = new HashMap<>();
shoppingCarMapper.selectList(wrapper).forEach(val -> hashMap.put(val.getGoodId(), val.convertExt(String.class)));
//redis 的 key 为用户 userId
if(redisTemplateUtils.hset("1656698374114156635",hashMap,1800)){
return redisTemplateUtils.hget("1656698374114156635");
}
return null;
}
在 Redis 可视化客户端的数据如下图3-1所示:
常见的应用场景:频繁更改的数据,如用户的购物车、用户会话信息等;适合使用 Hash 结构存储的数据:如城市与所处经纬度、城市与所处的学校等。
四、Set 结构
Redis 的 Set 是 String 类型的无序不重复集合。Set的底层是借助哈希表来实现的,所以添加、删除、查找的复杂度都是 O(1)。
所谓 Set 集合就是一堆不重复值的组合,并且这些值的摆放是没有顺序的。
如:在微博应用中,可以将某个用户所有的关注人存入一个集合中,将其所有的粉丝存入另一个集合。
Redis还提供了诸如collection、union和differences等操作,使得实现诸如commandism、poperhike、secondfriends这样的功能变得很容易,或者可以选择将结果返回,还是将它们保存到新的集合中。
可以使用以下命令添加、删除、检索和检查等,对集合中的内容进行操作:
SADD
– 向集合中添加一个或多个元素。SISMEMBER
– 判断set集合中是否包含指定值。SMEMBERS
– 根据 key 获取集合中所有元素。SREM
– 从集合中删除现有元素。
@Resource
private UserMapper userMapper;
/**
* 测试 Set 类型
* @return
*/
@Override
public Set<Object> testSetType() {
//这里从数据库中构建出一个 String[]
String[] strs = userMapper.selectList(new LambdaQueryWrapper<User>()
.select(User::getUnionId)).stream()
.map(val -> val.convertExt(String.class)).toArray(String[]::new);
//可以放单个的 String 字符串,也可以一次性放一个 String[]
if (redisTemplateUtils.sSet(SysConstant.TEST_REDIS_SET_KEY, strs) > NumberUtils.LONG_ZERO){
Set<Object> result = redisTemplateUtils.sGet(SysConstant.TEST_REDIS_SET_KEY);
return result;
}
return null;
}
在 Redis 可视化客户端的数据如下图4-1所示:
常见的应用场景:判断用户是否在线(结合过期时间)、记录文章或者商品的标签(结合去重)、交集/并集寻找共同好友等。
五、Sort Set (Zset)结构
Sorted Set 也叫 Zset ,和 Set 一样也是 String 类型元素的集合,且不允许重复的成员。不同的是每个元素都会携带一个 Double 类型的双精度浮点数。
Zset 正是通过上述的分数来为集合中的成员进行从小到大的排序。Zset的 value 是唯一的,但分数(权重)却可以重复。
以下的一些基本命令可以根据 value 或分数大小进行获取、添加、删除、检索等的操作:
ZADD
– 将具有分数的元素添加到集合。ZRANGE
– 获取经过的排序的集合。withscores
选项生成实际分数值(从小到大排列)。ZRANGEBYSCORE
– 按照定义的分数范围从集合中获取元素。withscores
选项生成实际分数值。ZREM
–从已排序的集中删除元素。
@Resource
private ShoppingCarMapper shoppingCarMapper;
/**
* 测试 ZSet 类型
* @return
*/
@Override
public Set<String> testZSetType() {
//这里先初始化一个 Redis 中对 ZSet 专门设置的类型对象,其中一个元素是 value,另一个是 Double 类型的 Score 分数
Set<ZSetOperations.TypedTuple<String>> typedTupleSet = new HashSet<>();
shoppingCarMapper.selectList(new LambdaQueryWrapper<ShoppingCar>()
.eq(ShoppingCar::getUserId, "1656698374114156635"))
.forEach(val -> typedTupleSet.add(new DefaultTypedTuple<>(val.convertExt(String.class), val.getPrice())));
if (redisTemplate.opsForZSet().add("1656698374114156635", typedTupleSet) > NumberUtils.LONG_ZERO){
//这里是按照分数从小到大的顺序获取集合,reverseRange() 则顺序相反
Set<String> result = redisTemplate.opsForZSet().range("1656698374114156635", 0, -1);
return result;
}
return null;
}
在 Redis 可视化客户端的数据如下图5-1所示:
常见的应用场景:根据游戏段位(作为Score)生成排行榜、按照发布时间范围(作为Score)来展示文章、按照商品价格(作为Score)去排序等。
六、文章小结
本文介绍了关于 Redis 的几个基本数据结构,以及在 Spring 项目中 RedisTemplate 的一些简单使用。
如有错误,还望指正,同时也欢迎大家在评论区说出自己的想法。
最后,我有计划写一篇关于上述几种数据结构在真实场景中具体应用文章,尝试着给出一些解决方案,还请期待。
参考文档:
- Redis 官方文档关于数据类型的说明:https://redis.io/docs/data-types/
- Spring Data Redis 官方关于2.6.10版本的 Api 文档:https://docs.spring.io/spring-data/redis/docs/2.6.10/api/