Redis事务、Jedis、SpringBoot整合Redis

学习视频链接,示以尊重:https://space.bilibili.com/95256449/video


一、Redis 事务

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

注意:

  • Redis 单条命令是保证原子性的,但是 Redis 的事务不保证原子性
  • Redis 事务没有隔离级别的概念
  • 所有的命令在事务中,并没有直接被执行。只有发起执行命令的时候才会执行

Redis 事务执行阶段:

  • 开启事务:multi
  • 命令入队:…
  • 执行事务:exec
1.1 正常执行
127.0.0.1:6379> multi #开启事务
OK
127.0.0.1:6379> set k1 v1 #命令入队
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> exec #执行事务
1) OK
2) OK
3) "v2"
4) OK
1.2 放弃事务

discard 命令,会取消掉事务,事务队列中的命令都不会被执行。

127.0.0.1:6379> multi #开启事务
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> DISCARD #取消事务
OK
127.0.0.1:6379> get k4
(nil)
1.3 出现异常
  • 如果出现了编译型异常,事务中所有的命令都不会被执行
  • 运行时异常,错误命令会抛出异常,但其他命令可以正常执行

二、Redis 乐观锁

悲观锁:很悲观,认为什么时候都会出问题,无论做什么都会加锁

乐观锁:很乐观,认为什么时候都不会出问题,所以不会上锁。更新数据的时候去判断一下,在此期间是否有人修改过这个数据

Redis 监视测试:(使用watch来当做redis的乐观锁操作)

正常执行成功:

127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> WATCH money # 监视money对象
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 20
QUEUED
127.0.0.1:6379> incrby out 20
QUEUED
127.0.0.1:6379> exec # 事务正常结束,数据期间没有发生变动,这个时候就可以正常执行成功
1) (integer) 80
2) (integer) 20

多线程操作值,可能会执行失败:

127.0.0.1:6379> watch money # 监视 money
OK 
127.0.0.1:6379> multi 
OK 
127.0.0.1:6379> DECRBY money 10 
QUEUED 
127.0.0.1:6379> INCRBY out 10 
QUEUED 
127.0.0.1:6379> exec # 执行事务之前,另外一个线程,修改了值,这个时候,就会导致事务执行失败! 
(nil)
127.0.0.1:6379> unwatch money # 解除监视(解锁)
OK

如果事务执行失败,Redis会这么处理(自旋锁):

  1. 解锁(unwatch);
  2. 获取最新的值,再次监视(watch);
  3. 比较监视的值是否发生了变化,如果没有发生变化,则执行成功,否则执行失败。

三、Jedis

Jedis 是 Redis 官方推荐的 java 连接开发工具,是使用 Java 操作 Redis 的中间件。

对应依赖:


<dependency>
    <groupId>redis.clientsgroupId>
    <artifactId>jedisartifactId>
    <version>3.3.0version>
dependency>

连接数据库:

Jedis jedis = new Jedis("127.0.0.1", 6379);

进行事务操作:

public class TestPing {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        jedis.flushDB();
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("hello", "world");
        jsonObject.put("name", "xingyu");
        // 开启事务
        Transaction multi = jedis.multi();
        String result = jsonObject.toJSONString();
        multi.watch("user1","user2");
        try {
            multi.set("user1", result);
            multi.set("user2", result);
            int i = 1 / 0; // 代码抛出异常事务,执行失败!
            multi.exec(); // 执行事务!
        } catch (Exception e) {
            multi.discard(); // 放弃事务 e.printStackTrace();
        } finally {
            System.out.println(jedis.get("user1"));
            System.out.println(jedis.get("user2"));
            jedis.close(); // 关闭连接
        }
    }
}

四、SpringBoot整合Redis

在 SpringBoot2.x 之后,原来使用的 jedis 被替换为了 lettuce

  • jedis : 采用的直连,多个线程操作的话,是不安全的,如果想要避免不安全的,使用 jedis pool 连接池! 更像 BIO 模式
  • lettuce : 采用 netty,实例可以再多个线程中进行共享,不存在线程不安全的情况。可以减少线程数据,更像 NIO 模式
4.1 连接测试

1、导入依赖

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-data-redisartifactId>
dependency>

2、配置连接

spring.redis.host=127.0.0.1
spring.redis.port=6379

3、测试

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    void contextLoads() {
//        RedisConnection redisConnection = redisTemplate.getConnectionFactory().getConnection();
//        redisConnection.flushAll();

        redisTemplate.opsForValue().set("mykey","星宇");
        System.out.println(redisTemplate.opsForValue().get("mykey"));
    }
4.2 自定义RedisTemplate
@Configuration
public class RedisConfig {

    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        // 为了自己开发方便,一般直接使用 
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(factory);
        // Json序列化配置
        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); // String 的序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的value序列化方式采用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        
        template.afterPropertiesSet();
        return template;
    }
}

你可能感兴趣的:(数据库)