若依+redis基础

Redis

Redis是一个开源的由C语言开发的高性能键值对(key-value)数据库,是互联网技术领域使用最为广泛的存储中间件

官方提供的数据表示可以达到100000+的QPS(每秒内查询次数)

QPS扩展:

  1. 小型应用
    范围:10 - 100 QPS
    特点:通常为初创公司或个人项目,用户基数较小,功能相对简单。
    示例:博客平台、小型电商网站、内部工具。
  2. 中型应用
    范围:100 - 1,000 QPS
    特点:有一定用户量,业务逻辑较为复杂,可能涉及多个服务和数据库。
    示例:中型企业官网、中小型电商平台、在线教育平台。
  3. 大型应用
    范围:1,000 - 10,000 QPS
    特点:用户量大,系统架构复杂,通常采用分布式架构和负载均衡。
    示例:大型电商平台、社交网络、视频流媒体平台。
  4. 超大型应用
    范围:10,000+ QPS
    特点:全球性用户,超高并发,使用微服务架构、CDN、缓存等技术优化性能。
    示例:Google、Facebook、阿里巴巴、亚马逊等互联网巨头。

NoSQL(Not Only SQL),泛指非关系型数据库,是对关系型数据库的补充

主要特点:

  • 基于内存存储,读写性能高
  • 适合存储热点数据(访问量高)
  • 企业应用广泛(几乎没有不用)

图像化工具:Another Redis Desktop Manager 等;


常用数据类型与命令

常用数据类型:String(字符串),hash(哈希),list(列表),set(无序集合),zset(有序集合)

Redis中文网:https://www.redis.net.cn

String常用命令

命令 功能
set key value 设置指定KEY的值
get key 获取指定KEY的值
setex key seconds value 将值value关联到key,并将key的过期时间设为seconds(以秒为单位)
setnx key value 只要在key不存在时设置key的值
mset key value [key value ...] 同时设置一个或多个 key-value 对
msetnx key value [key value ...] 同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在
incr key 将key中存储的数字值加一
decr key 将key中存储的数字值减一

其他数据类型对于的命令可以查看官方文档;


Spring Data Redis

Spring Data Redis 是 Spring 的一部分,在 Spring 应用中通过简单的配置就可以访问 Redis 服务,对 Redis 底层开发包进行了高度封装。在 Spring 项目中,可以使用Spring Data Redis来简化 Redis 操作。

依赖引入:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

常用数据类型对于的相关API

字符串

/**
* 字符串 (String) 操作测试
* 展示如何使用 RedisTemplate 操作字符串数据类型
*/
@Test
public void stringTest() {
    ValueOperations valueOperations = redisTemplate.opsForValue();
    // 设置键 name 和 address 的值,并为 address 设置过期时间
    valueOperations.set("name", "zhangsan");
    valueOperations.set("address", "hangzhou", 3, TimeUnit.SECONDS);
    // 条件性设置 gender 的值
    valueOperations.setIfAbsent("gender", 1);
    valueOperations.setIfAbsent("gender", 2);

    // 打印键 name 和 address 的值
    String name = (String) valueOperations.get("name");
    System.out.println("name = " + name);
    System.out.println(valueOperations.get("address"));
    // 等待 3.5 秒后再次打印键 address 的值
    ThreadUtil.sleep(3500);
    System.out.println(valueOperations.get("address"));

    // 设置并增加 age 的值
    valueOperations.set("age", 18);
    valueOperations.increment("age");
    System.out.println(valueOperations.get("age"));
}

哈希

/**
* 哈希 (Hash) 操作测试
* 展示如何使用 RedisTemplate 操作哈希数据结构
*/
@Test
public void hashTest() {
    HashMap<String, Object> map = new HashMap<>();
    // 向哈希 user 中添加字段
    map.put("name", "zhangsan");
    map.put("age", 18);
    map.put("address", "hangzhou");
    redisTemplate.opsForHash().putAll("user", map);
    // 打印哈希 user 中的 name 和 age 字段
    System.out.println(redisTemplate.opsForHash().get("user", "name"));
    System.out.println(redisTemplate.opsForHash().get("user", "age"));

    // 打印哈希 user 中的所有字段名、值和键值对
    System.out.println(redisTemplate.opsForHash().keys("user"));
    System.out.println(redisTemplate.opsForHash().values("user"));
    System.out.println(redisTemplate.opsForHash().entries("user"));
}

列表

/**
* 列表 (List) 操作测试
* 展示如何使用 RedisTemplate 操作列表数据结构
*/
@Test
public void listTest() {
    // 向列表 location 中添加元素
    redisTemplate.opsForList().rightPushAll("location", "hangzhou", "beijing", "shanghai");
    // 打印列表 location 的大小和所有元素
    System.out.println(redisTemplate.opsForList().size("location"));
    System.out.println(redisTemplate.opsForList().range("location", 0, -1));

    // 获取并打印列表 location 的第一个元素
    redisTemplate.opsForList().index("location", 0);
    System.out.println(redisTemplate.opsForList().leftPop("location"));
}

无序集合

/**
* 集合 (Set) 操作测试
* 展示如何使用 RedisTemplate 操作集合数据结构
*/
@Test
public void setTest() {
    SetOperations setOperations = redisTemplate.opsForSet();
    // 向集合 s1 和 s2 中添加元素
    setOperations.add("s1", "java", "python", "network", "run", "dance");
    setOperations.add("s2", "java", "python", "c#", "work", "sing");
    // 打印集合 s1 的所有成员
    System.out.println(setOperations.members("s1"));
    // 随机移除并打印集合 s1 中的一个元素
    System.out.println(setOperations.pop("s1"));
    // 打印集合 s1 的大小
    System.out.println(setOperations.size("s1"));

    // 打印集合 s1 和 s2 的差集
    System.out.println(setOperations.difference("s1", "s2"));
    // 打印集合 s1 和 s2 的交集
    System.out.println(setOperations.intersect("s1", "s2"));
    // 计算集合 s1 和 s2 的并集并存储在 s3 中
    setOperations.unionAndStore("s1", "s2", "s3");
    // 打印集合 s3 的所有成员
    System.out.println(setOperations.members("s3"));

}

有序集合

/**
* 有序集合 (ZSet) 操作测试
* 展示如何使用 RedisTemplate 操作有序集合数据结构
*/
@Test
public void zsetTest() {
    ZSetOperations zSetOperations = redisTemplate.opsForZSet();
    // 向有序集合 phb 中添加元素
    zSetOperations.add("phb", "zhangsan", 8000);
    zSetOperations.add("phb", "lisi", 6500);
    zSetOperations.add("phb", "wangwu", 18000);
    zSetOperations.add("phb", "zhaoliu", 9000);

    // 增加 zhangsan 的分数
    zSetOperations.incrementScore("phb", "zhangsan", 2000);

    // 打印有序集合 phb 中所有元素及其分数
    Set<ZSetOperations.TypedTuple> phb = zSetOperations.reverseRangeWithScores("phb", 0, -1);
    for (ZSetOperations.TypedTuple typedTuple : phb) {
        System.out.println(typedTuple.getValue() + ":" + typedTuple.getScore());
    }
}

通用命令

/**
* 基本操作测试
* 展示如何使用 RedisTemplate 进行基本的键值操作
*/
@Test
public void normalTest() {
    // 打印所有键
    System.out.println(redisTemplate.keys("*"));
    // 设置键 phb 的过期时间为 1 分钟
    redisTemplate.expire("phb", Duration.ofMinutes(1));
    // 打印键 phb 的类型
    System.out.println(redisTemplate.type("phb"));
    // 检查键 s3 是否存在
    System.out.println(redisTemplate.hasKey("s3"));
    // 删除键 location 并打印结果
    System.out.println(redisTemplate.delete("location"));
    // 再次检查键 location 是否存在
    System.out.println(redisTemplate.hasKey("location"));
}

若依中的Redis缓存使用

在若依框架中,使用了Redis作为缓存,并且以及进行了相关序列化配置

核心配置类为framework模块中的config.RedisConfig类

具体代码:

@Bean
@SuppressWarnings(value = { "unchecked", "rawtypes" })
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)
{
    RedisTemplate<Object, Object> template = new RedisTemplate<>();
    template.setConnectionFactory(connectionFactory);

    FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);

    // 使用StringRedisSerializer来序列化和反序列化redis的key值
    template.setKeySerializer(new StringRedisSerializer());
    template.setValueSerializer(serializer);

    // Hash的key也采用StringRedisSerializer的序列化方式
    template.setHashKeySerializer(new StringRedisSerializer());
    template.setHashValueSerializer(serializer);

    template.afterPropertiesSet();
    return template;
}

执行流程:

  1. 创建RedisTemplate实例:创建一个RedisTemplate<Object, Object>实例。
  2. 设置连接工厂:将传入的RedisConnectionFactory设置为该模板的连接工厂。
  3. 创建序列化器:
    • 使用FastJson2JsonRedisSerializer作为值(value)的序列化器,确保对象可以被正确序列化和反序列化。
    • 使用StringRedisSerializer来序列化和反序列化Redis的键(key)和哈希键(hash key),确保键以字符串形式存储。
  4. 设置序列化器:
    • 设置键序列化器为StringRedisSerializer。
    • 设置值序列化器为FastJson2JsonRedisSerializer。
    • 设置哈希键序列化器为StringRedisSerializer。
    • 设置哈希值序列化器为FastJson2JsonRedisSerializer。
  5. 初始化模板:调用afterPropertiesSet方法完成配置并返回配置好的RedisTemplate。

补充:在afterPropertiesSet方法中如果没有自己配置相关的序列化器,则会使用jdk默认的序列化器


JSON 序列化和 JDK 序列化的区别

FastJson2JsonRedisSerializer 使用的是 JSON 格式的序列化,而 JDK 序列化是 Java 内置的二进制序列化机制。它们在多个方面存在显著差异:

  1. 序列化格式
    JDK 序列化:使用二进制格式进行序列化,生成的字节流包含类的元数据(如类名、字段信息等)。这种格式适合 Java 对象的持久化和传输,但不便于人类阅读。
    JSON 序列化:使用 JSON 格式进行序列化,生成的是人类可读的文本字符串。JSON 是一种轻量级的数据交换格式,广泛用于 Web 开发和跨语言通信。
  2. 性能
    JDK 序列化:相对较慢,因为需要处理大量的元数据,并且生成的二进制数据较大。此外,JDK 序列化在反序列化时对类版本敏感,如果类结构发生变化,可能会导致反序列化失败。
    JSON 序列化:通常更快,尤其是在处理大量数据时,JSON 格式的解析和生成效率较高。FastJSON2 等高性能库进一步优化了 JSON 序列化的速度。
  3. 跨语言支持
    JDK 序列化:只能用于 Java 程序之间的通信,因为它依赖于 Java 类的元数据。其他编程语言无法直接解析 JDK 序列化的二进制数据。
    JSON 序列化:由于使用 JSON 格式,可以轻松与其他编程语言(如 JavaScript、Python、Go 等)进行数据交换,具有良好的跨语言兼容性。
  4. 可读性和调试
    JDK 序列化:生成的二进制数据难以直接阅读和调试,需要专门的工具或反序列化才能查看内容。
    JSON 序列化:生成的 JSON 字符串是人类可读的,方便调试和日志记录,可以直接查看和修改。
  5. 安全性
    JDK 序列化:存在安全风险,特别是当反序列化不受信任的数据时,可能导致远程代码执行漏洞。因此,在使用 JDK 序列化时需要特别小心。
    JSON 序列化:相对更安全,特别是在 FastJSON2 中可以通过配置白名单(如 JSONReader.autoTypeFilter)来限制允许反序列化的类型,从而减少安全风险。
  6. 灵活性
    JDK 序列化:要求被序列化的类实现 Serializable 接口,并且类的结构不能随意更改,否则会导致反序列化失败。
    JSON 序列化:更加灵活,不需要实现特定接口,且可以通过配置忽略某些字段或自定义序列化逻辑。
posted @ 2025-02-27 21:28  zhangfff  阅读(599)  评论(0)    收藏  举报