用户所属组织标签缓存
- 用户所属的标签是高频访问字段,所以在Redis中进行缓存
- 使用List结构存储而不使用set,List底层是压缩列表+双向链表,内存紧凑读写快,set天然去重是要依赖哈希表的,计算哈希值解决哈希冲突效率不如List,而且Set存储是乱序的,List存储是按照存储的顺序的,是有一定的顺序的
//标签分为所属组织标签和个人私有标签,其中组织所属标签是一对多的关系,也就是一个用户可以归属多个组织
//所以数据结构采用List更加合适,因为有多个所以使用方法是rightPushAll(),orgTags是一个List.of,在这个转换成数组全部加入
//rightPushAll有个问题是每次添加数据都是在追加,所以容易造成重复数据,所以在分配所属组织标签的时候要先删除key在添加数据
redisTemplate.opsForList().rightPushAll(key, orgTags.toArray());
//取值操作range从0到-1默认是取出从0索引到结尾的所有元素,Redis存储的是二进制数据,所以默认封装成Object对象,再通过map进行转换
try {
String key = USER_ORG_TAGS_KEY_PREFIX + username;
List<Object> result = redisTemplate.opsForList().range(key, 0, -1);
if (result != null && !result.isEmpty()) {
return result.stream()
.map(obj -> (String) obj)
.toList();
}
} catch (Exception e) {
logger.error("Failed to get organization tags for user: {}", username, e);
}
return null;
RedisCallback原生连接
- 在 Redis 中,像 opsForValue/opsForHash 其实都是高级封装,Spring 帮忙把 java 对象转换 Redis 能识别的字节数组,但是会有少量的封装开销, 而且并不是所有的命令都有封装
- RedisCallback 跳过 Spring 的封装直接拿到 RedisConnection 原生连接。RedisCallback 是一个函数式接口(只有一个抽象方法 doInRedis),必须实现这个唯一的抽象方法。直接显示重写 doInRedis 或者使用 Lambda 表达式都可以。
long uploadedCount = redisTemplate.execute(new RedisCallback<Long>() {
@Override
public Long doInRedis(RedisConnection connection) throws DataAccessException {
// 统计 redisKey 对应位图中 1 的数量(已上传分片数)
return connection.bitCount(redisKey.getBytes());
}
});
// 等价于上面的 Hash 统计(Lambda 隐式实现)
Long hashFieldCount = redisTemplate.execute(connection -> {
// 这里的代码就是 doInRedis 方法的逻辑,省略了方法声明和 @Override
return connection.hLen("user:100".getBytes());
});
文件分片上传在 Redis 中缓存
- Redis BitMap 是一个由二进制数组,每一位都是 0 和 1,分块的数对应偏移量下标, 1表示已上传, 0表示未上传
// 核心:查询 redisKey 对应位图中,下标为 chunkIndex 的位值(0=未上传,1=已上传)
boolean isUploaded = redisTemplate.opsForValue().getBit(redisKey, chunkIndex);
// 分片上传成功后,标记该分片为已上传(位值设为1)
redisTemplate.opsForValue().setBit(redisKey, chunkIndex, true);
//全部分片上传完成后,需要合并文件,此时用 bitCount 统计位图中 1 的数量,对比总分片数
long uploadedCount = redisTemplate.execute(new RedisCallback<Long>() {
@Override
public Long doInRedis(RedisConnection connection) throws DataAccessException {
// 统计 redisKey 对应位图中 1 的数量(已上传分片数)
return connection.bitCount(redisKey.getBytes());
}
});