Redis事务以及spring中的应用

目录

 

一. Redis的事务支持

 1. 使用事务

1.1 开启执行事务

1.2 取消事务

1.3 事务的异常处理

2. 事务实现监控乐观锁:watch

二. spring/springboot的支持jedis

1. jedis 

2. jedis操作事务

3. springboot整合Redis

3.1 springboot中依赖配置的源码分析

3.2 使用

3.3 redis 的序列化以及自定义配置bean


一. Redis的事务支持

①Redis单条指令是原子性的,但是Redis的事务是不保证原子性的;

②一个事务中的命令会被序列化,并且在事务中执行顺序是按照顺序一次性执行的,并且不受其它事务的影响;

③没有隔离性没有隔离级别

④Redis事务是按照队列的方式,有序排队,事务在接受命令时并没有执行,而是在执行执行命令后,一次性执行。

 1. 使用事务

1.1 开启执行事务

使用事务分为三个过程:启动事务、添加命令入队、执行命令

1.2 取消事务

开启事务后,添加命令,如果需要取消事务:discard,此时该事务的所有命令都会取消执行

执行discard后,该事务中所有命令将会被取消执行

1.3 事务的异常处理

①事务的执行语法存在问题(即指令有问题)

命令出现问题,事务在执行时会报错,所有的命令都不会执行。

②事务在执行过程中出现问题(setnx失败等)

命令语法正确,但是执行时失败了,例如,setnx k1 v1 失败了返回了0。事务执行后会正常执行,不会影响其他语句。所以它是不保证所有语句的原子性的。

2. 事务实现监控乐观锁:watch

通过watch来监视某个key,在启动事务后,如果另外一个线程修改了该key,事务在执行时,会比较该key值是否变更,如果变更了则事务会执行失败。

事务执行完成后,再次执行事务watch会失效

可以通过UNwatch解锁,后重新watch加锁。获取最新数据后执行事务。

通过watch锁定某个key后,就相当于获取到了这个key此时的version,当其他线程修改了该key,则该key在事务中就会执行失败。可以先将数据UNWATCH后重新watch再次执行事务。

二. spring/springboot的支持jedis

1. jedis 

jedis是java连接Redis的jar包中间件,通过导入jar包后就可以直接操作Redis,方法与Redis的命令一模一样。
使用非常简单

    public static void main(String[] args) {
        //如果Redis开启了密码验证,需要借助JedisShardInfo设置密码
        JedisShardInfo info = new JedisShardInfo("124.71.112.168", 6379);
        info.setPassword("root");
        Jedis jedis = new Jedis(info);
        System.out.println(jedis.ping());
        jedis.set("k1", "v1");
        jedis.get("k1");
        //压栈
        jedis.lpush("list", "a", "b", "c");
        //出栈
        for (int i = 0; i < jedis.llen("list"); i++) {
            System.out.println(jedis.lpop("list"));
        }
    }

2. jedis操作事务

    public static void main(String[] args) {
        //如果Redis开启了密码验证,需要借助JedisShardInfo设置密码
        JedisShardInfo info = new JedisShardInfo("124.71.112.168", 6379);
        info.setPassword("root");
        Jedis jedis = new Jedis(info);
        System.out.println(jedis.ping());
        jedis.flushAll();
        //jedis进行事务控制,启动事务,获取Redis的事务对象
        Transaction multi = jedis.multi();
        JSONObject u1 = new JSONObject();
        u1.put("id", 1127);
        u1.put("name", "张三");
        JSONObject u2 = new JSONObject();
        u2.put("id", 1128);
        u2.put("name", "李四");
        try {
            multi.set("user:1127", u1.toString());
            multi.set("user:1128", u1.toString());
            int i = 1/0;
            multi.exec();  //执行事务
        } catch (Exception e) {
            multi.discard(); //如果抛出异常,就回滚事务
            e.printStackTrace();
        } finally {
            System.out.println(jedis.get("user:1127"));
            jedis.close();
        }
    }

3. springboot整合Redis

springboot中使用Redis,可以导入spring-data包下的jar包依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>${redis.verison}</version>
</dependency>
  • springboot从2.X后,连接Redis底层就不再使用jedis进行连接了,而是使用了lettuce作为底层进行连接Redis了

区别:

  • jedis:采用直连模式,在多线程下是不安全的,需要借助jedisPool连接,造成了线程的浪费
  • lettuce:底层是通过netty实现,能够实现多线程共享资源,减少了线程数,并且在多线程情况下是安全的。效率更高。连接池尽量使用lettuce的连接池

lettuce的相关介绍参考博文:https://www.cnblogs.com/throwable/p/11601538.html(引用,如有侵权请联系删除)

Redis的所有使用,都需要序列化

3.1 springboot中依赖配置的源码分析

通过JRE中springboot-auto-configure包下的META-INF下的spring.factories文件中,找到Redis相关的配置类

其中RedisAutoConfiguration源码如下:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class) //这是Redis的自动配置文件属性类
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean(name = "redisTemplate") //该注解表示当不存在该bean时则会创建,存在不会创建,可自定义该bean
	public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
			throws UnknownHostException {
        //默认创建的泛型都是object类型的,使用时需要强转一下
		RedisTemplate<Object, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

	@Bean
	@ConditionalOnMissingBean  //专门配置了一个string类型的bean,操作string类型时,可以直接使用该bean
	public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
			throws UnknownHostException {
		StringRedisTemplate template = new StringRedisTemplate();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

}

3.2 使用

properties文件中配置了Redis的连接信息后,Redis启动时就自动注册成bean可以使用了。

Redis存储的key都是经过序列化转换之后的key。

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    void contextLoads() {
        // opsForValue: 相当于操作string类型 opsFor**就是操作某某类型的意思。后面的API几乎与命令一致
        redisTemplate.opsForValue().set("kkk1", "vvv1");
        redisTemplate.opsForList().leftPush("list", 1223);
        redisTemplate.opsForHash().put("user_1", "name", "张三");
        //常用的操作可以直接通过redisTemplate对象获取API
        redisTemplate.delete("");
        redisTemplate.discard();
        redisTemplate.rename("","");
        redisTemplate.expire("","");
        //获取连接对象
        RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
        connection.flushAll();
        connection.ping();
        System.out.println(redisTemplate.opsForValue().get("kkk1"));
    }

3.3 redis 的序列化以及自定义配置bean

java中如果没有通过特殊设置,使用Redis存储对象时,会因为没有序列化导致无法存储报错。所以实体对象都需要进行序列化。

通过默认的RedisTemplate存储的KV都是乱码的,是因为Redis在存储的时候必须要进行序列化,此时没有特殊配置,使用的是默认的序列化,源码如下:

可以通过自定义RedisTemplate的bean,来自定义序列化。

①我们在Redis的源码中可以发现,

所以我们只需要在注册RedisTemplate时,将序列化指定初始化进去,就可实现自定义序列化。连接工厂还是自动读取配置

如果使用默认的JDK序列化方式,并且实体类没有实现JDK的序列化接口,则存储实体就会抛出异常,所以需要在使用Redis时,自定义序列化方式,代码如下:将KEY的序列化方式设置为string,value的序列化方式设置为json。此时就不需要实体类序列化了

    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
            throws UnknownHostException {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        //注入连接工厂
        template.setConnectionFactory(redisConnectionFactory);
        //创建序列化类:实现了RedisSerializer接口的类都可以使用
        Jackson2JsonRedisSerializer<Object> fastJsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        fastJsonRedisSerializer.setObjectMapper(om);

        //创建string类型的序列化对象
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        //key使用string的序列化
        template.setKeySerializer(stringRedisSerializer);
        //value使用json的序列化
        template.setValueSerializer(fastJsonRedisSerializer);
        //key使用string的序列化
        template.setHashKeySerializer(stringRedisSerializer);
        //value使用json的序列化
        template.setHashValueSerializer(fastJsonRedisSerializer);
        //使参数生效
        template.afterPropertiesSet();
        return template;
    }

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
处理、分离和浓缩等操作,以便更准确地进行检测。 四、结论 随着科技是的,Spring Boot的`@Transactional`注解可以支持Redis事务。但需要注意的是,Redis事务的不断进步,新型传感器及其相关检测技术不断涌现,它们具有小型模型与关系型数据库事务模型有很大的区别。Redis事务不是像关系型数据库一样的ACID事务,而是一种乐观锁事务模型,也被称为WATCH/MULTI化、高灵敏度、快速响应和可重复使用等特点,被广泛应用于医疗、/EXEC事务模型。 在使用`@Transactional`注解时,需要在方法上添加该注解,并且在方法工业、环境等领域。未来,随部使用`multi()`方法开启事使用`exec()`方法提交事务,或者使用`discard()`方法回滚事务。具体示例代码如下: ```java @Service public class RedisService { @Autowired private RedisTemplate<String, Object> redisTemplate; @Transactional public void updateRedisData() { // 开启事务 redisTemplate.multi(); try { // 执行一系列Redis操作 redisTemplate.opsForValue().set("key1", "value1"); redisTemplate.opsForValue().increment("key2", 1); // 提交事务 redisTemplate.exec(); } catch (Exception e) { // 回滚事务 redisTemplate.discard(); } } } ``` 需要注意的是,Redis事务并不支持回滚到保存点,只能回滚到事务开始前的状态。因此,在使用Redis事务时,需要特别小心,确保每个操作都能正确执行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值