Redis--Lesson05--Redis进阶

一.Redis中的事务

在Redis中,单条命令依旧保持原子性,但是对于事务来说(命令集)不保证原子性

Redis事务的本质:一组命令的集合,一个事务中所有的命令都会被序列化,在事务的执行过程中,会按照顺序执行,一次性,顺序性,排他性!执行一些命令

如:--- 队列  set1,set2,set3  执行--

Redis事务没有隔离级别的概念,在创建Redis事务中,命令不会被直接执行,而是由exec命令发出后开始队列化执行

Redis执行事务的过程:

  • 开启事务(multi)
  • 命令入队(......)
  • 执行事务(exec)

 放弃事务:DISCARD

 执行事务的两种错误情况

1.编译型异常(代码有问题,命令有错)

这种错误会导致整个事务都不会被执行

 2.运行时异常(代码逻辑异常)

如果事务队列存在语法性的错误,那么执行命令的时候,其它命令都是可以正常执行的,但是语法性错误的指令肯定执行不了,丢失原子性

 二.Redis中的乐观锁(监控)

悲观锁(Pessimistic Locking)

  • 假设最坏的情况会发生,在读取数据时就加锁,防止其他事务修改该数据。
  • 在整个事务过程中保持锁定状态,直到事务提交或回滚。

乐观锁(Optimistic Locking)

  • 假设在大多数情况下不会发生冲突,因此在读取数据时不加锁。
  • 在更新数据时才检查数据是否被其他事务修改过。如果没有被修改,则更新成功;如果已经被修改,则放弃本次更新或重新尝试。

watch:监视对象

当在被监视的情况下的对象发生变化,那么事务会执行失败,需要放弃原监视后,再次监视;如果事务执行成功,则自动放弃监视

 执行失败的情况:

在监视的情况下,另外的一个线程率先更改值,那么被监视的事务会执行失败

另一线程率先更改

 返回本线程执行事务:

 本线程执行失败

 三.Jedis

使用Java操作redis是官方推荐使用的一款API,Jedis是一款操作Redis的中间件,也是操作redis的基础,虽然在springBoot中已经被其它的中间件取代,但是Jedis的操作上手程度很适合新手

导入依赖:

<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>5.2.0</version>
</dependency>

 

直接新建一个Jedis测试类,创建一个jedis对象,并且传递参数

//创建jedis对象
        Jedis jedis = new Jedis("127.0.0.1",6379);
        //测试连接
        System.out.println(jedis.ping());

 

String类型测试

//测试String类型
        jedis.set("name","maming");
        System.out.println(jedis.get("name"));
        //批量创建k-v
        jedis.mset("k1","v1","k2","v2","k3","v3");
        //批量获得值
        System.out.println(jedis.mget("k1", "k2", "k3"));

 测试list集合

//清空数据库
        jedis.flushDB();
        //向list中添加元素
        jedis.lpush("list","a","b","c");
        //获取list中的所有元素
        System.out.println(jedis.lrange("list",0,-1));
        //从list中弹出最后一个元素
        System.out.println(jedis.rpop("list"));
        //获取list中的所有元素
        System.out.println(jedis.lrange("list",0,-1));

 测试set集合

//清空数据库
        jedis.flushDB();
        //向集合中添加元素
        jedis.sadd("myset","a","b","c");
        //打印集合中的元素
        System.out.println(jedis.smembers("myset"));

 其它的命令都是和在redis上一样的,都不用测试了

Jedis事务

在Jedis中的事务和Redis中过程都是一样的,并且性质也是一样,只不过使用Java语言开启和执行事务罢了

//清空数据库
        jedis.flushDB();
        // 创建一个JSONObject对象
        JSONObject jsonObject = new JSONObject();
        // 向JSONObject对象中添加键值对
        jsonObject.put("name","zhangsan");
        jsonObject.put("age","18");
        // 将JSONObject对象转换为字符串
        String re = jsonObject.toString();
        // 创建一个Transaction对象
        Transaction multi = jedis.multi();
        try {
            // 在Transaction对象中执行set操作
            multi.set("user1",re);
            multi.set("user2",re);
            // 执行Transaction对象中的操作
            multi.exec();
        }catch (Exception e){
            // 打印异常信息
            e.printStackTrace();
            // 放弃Transaction对象中的操作
            multi.discard();
        }finally {
            // 打印user1的值
            System.out.println(jedis.get("user1"));
            // 打印user2的值
            System.out.println(jedis.get("user2"));
            // 关闭jedis连接
            jedis.close();
        }

 

 四.SpringBoot整合Redis

如果SpringBoot需要操作数据类型必须要使用另一个家族成员SpringData

在SpringBoot2.0以后的版本Jedis都被改为lettuce

Jedis:采用的是直连,多个线程操作是不安全的,想要避免的话就是用jedis线程池

lettuce:次啊用netty,实例可以在多个线程中共享,不存在线程不安全的情况

安装依赖:

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

 

配置Redi数据源:

spring.data.redis.host=127.0.0.1
spring.data.redis.port=6379
spring.data.redis.database=0

 

测试类:

 @Autowired
    private RedisTemplate redisTemplate;
    @Test
    // 测试上下文加载
    void contextLoads() {
        // 从Redis中获取键为"name"的值并打印
        System.out.println(redisTemplate.opsForValue().get("name"));
        // 将字符串"a"添加到Redis中键为"list"的列表的左边
        redisTemplate.opsForList().leftPush("list","a");
        // 从Redis中键为"list"的列表的左边弹出元素并打印
        System.out.println(redisTemplate.opsForList().leftPop("list"));
    }

 

测试结果

springBoot整合后的主要操作数据方法:

 

opsForValue): 用于处理简单的字符串键值对。
opsForList): 用于处理列表数据结构。
opsForSet): 用于处理无序集合数据结构。
opsForZSet): 用于处理有序集合数据结构。
opsForHash): 用于处理哈希数据结构。

 

Redis自动配置类:

 Redis的自动配置属性(默认写入的地址和端口):

 五.自定义RedisTemplate模板

在使用系统默认的模板使用的jdk转义,将传递的值进行转义后存储到redis中

测试一个使用json传递方式:

 void testSer() throws JsonProcessingException {
        //真实的开发都使用json来传递对象
        User user = new User("maming",18);
        String s = new ObjectMapper().writeValueAsString(user);
        redisTemplate.opsForValue().set("user",s);
        System.out.println(redisTemplate.opsForValue().get("user"));
    }

 

Redis拿取到的结果:

 同时我们需要知道的是,在springBoot操作redis的时候,传递对象有两种方式,一种是传递json字符串,还有一种方式就是将对象序列化,如果对象没有序列化是传输不了的

例如我们直接传递一个对象:

void testSer() throws JsonProcessingException {
        //真实的开发都使用json来传递对象
        redisTemplate.opsForValue().set("user",new User("maming",18));
        System.out.println(redisTemplate.opsForValue().get("user"));
    }

 

报错:

org.springframework.data.redis.serializer.SerializationException: Cannot serialize

 

标志传递的值没有序列化,需要序列化只需要将我们的类实现一个接口Serializable就可以了

编写一个适用于自己项目的RedisTemplate模板:

@Configuration
public class RedisConfig {
    @Bean
    @SuppressWarnings("all") // 抑制所有警告
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        // 创建RedisTemplate实例,用于操作Redis
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();

        // 设置Redis连接工厂,以便模板能够连接到Redis服务器
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        // 创建Jackson2JsonRedisSerializer实例,用于将Java对象序列化为JSON格式存储在Redis中
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);

        // 创建ObjectMapper实例,用于处理JSON数据
        ObjectMapper mapper = new ObjectMapper();

        // 设置ObjectMapper的可见性策略,使得所有属性都能被序列化和反序列化
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);

        // 启用默认的类型信息,以便在反序列化时能够确定确切的类类型
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

        // 将配置好的ObjectMapper设置到Jackson2JsonRedisSerializer中
        jackson2JsonRedisSerializer.setObjectMapper(mapper);

        // 创建StringRedisSerializer实例,用于将字符串格式的key序列化到Redis中
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        // 设置RedisTemplate的key序列化器为StringRedisSerializer
        redisTemplate.setKeySerializer(stringRedisSerializer);

        // 设置RedisTemplate的hash key序列化器为StringRedisSerializer
        redisTemplate.setHashKeySerializer(stringRedisSerializer);

        // 设置RedisTemplate的value序列化器为Jackson2JsonRedisSerializer
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);

        // 设置RedisTemplate的hash value序列化器为Jackson2JsonRedisSerializer
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);

        // 初始化RedisTemplate,使配置生效
        redisTemplate.afterPropertiesSet();

        // 返回配置好的RedisTemplate实例
        return redisTemplate;
    }

}

 

这是模板直接复制粘贴就好了;使用新模板之后再次查看插入的数据:

 简单编写utils类,用于封装对redis命令的操作

@Component
public class RedisUtil {
    @Autowired
    private RedisTemplate redisTemplate;

    //向键值对中添加数据
    public void setString(String key, String value) {
        redisTemplate.opsForValue().set(key, value);
    }
    //拿取键值对中的数据
    public String getString(String key) {
        return (String)redisTemplate.opsForValue().get(key);
    }
    //刷新数据库
    public void flushDB(){
        try {
            RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
            connection.flushDb();
            connection.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

 

简单测试封装类:

    @Test
    void testRedisUtil() throws JsonProcessingException {
        // 清空Redis数据库中的所有数据
        redisUtil.flushDB();
        
        // 在Redis中设置一个字符串键值对,键为"user",值为"maming"
        redisUtil.setString("user", "maming");
        
        // 从Redis中获取键为"user"的字符串值,并打印到控制台
        System.out.println(redisUtil.getString("user"));
    }

 

输出:

maming

 

六.简单查看Redis的配置文件

1.单位篇:描述了redis支持的数据单位,并且告知我们redis单位是不区分大小写的

 2.redis可以导入其它文件,引入到本文件之后一起运行

 3.网络配置:默认绑定的是环回地址127.0.0.1

 4.端口篇:预载了启动redis服务的默认端口

 5.是否守护进程运行,默认否

 6.默认的数据库个数16个,是否显示log

 7.快照:如果900s内有一个key改动持久化,如果300s内有10个key改动持久化,还有60s内改动10000次

8.rdb持久化配置

 9.安全验证:redis可以设置密码

 

127.0.0.1:6379> config get requirepass
1) "requirepass"
2) ""
127.0.0.1:6379> config set requirepass "123456"
OK
127.0.0.1:6379> config get requirepass
(error) NOAUTH Authentication required.
127.0.0.1:6379> set k1 v1
(error) NOAUTH Authentication required.
127.0.0.1:6379> auth 123456
OK
127.0.0.1:6379> config get requirepass
1) "requirepass"
2) "123456"
127.0.0.1:6379> 

 

10.aof持久化,默认不开启使用的是rdb

 

posted @ 2025-03-11 23:45  回忆也交给时间  阅读(9)  评论(0)    收藏  举报