美文网首页
Redis 事务

Redis 事务

作者: SheHuan | 来源:发表于2021-07-12 20:55 被阅读0次

相比关系型数据库中的事务模型,Redis 中事务要简单一些。Redis 中的事务不能保证原子性,也就是说,事务中某一个命令执行时出现异常不会影响其它命令的执行;Redis 中的事务具有隔离性,即当前事物可以不被其它事务打断,但没有隔离级别的概念。

一、事务基本概念

在 Redis 中,事务相关的常用命令如下:

  • multi:开启事务。
  • 输入要执行的 Redis 命令,可以理解为命令入队列。
  • exec:执行事务,会依次执行队列中的命令,各个命令的执行结果会在exec结束后统一返回。
  • discard:取消事务,放弃执行队列中的命令,注意,已经开始执行的事务是无法取消的。
  • watch:在通过multi开启事务之前,我们可以使用watch命令监控指定的 key,在事务执行之前,如果被监控的 key 对应的值被修改了,exec将放弃执行当前事务队列中的所有命令,这也是一种乐观锁实现。
  • unwatch:是和watch对应的命令,用来取消对 key 的监控,但如果执行了execdiscard命令,则事务中所有被监控的 key 都将自动取消监控,则无需再手动执行该命令了。

下边我们结合Jedis来看看如何使用事务。

二、事务的基本操作

public class TransactionTest {
    public static void main(String[] args) {
        new TransactionTest().test1();
    }

    public void test1() {
        JedisPool jedisPool = new JedisPool("localhost", 6379);
        Jedis jedis = jedisPool.getResource();
        jedis.auth("shehuan");
        jedis.flushDB();
        // 开启事务
        Transaction tx = jedis.multi();
        try {
            // 命令入队
            tx.set("key1", "value1");
            tx.incr("key1");
            tx.set("key2", "10");
            tx.incr("key2");
            // 执行事务
            List<Object> results = tx.exec();
            // 查看事务执行结果
            results.forEach(r -> {
                System.out.println(r.toString());
            });
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            jedis.close();
        }
    }
}

从上边可以看出tx.incr("key1")命令虽然执行失败,但不影响其它命令,事务正常执行结束,这也验证了 Redis 中的事务不能保证原子性。

三、取消事务

public class TransactionTest {
    public static void main(String[] args) {
        new TransactionTest().test2();
    }

    public void test2() {
        JedisPool jedisPool = new JedisPool("localhost", 6379);
        Jedis jedis = jedisPool.getResource();
        jedis.auth("shehuan");
        jedis.flushDB();
        // 开启事务
        Transaction tx = jedis.multi();
        try {
            // 命令入队
            tx.set("key1", "value1");
            tx.set("key2", "10");
            // 制造异常
            int i = 1 / 0;
            // 执行事务
            List<Object> results = tx.exec();
            // 查看事务执行结果
            results.forEach(r -> {
                System.out.println(r.toString());
            });
        } catch (Exception e) {
            e.printStackTrace();
            // 取消事务
            tx.discard();
            System.out.println("key1=" + jedis.get("key1"));
            System.out.println("key2=" + jedis.get("key2"));
        } finally {
            jedis.close();
        }
    }
}

在事务执行前,我们手动制造了异常,这样事务就不会执行,捕获异常后再取消事务,如果有业务上的异常我们可以这样处理。注意,在事务执行过程中发生异常是无法被捕获的,文中第一个例子就说明了这一点。

四、watch 监控

有关watch命令的作用在前边已经介绍过了,下边通过一个简单的商品抢购例子看下具体的用法。

public class TransactionTest {
    public static void main(String[] args) {
        new TransactionTest().test3();
    }

    public void test3() {
        JedisPool jedisPool = new JedisPool("localhost", 6379);
        Jedis jedis = jedisPool.getResource();
        jedis.auth("shehuan");
        jedis.flushDB();

        // 设置商品库存为1000件
        jedis.set("stock", "1000");
        // 监控库存
        jedis.watch("stock");
        // 获取库存
        int stock = Integer.parseInt(jedis.get("stock"));
        // 如果库存大于购买数量
        if (stock > 10) {
            stock = stock - 10;
        } else {
            // 取消监控
            jedis.unwatch();
            return;
        }
        // 开启事务
        Transaction tx = jedis.multi();
        try {
            // 减扣库存
            tx.set("stock", String.valueOf(stock));
            // 执行事务
            List<Object> results = tx.exec();
            // 如果事务执行过程中发现监控的 key 对应的值发生改变,也就是库存在其它地方被修改,则事务的执行结果为 null
            if (results == null) {
                System.out.println("库存减扣失败!");
            } else {
                System.out.println("剩余库存:" + jedis.get("stock"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            jedis.close();
        }
    }
}

在执行事务代码行打上断点,然后运行程序:


在 Redis 客户端窗口修改库存:


现在释放断点,让程序继续执行,最终结果如下:


由于在程序监控库存后,我们又在客户端窗口修改了库存,导致事务执行时发现监控的 key 对应的值发生了变化,所以放弃执行事务中的命令,不会去减扣库存,此时事务的执行结果为null

正常情况下,如果我们不人工干预,则结果符合我们的预期:


相关文章

  • redis系列(十):事务

    redis有事务么? redis官方说是有事务的。但这个事务不是我们普遍理解的mysql事务。 redis的事务不...

  • 九、Redis 事务

    Redis 事务 Redis事务描述: Redis事务允许在单个步骤中执行一组命令。以下是Redis事务的两个属性...

  • Redis事务

    redis事务机制 Redis事务与传统关系型事务的比较

  • Redis事务

    转载自Redis之Redis事务 Redis事务的概念: Redis 事务的本质是一组命令的集合。事务支持一次执行...

  • redis中的事物、消息订阅、持久化

    Redis 中的事务 Redis支持简单的事务 Redis与 mysql事务的对比 注: rollback与dis...

  • Redis简单操作记录

    Redis事务 1.Redis事务本质:一组命令的集合,加入队列,然后执行,执行完事务结束。 redis事务: ①...

  • JavaGuide知识点整理——Redis面试题总结(下)

    Redis事务 如何使用Redis事务? Redis可以通过multi,exec,discard和watch等命令...

  • redis 常用指令

    Redis 的事务 Redis 的事务处理与 RDBMS 的事务有一些不同。首先 Redis 不支持事务的回滚机制...

  • Redis学习笔记:事务

    Redis学习笔记:事务 原文链接:Redis学习笔记:事务 一、事务的描述 和MySQL一样,Redis中也有事...

  • 事务

    简介 MULTI、EXEC、DISCARD、WATCH是redis事务的基础。事务特征如下: redis事务允许将...

网友评论

      本文标题:Redis 事务

      本文链接:https://www.haomeiwen.com/subject/pndfsltx.html