SpringBoot整合Redis时踩到的坑
jdk1.8环境,用idea的Spring Initializr创建spring boot项目,版本我选的2.7.6。pom文件添加的依赖如下,仅供参考。
注意commons-pool2选错版本的话可能会报错,具体意思好像是说有什么Factory跟它的版本不匹配。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.11.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
对应的配置文件代码如下(这个基本上是写死的)
@EnableCaching @Configuration public class RedisConfig extends CachingConfigurerSupport { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); RedisSerializer<String> redisSerializer = new StringRedisSerializer(); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); // om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); 这个函数已经是duplicated,用下面这个 om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); jackson2JsonRedisSerializer.setObjectMapper(om); template.setConnectionFactory(factory); //key序列化方式 template.setKeySerializer(redisSerializer); //value序列化 template.setValueSerializer(jackson2JsonRedisSerializer); //value hashmap序列化 template.setHashValueSerializer(jackson2JsonRedisSerializer); return template; } @Bean public CacheManager cacheManager(RedisConnectionFactory factory) { RedisSerializer<String> redisSerializer = new StringRedisSerializer(); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); //解决查询缓存转换异常的问题 ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); // 配置序列化(解决乱码的问题),过期时间600秒 RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofSeconds(600)) .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer)) .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)) .disableCachingNullValues(); RedisCacheManager cacheManager = RedisCacheManager.builder(factory) .cacheDefaults(config) .build(); return cacheManager; } }
用RDM,以及Java去连接子系统的redis报错:
Connection: WSLUbuntu > connected Connection: WSLUbuntu > [runCommand] AUTH ******* Connection: WSLUbuntu > [runCommand] PING Connection: Connection error on AUTH: MISCONF Redis is configured to save RDB snapshots, but it is currently not able to persist on disk. Commands that may modify the data set are disabled, because this instance is configured to report errors during writes if RDB snapshotting fails (stop-writes-on-bgsave-error option). Please check the Redis logs for details about the RDB error.
上一个笔记是改密码解决了问题,没想到今天又出现一样的错误。总之它让我去看Redis log,就先看看。
看起来是权限不够的问题,
joey@JoeyRedfield615:~$ sudo chmod 777 /var/lib/redis/rdb
这样就解决了。网上有很多各种各样的解决方案(Redis踩坑——MISCONF Redis is configured to save RDB snapshots),但是最好还是要根据生成的日志去判断要干什么。
事务、锁机制、乐观锁,做秒杀案例
Redis的事务操作和MySQL是不太一样的。
Redis事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
Redis事务的主要作用就是串联多个命令防止别的命令插队。
Redis事务三特性
- 单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
- 没有隔离级别的概念:队列中的命令没有提交之前都不会实际被执行,因为事务提交前任何指令都不会被实际执行
- 不保证原子性:事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚。
Transaction(事务) — Redis 命令参考 (redisfans.com)
Muiti、Exec、discard
从输入Multi命令开始,输入的命令都会依次进入命令队列中,但不会执行,直到输入Exec后,Redis会将之前的命令队列中的命令依次执行。组队的过程中可以通过discard来放弃组队。
如果执行阶段某个命令报出了错误(比如说给字符串值执行incr),则只有报错的命令不会被执行,而其他的命令都会执行,不会回滚。
不过如果有命令是语法错误(类似Java出现Error的情况),则整个队列的命令都不会被执行。
事务冲突的问题
直接点就是网购时的问题,如果有多个请求想要扣钱,那系统可不能出现让用户扣钱扣到负数的情况。。
由此就引出了乐观锁和悲观锁的解决方案。
乐观锁
乐观锁(Optimistic Lock),顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。
乐观锁适用于多读的应用类型,这样可以提高吞吐量。Redis就是利用这种check-and-set机制实现事务的。
悲观锁
悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block(阻塞)直到它拿到锁。
传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。(有一种MySQL事务等级设置成串行化的美)
Watch/Unwatch
WATCH key [key ...]
这个是可以实现乐观锁的命令。它可以监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。
由第9步可以看到结果为(nil),因为在⑨执行之前,debt已经被改过了。
标签:事务,SpringBoot,Redis,om,笔记,命令,new,执行 From: https://www.cnblogs.com/joey-redfield/p/17050196.html