首页 > 数据库 >Redis——06 学习

Redis——06 学习

时间:2022-12-19 10:45:00浏览次数:45  
标签:06 -- redis Redis 学习 user new 序列化 redisTemplate

Redis——06

将 Redis 的基本使用以及三种模式进行了学习和了解。接下来就学习如何在 Java 中以及 Spring Boot 框架中使用 Redis。

Jedis

Redis 在 Java 上的操作多半是集群模式

直接创建 Maven 项目映入 Jedis 依赖即可。

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.2.0</version>
</dependency>

在虚拟机中运行 redis 的单机版和集群(构建集群相同的节点构建一次就可以了)。

访问单机版 Redis

image-20221218092911191

打印结果:redis-01-value

当然还需要进行资源释放:jedis.close();

接下来在 linux 中的 redis 客户端查看是否有对应的值:

image-20221218093014580

单机版使用 Jedis 对象来获取连接的,接下来使用连接池 Pool,Jedis 提供了连接池对象:JedisPool

// 用线程池访问 redis
public static void test02(){
    // JedisPool 的配置文件对象。
    JedisPoolConfig config = new JedisPoolConfig();
    // 最多连接对象个数
    config.setMaxTotal(20);
    // 最大活动连接
    config.setMaxIdle(7);
    // 最小活动连接
    config.setMinIdle(3);
    JedisPool pool = new JedisPool(config,"192.168.12.167",6379);
    // 通过 JedisPool 获取连接对象 Jedis
    Jedis jedis= pool.getResource();
    jedis.set("redis-02-key","redis-02-value");
    System.out.println(jedis.get("redis-02-key"));
    // 关闭 jedis 连接,并不需要关闭连接池 ——> JedisPool,由虚拟机关闭redis服务自动关闭。
    jedis.close();
}

image-20221218093715247

redis 客户端结果:

image-20221218093743084

访问 Redis 集群

// 访问 redis 集群
public static void test03(){
    Set<HostAndPort> nodes = new HashSet<>();
    // 一共 6 个节点
    nodes.add(new HostAndPort("192.168.12.167",7100));
    nodes.add(new HostAndPort("192.168.12.167",7101));
    nodes.add(new HostAndPort("192.168.12.167",7102));
    nodes.add(new HostAndPort("192.168.12.167",7103));
    nodes.add(new HostAndPort("192.168.12.167",7104));
    nodes.add(new HostAndPort("192.168.12.167",7105));
    // JedisCluster 默认自带 JedisPool,以及 JedisPoolConfig 也是默认的。
    JedisCluster cluster = new JedisCluster(nodes);
    // 通过 cluster 直接操作
    cluster.set("redis-cluster-key","redis-cluster-value");
    System.out.println(cluster.get("redis-cluster-key"));
}

image-20221218094701049

查看 redis 客户端结果:

image-20221218094819882

注意:JedisClusterJedis 可能有些许不同,这里不是说使用方法,而是 JedisCluster 中默认实现了 JedisPool,而不像 Jedis 使用 JedisPool 一样,需要 pool.getResource();,因为这个也是比较容易理解,我们在 redis 客户端访问集群时,也是使用一个客户端,不过加了一个参数 -c,帮我们自动的进行重定向,包括客户端连接端口切换。所以将 JedisCluster 理解成加了-c 参数的 redis 客户端,而重定向已经帮我们自动实现了。Jedis 就是没加参数的客户端。

SpringDataRedis

1. 创建 Spring Boot 项目

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

写配置文件:

application.yml

spring:
  redis:
    port: 6379 # 默认是6379
    database: 0 # 数据库编号 默认是0
    host: 192.168.12.167 # ip 地址,默认是本地 localhost

2. 使用 RedisTemplate 操作 Redis

SpringData 系列框架中,会提供 XxxTemplate,用作数据访问客户端对象。

@SpringBootTest(classes = {RedisApp.class})
@RunWith(SpringRunner.class)
public class RedisDataTest {

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    @Test
    public void test01(){
        redisTemplate.opsForValue().set("key-1","value-1");
    }
}

启动运行单元测试,发现报错,大致意思是:Spring 容器中没有找到一个 RedisTemplate <String,Object> 类型将其注入。因为 SpringDataRedis 默认提供的 RedisTemplate 的泛型是:<Object,Object> 或 <String,String>

如果要使用别的泛型需要自己手动创建泛型放入容器中。

RedisConfiguration

@Configuration
public class RedisConfiguration {
    @Bean
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        // 此时 redisTemplate 只是一个对象,并没有 application.yml 中的相关配置参数以及默认参数
        // 加载 RedisConnectionFactory 连接工厂。
        redisTemplate.setConnectionFactory(factory);
        return redisTemplate;
    }
}

再次运行,没有报错,登录 redis 客户端查看数据:

image-20221218105256528

可以看到值是存进去了,但是前面有一串超乎预期的字符串,这就是接下来要解决的问题序列化。

再设置一个 User 类并对其进行获取:

    public void test01(){
        User user = new User();
        user.setUsername("Tom");
        user.setAge(18);
        user.setClazz("1977");
        user.setPassword("rootAdmin");
        redisTemplate.opsForValue().set("key-1","value-1");
        redisTemplate.opsForValue().set("user-1",user);
        System.out.println(redisTemplate.opsForValue().get("key-1"));
        System.out.println(redisTemplate.opsForValue().get("user-1"));
    }

客户端结果:

image-20221218180726458

在 Java 中通过键值可以直接获取到数据。

3. 序列化

Java 程序在向 Redis 存数据时,有时我们需要传递的并不是字符串类型的数据,所以 SpringDataRedis 在存储数据到 Redis 中时,都会将其进行序列化存储方便在网络中传输。

默认情况下使用的是 JDK 自带的序列化方式。最后存储到 Redis 中就如上图所示,可以看到 User 类仅仅4个简单的属性经过 JDK 的序列化后占用的空间比较大,并且可读性非常差。

所以可以使用 SpringDataRedis 提供实现比较好的序列化器,帮助我们完成序列化。

RedisConfiguration 中对其进行设置序列化器:

@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){
    RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
    redisTemplate.setConnectionFactory(factory);
    
    // 设置序列化器:
    redisTemplate.setKeySerializer(new StringRedisSerializer()); // 设置 key 的序列化器
    redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class)); // 设置 value 的序列化器

    // 设置 hash
    redisTemplate.setHashKeySerializer(new StringRedisSerializer());
    redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class));
    return redisTemplate;
}

image-20221218181422175

查看redis客户端:

image-20221218181943797

这样看起来就非常的符合我们预期中的结果了。

SpringDataRedis 提供的序列化器:

那么此时在程序中来获取一下key-1、user-1 的数据类型:

class java.lang.String
class java.util.LinkedHashMap

明显 user-1 反序列化返回的引用类型是:LinkedHashMap,而我们理想的是 User,这就是我们下一个要解决的问题!反序列化时将引用类型转换成正确的类型。

4. 反序列化

为了解决 redis 存储 pojo 实例反序列化问题:

方法1:Jackson2JsonRedisSerializer

通过观察 redis 存的 User 实例对象信息,只有 User 的属性以及值。并没有更多和 User 相关的信息了,而如果此时 Admin 实例中也有其中的字段呢?程序并不能根据属性就知道是哪个实例,所以干脆就返回了 LinkedHashMap,因为设置序列化器的泛型是 Object,为了在存值的时候能适用所有引用类型,所以反序列化的时候我们也需要对序列化器进行设置,将泛型设置成我们需要的类即可:

    @Test
    public void test01(){
        System.out.println(redisTemplate.opsForValue().get("key-1").getClass() + "-->" + redisTemplate.opsForValue().get("key-1"));

        redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(User.class)); // 反序列化时对其进行指定泛型 User
        System.out.println(redisTemplate.opsForValue().get("user-1").getClass() + "-->" + redisTemplate.opsForValue().get("user-1"));
        redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class)); // 再恢复序列化的泛型 Object
    }

在获取完成 pojo 实例后还需要将其 Jackson2JsonRedisSerializer 序列化器的泛型指定为 Object 防止下次还是以 User.class 的类进行序列化和反序列化。

class java.lang.String-->value-1
class com.lyl.entity.User-->User{username='Tom', age=18, password='rootAdmin', clazz='1977'}

方法2:GenericJackson2JsonRedisSerializer

可以使用 GenericJackson2JsonRedisSerializer 这个序列化器,也是 SpringDataRedis 提供的。

@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){
    RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
    redisTemplate.setConnectionFactory(factory);
    
    redisTemplate.setKeySerializer(new StringRedisSerializer());
    redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    
    // 设置 hash
    redisTemplate.setHashKeySerializer(new StringRedisSerializer());
    redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());

    return redisTemplate;
}

获取数据:

@Test
public void test01(){
    User user = new User();
    user.setUsername("Tom");
    user.setAge(18);
    user.setClazz("1977");
    user.setPassword("rootAdmin");
    redisTemplate.opsForValue().set("key-1","value-1");
    redisTemplate.opsForValue().set("user-1",user);
    System.out.println(redisTemplate.opsForValue().get("key-1").getClass() + "-->" + redisTemplate.opsForValue().get("key-1"));
    System.out.println(redisTemplate.opsForValue().get("user-1").getClass() + "-->" + redisTemplate.opsForValue().get("user-1"));
}

结果:

class java.lang.String-->value-1
class com.lyl.entity.User-->User{username='Tom', age=18, password='rootAdmin', clazz='1977'}

查看 redis 客户端:

image-20221219093848111

可以看到跟之前使用序列化器序列化 pojo 实例不同的一点是,会给其加上一个类的全限定名,以便根据类的全限定名来对其反序列化。这样不需要像上面一样进行泛型的设置了。

同时这样做会有一点缺点:

  • redis 数据中额外加入了一个 字段,如果这个类中还包含其他pojo引用,依然会再增加。
  • 在反序列化时根据@class 字段去获取类的全限定名,如果没有该字段或者项目中不存在该类,非常容易出现 Class not found 异常。

两者都有自己的优缺点,根据实际情况而定。

SpringDataRedis 中提供的序列化器有这么多,一般情况下足够使用。

5. List 和 Hash

List

@Test
public void test02(){
    User user = new User();
    user.setUsername("Tom");
    user.setAge(18);
    user.setClazz("1977");
    user.setPassword("rootAdmin");
    redisTemplate.opsForList().leftPushAll("list",user,"list-2","list-3","list-4"); // 向左添加
    redisTemplate.opsForList().rightPushAll("list","list-100","list-200","list-300","list-400"); // 向右添加
    // 一般情况下 List 中存放的数据类型都应该是准确一定的,这里是测试反序列化。
    List<Object> list = redisTemplate.opsForList().range("list",0,-1);
    for (Object o:list){
        System.out.println(o.getClass()+ "-->"+o);
    }
}

结果:

class java.lang.String-->list-4
class java.lang.String-->list-3
class java.lang.String-->list-2
class com.lyl.entity.User-->User{username='Tom', age=18, password='rootAdmin', clazz='1977'}
class java.lang.String-->list-100
class java.lang.String-->list-200
class java.lang.String-->list-300
class java.lang.String-->list-400

redis 客户端:

image-20221219095732488

这里使用的序列化器:GenericJackson2JsonRedisSerializer

Hash

@Test
public void test03(){
    User user = new User();
    user.setUsername("Tom");
    user.setAge(18);
    user.setClazz("1977");
    user.setPassword("rootAdmin");
    
    // 单个操作
    redisTemplate.opsForHash().put("map-1","field-k1","filed-v1");
    redisTemplate.opsForHash().put("map-1","field-k2","filed-v2");
    
    // 批量操作
    Map<Object,Object> map = new HashMap<>();
    map.put("field-k3","v3");
    map.put("field-k4","v4");
    map.put("field-k5",user);
    map.put("field-k6",18);
    redisTemplate.opsForHash().putAll("map-1",map);
	
    // 打印
    Map<Object, Object> entries = redisTemplate.opsForHash().entries("map-1"); // 相对于 hgetall
    for (Map.Entry<Object,Object> entry: entries.entrySet()){
        System.out.println(entry.getKey() + "-->" + entry.getValue());
    }
}

控制台打印:

field-k3-->v3
field-k1-->filed-v1
field-k6-->18
field-k4-->v4
field-k5-->User{username='Tom', age=18, password='rootAdmin', clazz='1977'}
field-k2-->filed-v2

redis 客户端:

image-20221219100749955

以及后面的 set 和 zset 操作都是类似的。

小结

就 redis 的学习和简单使用而言就到这儿,对于 redis 的高级使用和面试,应该要进一步学习 Java Web 知识,甚至需要到分布式和微服务才能有深刻体会。

加上学习JedisSpringDataRedis,两个和 Java 相关的,以及之间的高级操作后续学习。

标签:06,--,redis,Redis,学习,user,new,序列化,redisTemplate
From: https://www.cnblogs.com/cndada/p/16991611.html

相关文章

  • Markdown学习
     标题#+标题名字+空格+回车#号数量代表几级标题字体helloworld!!两边都加**变成粗体helloworld!!两边都加*变成斜体helloworld!!三个*变成斜体加粗helloworld!!两边......
  • delphi D11编程语言手册 学习笔记(P424-477) 泛型
      这本书可以在Delphi研习社②群256456744的群文件里找到.书名:Delphi11AlexandriaEdition.pdf 泛型在C++中叫做类型模板(templateclasses),单从字面上理......
  • pycaret学习之异常检测
    异常检测是识别与大多数数据明显不同的稀有物品、事件或观察结果的任务。通常,异常项目会转化为某种问题,例如银行欺诈、结构缺陷、医疗问题或文本中的错误。存在三大类异常......
  • 高性能Redis服务器注意事项
    摘要昨天简单理了理安装与配置相关的但是很多比较重要的核心性能参数并没有进行学习与探讨就基于昨天理解不深入的地方进行进一步的学习与了解希望能够提高Redis-Serve......
  • Redis7.0.7的简单安装与学习
    Redis7.0.7的简单安装与学习摘要2022.12.18世界杯决赛另外是我感染奥密克戎第五天.高烧已经没了,但是嗓子巨疼.睡不着觉,肝胆学习一下最新的Redis7.0.7第一部分......
  • spring的学习
    构造器的注入  这是原始的方法的注入  构造器注入  可以通过两种方法来进行注入 ......
  • ubuntu redis sentinel安装部署
    1.命令行安装sudoaptupdatesudoaptinstallredis-serversudoaptinstallredis-sentinel2.查看安装版本#redis-cli--versionredis-cli5.0.73.配置修改......
  • 深度学习常见问题汇总
    1.Expectedalltensorstobeonthesamedevice,butfoundatleasttwodevices,cuda:0andcpu!原因参与运算的两个或多个变量,有的在CPU上,有的在GPU上解决首先......
  • 18号CSS学习
    一、CSS简介 1.HTML局限性只关注内容的语义。“丑”2.CSS-网页的美容师CSS是层叠样式表的简称,也称为CSS样式表或级联样式表。主要用于设置HTML页面中的文本内容(......
  • saltstack学习
    salt-master:[root@salt-master~]#systemctl stop  firewalld [root@salt-master~]#systemctl  disable  firewalld [root@salt-master~]#sed -i......