zoukankan      html  css  js  c++  java
  • Redis事务

    事务的作用是为了确保多个连续的操作的原子性,Redis也支持事务,但与MySQL不同,Redis的事务模型并不严格,使用前还是需要对其特性准确把握,避免误用。
    【基本用法】
    Redis中与事务相关的指令分别是multi,exec,discard,watch,unwatch。
    multi表示事务开始,类似begin。在multi提交之后,所有的指令会被加载到一个queue中,而不是立即执行,当exec提交之后,之前提交到queue中的指令会被依次执行。而如果在exec之前提交了discard指令,那么queue中的指令就会被丢弃。而watch可以实现一个乐观锁。
    如图,展示了在multi提交指令后,exec执行之前,其它的客户端无法读取books中的数据:

    【不严格的事务模型】
    当我们提交了一批指令,放在queue并提交了exec执行时,redis可以保证queue中指令的串行,不被其它客户端指令所影响,但是如果当中某个指令执行结果错误,redis是不会进行回滚操作的。
    这其实和pipeline很像,但是与pipeline的区别在于,pipeline中的指令不能保证与其它客户端提交的指令“隔离”,也就是pipeline中的指令执行期间,其它客户端的指令也会穿插执行;而exec提交后,queue中的指令是具有“隔离性”的,其它不在queue中的指令是不会执行的。即Redis的事务保证不被其它指令和事务打断的权力。
    【事务一般和pipeline结合使用】
    Redis是个单线程模型,所以每个单独的指令在执行时天然就是原子性的,不会被其它指令影响到。所以涉及事务操作的,一般都是一批指令一起执行,为了减少客户端等待的RTT时间,事务往往和管道结合使用。

    public class App {
        public static void main(String[] args) {
            Jedis jedis = RedisClientUtil.getJedisClient();
            Pipeline pipelined = jedis.pipelined();
            pipelined.multi();
            for (int i = 0; i < 10; i++) {
                pipelined.set("key" + i, "value" + i);
            }
            Response<List<Object>> response = pipelined.exec();
            pipelined.close();
            List<Object> objects = response.get();
            System.out.println("objects.toString() = " + objects.toString());
        }
    }

    执行结果如图:

    这样就可以保证pipeline中的指令可以依次执行,且不被其他客户端提交的指令插队。
    【watch实现乐观锁】
    使用pipeline加事务机制可以保证我们提交的指令可以排他顺序执行,但如果操作过程需要用到指令无法完成的操作,必须在内存中处理怎么办呢?
    Redis提供了一种watch机制,它就是一种乐观锁。使用思路如下:
    在multi之前用watch指令盯住一个或者多个关键变量,当事务执行时,也就是服务器收到了exec指令要开始顺序执行缓存的事务队列时,Redis会检查关键变量自watch之后是否被修改了(包括当前事务所在的客户端)。如果关键变量被修改过,那么exec指令就会返回NULL回复告知客户端事务执行失败。
    是不是很像MySQL中用于对比版本号的version:

    select version as queryVersion from table;
    update table set status=newStatus,version=queryVersion+1 where version=queryVersion;

    这里的watch其实就很像MySQL中用于保证版本的version=queryVersion,在前面的盯住变量以保证事务操作发生于变量发生变化之前。
    注意Redis禁止在multi和exec之间进行watch,必须在multi之前就用watch盯住变量。
    【实验-一个乐观锁的实验】
    对于我们需要比较的version,可以先进行watch,保证后续的操作都是value不变的前提下进行的。
    watch住之后,可以对这个value进行一次对比,如果对比发现已经不是之前的值,可以直接放弃后续的操作。如果值没有异常,那么此后的操作直到exec执行,都可以保证这个事务队列的操作是基于这个变量不变的前提执行的。如果变量发生变化,exec会返回一个null。看以几个例子:
    首先,在set一个key,value之后,外部客户端对其进行修改,watch住之后,对比value发现已经变化,直接返回:

    第二次,我们在watch住之后,exec之前,将value值修改,此时exec返回了null,于是我们知道这个double失败了:

    第三次,没有其他客户端修改key的value:

  • 相关阅读:
    RPi.GPIO
    Linux(Ubuntu)下查看摄像头是否挂载及挂载的USB端口号
    python--python脚本中调用shell命令
    pip
    MQTT
    Ubuntu安装pip
    python Opencv开启USB摄像头并录像保存
    视频 客服
    Android Test
    MAC系统下ADB入门与简单使用
  • 原文地址:https://www.cnblogs.com/bruceChan0018/p/15712979.html
Copyright © 2011-2022 走看看