Redis课后讨论

来源:10-1 怎么正确的选择数据类型,让维护不再是噩梦

zk0405

2020-09-23

  一哥你好。
  1、首先我想说说我对Key命名的理解。第一次接触到redis是在某教学视频中,一般都是这种形式。

redisTemplate.opsForValue().set("user_" + id,new Object());
Object o = redisTemplate.opsForValue().get("user_" + id);

相信大多数人一开始接触redis都是这种代码,甚至直接以id为key。说实话,一开始我对这种key的使用方式也是觉得没啥问题。直到有一次我写了类似下面这段代码。

Object o = redisTemplate.opsForValue().get("user" + id);//key少了一个下划线

后果就是虽然有对取出的user进行判空可以将错就错,但是这样的缓存是无法保证一致性的(做一致性处理的只有"user_")。也就是一开始不会有什么问题,无非就是往redis中多加了个缓存而已,可一旦出现一致性的问题而且是在生产环境上,那么面对的就是客户铺天盖地的投诉了。
  那么如何避免这个问题?我一开始的想法很简单,复制粘贴就好了,使用已有缓存必须复制之前的key规则,遵照这个规范基本不会犯错(因为后端接口只有我自己一个人开发)。闲下来的时候我也会想,这种如此主观的处理真的合适么?说到底这还是项目中存在魔法值的问题,能对抗魔法值的是什么?枚举!由于之前就想着要写一套redis使用的工具类,但是传统方法用着也不是特别难受就一直搁置着。可是这次的思路一出现,我就立马着手去实现这个方案。
  首先是key的命名,当然是运用枚举去列举所有需要的key,加上为了在RDM上更好的观察数据,对命名上也进行了分包(运用redis的冒号分隔符来分组)。最后实现出来的样子如下:

@Getter
@AllArgsConstructor
public enum RedisKeyEnum {
    USER("user", "userId_"),//用户缓存,以用户Id为key
    ROLE("role", "roleId_");//角色缓存,以角色Id为key
	private String folder;//分类名
    private String keyPrefix;//key前缀(对key的描述) 必填项
}

再来是redis的工具类,基本是从博客上抄下来的,然后进行改动(主要是针对key的构造)。

public <T> T get(RedisKeyEnum keyEnum, String key, Function<String,T> function) {//篇幅原因,省略实现
public void set(RedisKeyEnum keyEnum, String key, Object value);
public void delete(RedisKeyEnum keyEnum, String key);

在此之上,我又模仿了HashMap的computeIfAbsent方法,对get进行了优化。以下是传统写法:

	User u = (User) redisTemplate.opsForValue().get("user_" + id);
	if(u == null){
	   //从数据库获取或其他
	}

接下来是改良后

	User u= redisUtil.get(RedisKeyEnum.USER, "100", s -> new User());

对key的改造中隐藏了对key繁琐且容易出现失误的拼接,采用了指定缓存枚举+id为入参。只要枚举中的注释能够写明白,那么在多人开发的背景下对缓存的复用也提供了便利。最后附上一张rdm中的效果:
图片描述
  2、用过哪些数据类型?
  说来有点惭愧,目前只用过K-V类型,V采用json字符串。以前也有想过是否要用下其他数据类型,但K-V用起来确实太酸爽了,就算有一些特殊需求如去重或者排序,也可以加载到代码中进行处理。我也秉承自己目前信奉的开发规则,若无必要,勿增实体。
  不过还是想问下一哥,梭哈K-V是否有弊端?我目前的猜想是数据从redis加载,在代码中处理,再set回redis中,此过程非原子性操作,会有脏数据风险。而且大量数据的json转换也会损耗性能。当然这只是我的猜想,接触的项目中也没有这么庞大的规模让我发现错误

写回答

1回答

张勤一

2020-09-23

zk 同学你好:

    非常感谢你这里的实践分享,我这里针对你的疑问进行回复。你这里一共提出了两个问题:应该怎样去命名 redis key、如何对 Redis 支持的数据类型进行选型。

    首先,第一个问题:应该怎样去命名 redis key?

    正如我在视频课程中所说,redis key 一定不能冲突,否则,你的数据将会被覆盖,这可能会导致严重的后果。所以,在遵循不浪费内存空间的基础之上,我们应该让 key 越长越好。我这里给出一个建议(注意,只是我个人的建议,而不是规定):

    部门名称 + 项目名称 + 模块名称 + 业务场景 + id

    当然,如果各个部门使用的 redis server 都是不同的,当然可以直接去掉 “部门名称”这个前缀,其他的前缀也是一样以此类推的。

    第二个问题:如何对 Redis 支持的数据类型进行选型

    确实,如你所说,很多时候,我们都只是使用 String-String 类型的 KV 去存储,反正可以在代码里面直接做序列化和反序列化。我真实的告诉你,除非你的工程非常大,访问量非常高,否则,这种做法绝对是可行的。毕竟,这已经可以很好的支持我们的业务场景了,又有什么错误呢?

    但是,如果是面试或者从理论角度出发,你不能这样说,你需要遵循 Redis 数据类型的特性,例如:list 存储有序数据、hash 存储多属性的对象数据等等。随机应变即可,不需要过多纠结。


    我是勤一,致力于将这门课程的问答区打造为 Java 知识体系知识库,Java 知识体系 BBS!共同建造、维护这门课程,我需要每一个你!

3
1
zk0405
感谢一哥的解答
2020-09-23
共1条回复

Java实操避坑指南 SpringBoot/MySQL/Redis错误详解

掌握业务开发中各种类型的坑,,Java web开发领域通用

452 学习 · 204 问题

查看课程

相似问题