zoukankan      html  css  js  c++  java
  • redis-使用-事物

    什么是redis事物

    Redis的事务是下面4个命令来实现
    1.multi,开启Redis的事务,置客户端为事务态。
    2.exec,提交事务,执行从multi到此命令前的命令队列,置客户端为非事务态。
    3.discard,取消事务,置客户端为非事务态。
    4.watch,监视键值对,作用时如果事务提交exec时发现监视的监视对发生变化,事务将被取消。

    redis事物和数据库事物不一样,可以理解成是一串命令的集合 要么一起执行 要么都不执行

    执行图

    非cas事物使用案例

    正常执行

    本地:0>multi //标识连接为事物连接
    OK
    本地:
    0>get test //命令入队 QUEUED
    本地:
    0>set test2 1 //命令入队 QUEUED
    本地:
    0>set test 2 //命令入队 QUEUED 本地:0>get test //命令入队 QUEUED 本地:0>exec //执行命令 并重置连接状态为事物连接 1) 1 2) OK 3) OK 4) 2 本地:0>

    放弃事物 

    本地:0>get test //修改前test数据值
    3
    
    本地:0>get test2  //修改前test2数据值
    4
    
    本地:0>multi   //标识连接为事物连接
    OK
    
    本地:0>set test 30 //命令入队
    QUEUED
    
    本地:0>set test2 40 //命令入队
    QUEUED
    
    本地:0>discard  //丢失事物队列命令,并重置连接为非事物连接
    OK
    
    本地:0>get test  //test数据未发送改变
    3
    
    本地:0>get test2 //test2数据未发生改变
    4

    命令编辑错误提交事物

    本地:0>get test  //test修改前的值
    3
    
    本地:0>get test2  //test修改前的值
    4
    
    本地:0>multi   //表示连接为事物连接
    OK
    
    本地:0>set test 3333  //修改test
    QUEUED
    
    本地:0>sett tset2 2 //编译错误,不是正确的命令格式
    ERR unknown command `sett`, with args beginning with: `tset2`, `2`, 
    
    本地:0>set test2 4444  //修改test2
    QUEUED
    
    本地:0>exec  //提交报错 并重置事物连接为非事物
    EXECABORT Transaction discarded because of previous errors.
    
    本地:0>get test  //test数据未修改
    3
    
    本地:0>get test2 //test2数据未修改
    4

    命令运行时错误 

    本地:0>get test 
    f
    
    本地:0>get  test2
    5
    
    本地:0>get test3
    2
    
    本地:0>multi //开启事物
    OK
    
    本地:0>set  test2 6 //修改test2
    QUEUED
    
    本地:0>incr test //对值为f的执行+1操作
    QUEUED
    
    本地:0>set test3 8 //修改test3
    QUEUED
    
    本地:0>exec  
    1) OK
    2) ERR value is not an integer or out of range //除了修改test失败其他都修改成功
    3) OK
    本地:0>get test
    f
    
    本地:0>get test2
    6
    
    本地:0>get  test3
    8

    CAS事物案例

    什么是CAS

    参考:点击跳转

    正常执行

    本地:0>get test //监控的key值
    f
    
    本地:0>watch test //监控test key的值 也可以监控多个
    OK
    
    本地:0>multi //开启事物
    OK
    
    本地:0>set test2 1 //修改test2
    QUEUED
    
    本地:0>set test 1 //修改test1
    QUEUED
    
    本地:0>exec //提交事物 并自动取消watch监控
    1) OK
    2) OK
    本地:0>get test //修改成功
    1
    
    本地:0>get test2 //成功
    1

    异常提交

    本地:0>get test //test修改前的值
    1
    
    本地:0>get test2 //test2修改前的值
    1
    
    本地:0>watch test //监控test 也可以监控多个
    OK
    
    本地:0>multi //开启事物
    OK
    
    本地:0>set test 33 //修改test的值
    QUEUED
    
    本地:0>set test2 33 //修改test2的值
    QUEUED
    
    本地:0>exec //提交事物,提交之前我使用另外一个连接改成了555  所以返回null修改失败 并取消test监控
    
    
    本地:0>get test  //另外一个连接 改的值
    555
    
    本地:0>get test2 //未修改成功
    1

    银行转账例子

       public static void main(String[] args)
                throws Exception {
            Jedis conn = new Jedis("127.0.0.1", 6379);
            //=====================删除历史测试数据======================
            //转入方
            String inputUserIdKey=String.format("user:%s", "1");
            //转出方
            String outputUserIdKey=String.format("user:%s", "2");
            conn.del(inputUserIdKey);
            conn.del(outputUserIdKey);
            //设置默认值
            conn.hset(outputUserIdKey,"money","100");
            conn.hset(inputUserIdKey,"money","50");
            boolean processStatus=false;
            int index=0;
            do {
                index++;//重试3次
                processStatus= transaction(conn, "1", "2", 100);
            }while (!processStatus&&index<=3);//cas重试
            //打印转出后金额
            System.out.println(processStatus?"转账成功!":"转账失败!");
            System.out.println("转出方余额:"+conn.hget(outputUserIdKey,"money"));
            System.out.println("转入方余额:"+conn.hget(inputUserIdKey,"money"));
        }
    
        public static boolean transaction(Jedis conn, String inputUserId, String outUserId, Integer tranMoney) {
            String outUserKey = String.format("user:%s", outUserId);
            String inputUserKey = String.format("user:%s", inputUserId);
            //监控转出方金额 防止金额变化 不足以扣除
            conn.watch(String.format("user:%s", outUserId));
            //分为单位
            Long money = Long.valueOf(conn.hget(outUserKey, "money"));
            if (money < tranMoney) {
                System.out.println("余额不足");
                return false;
            }
            Transaction transaction= conn.multi();
            transaction.hincrBy(outUserKey, "money", -money);
            transaction.hincrBy(inputUserKey, "money", money);
            List<Object> result=  transaction.exec();
            //如果监控值改变返回的是空集合
            return  result!=null&&result.size()>0;
        }
  • 相关阅读:
    ExtJS小技巧
    Oracle 表的行数、表占用空间大小,列的非空行数、列占用空间大小 查询
    NPM 私服
    IDEA 不编译java以外的文件
    SQL 引号中的问号在PrepareStatement 中不被看作是占位符
    Chrome 浏览器自动填表呈现淡黄色解决
    批量删除Maven 仓库未下载成功.lastupdate 的文件
    Oracle 11g 监听很慢,由于监听日志文件太大引起的问题(Windows 下)
    Hibernate 自动更新表出错 建表或添加列,提示标识符无效
    Hibernate 自动更新表出错 More than one table found in namespace
  • 原文地址:https://www.cnblogs.com/LQBlog/p/13321449.html
Copyright © 2011-2022 走看看