【Redis缓存机制】详解Java连接Redis_Jedis_事务

Jedis事务

我们使用JDBC连接Mysql的时候,每次执行sql语句之前,都需要开启事务;在MyBatis中,也需要使用openSession()来获取session事务对象,来进行sql执行、查询等操作。当我们对数据库的操作结束的时候,是事务对象负责关闭数据库连接。

事务对象用于管理、执行各种数据库操作的动作。它能够开启和关闭数据库连接,执行sql语句,回滚错误的操作。

我们的Redis也有事务管理对象,其位于redis.clients.jedis.Transaction下。

Jedis事务的相关代码:

package cn.com.redis; 

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction; 

public class Test7 {
  public static void main(String[] args) {
    Jedis jedis = new Jedis("192.168.248.129",6379); 

    Transaction transaction=jedis.multi();//返回一个事务控制对象 

    //预先在事务对象中装入要执行的操作
    transaction.set("k4", "v4");
    transaction.set("k5", "v5"); 

    transaction.exec();//执行
  }
}

我们查看一下redis:

发现数据已经加入进去

我们把k4的value和k5的value改为“v44”和“v55”,然后在transaction.exec()语句后加入transaction.discard()语句:

package cn.com.redis; 

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction; 

public class Test7 {
  public static void main(String[] args) {
    Jedis jedis = new Jedis("192.168.248.129",6379); 

    Transaction transaction=jedis.multi();//返回一个事务控制对象 

    //预先在事务对象中装入要执行的操作
    transaction.set("k4", "v44");
    transaction.set("k5", "v55"); 

    transaction.discard();//回滚
  }
}

会发现数据插入操作被回滚,redis中那两个值未被改变:

我们模拟一个刷一次信用卡的交易,使用redis的事务来处理一些逻辑:

package cn.com.redis; 

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction; 

public class TestTransaction {
  //模拟信用卡消费和还款
  public static void main(String[] args) {
    TestTransaction t = new TestTransaction();
    boolean retValue = t.transMethod(100);
    if(retValue){
      System.out.println("使用信用卡消费成功!");
    }else{
      System.out.println("使用信用卡消费失败!");
    } 

  } 

  /**
   * 通俗点讲,watch命令就是标记一个键,如果标记了一个键,
   * 在提交事务前如果该键被别人修改过,那事务就会失败,这种情况通常可以在程序中
   * 重新再尝试一次。
   *
   * 首先标记了balance,然后检查余额是否足够,不足就取消标记,并不做扣减;
   * 足够的话,就启动事务进行更新操作。
   * 如果在此期间键balance被其他人修改,拿在提交事务(执行exec)时就会报错,
   * 程序中通常可以捕获这类错误再重新执行一次,直到成功。
   * */
  private boolean transMethod(int amount) { 

    System.out.println("您使用信用卡预付款"+amount+"元"); 

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

    int balance = 1000;//可用余额
    int debt;//欠额
    int amtToSubtract = amount;//实刷额度 

    jedis.set("balance", String.valueOf(balance));
    jedis.watch("balance");
    //jedis.set("balance", "1100");//此句不该出现,为了模拟其他程序已经修改了该条目
    balance = Integer.parseInt(jedis.get("balance"));
    if(balance < amtToSubtract){//可用余额小于实刷金额,拒绝交易
      jedis.unwatch();
      System.out.println("可用余额不足!");
      return false;
    }else{//可用余额够用的时候再去执行扣费操作
      System.out.println("扣费transaction事务开始执行...");
      Transaction transaction = jedis.multi();
      transaction.decrBy("balance",amtToSubtract);//余额减去amtToSubtract的钱数
      transaction.incrBy("debt", amtToSubtract);//信用卡欠款增加amtToSubtract的钱数
      transaction.exec();//执行事务
      balance = Integer.parseInt(jedis.get("balance"));
      debt = Integer.parseInt(jedis.get("debt"));
      System.out.println("扣费transaction事务执行结束..."); 

      System.out.println("您的可用余额:"+balance);
      System.out.println("您目前欠款:"+debt);
      return true;
    }
  } 

}

此代码就是模拟用户使用信用卡刷了100元的东西,此时应该减去信用卡的可用余额100元,增加100元的欠款。

运行结果:

redis的结果:

证明我们的操作是成功的。

加watch命令是为了在事务执行的过程中,防止其它的操作打断事务,或者是影响事务的计算结果,导致“幻读”、“脏数据”等异常情况的发生。watch命令建立了一个键,一旦发现执行过程中该键被别人修改过,那事务就会失败,程序中通常可以捕获这类错误再重新执行一次,直到成功。所以watch命令可以保证数据的同步安全。

为了证明watch命令的用途,我们把上面代码里面的jedis.set("balance", "1100");注释释放,然后transMethod方法抛出打断异常:throws InterruptedException,main方法捕获打断异常,然后弹出相应警告框。

package cn.com.redis; 

import java.util.List; 

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction; 

public class TestTransaction {
  //模拟信用卡消费和还款
  public static void main(String[] args) {
    TestTransaction t = new TestTransaction();
    boolean retValue=false;
    boolean Interrupted = false; 

    try {
      retValue = t.transMethod(100);
    } catch (InterruptedException e) {
      Interrupted = true;
      System.out.println("事务被打断,请重新执行!");
    }finally{
      if(retValue){
        System.out.println("使用信用卡消费成功!");
      }else{
        if(!Interrupted){
          System.out.println("使用信用卡消费失败!余额不足!");
        }
      }
    }
  } 

  /**
   * 通俗点讲,watch命令就是标记一个键,如果标记了一个键,
   * 在提交事务前如果该键被别人修改过,那事务就会失败,这种情况通常可以在程序中
   * 重新再尝试一次。
   *
   * 首先标记了balance,然后检查余额是否足够,不足就取消标记,并不做扣减;
   * 足够的话,就启动事务进行更新操作。
   * 如果在此期间键balance被其他人修改,拿在提交事务(执行exec)时就会报错,
   * 程序中通常可以捕获这类错误再重新执行一次,直到成功。
   * */
  private boolean transMethod(int amount) throws InterruptedException{ 

    System.out.println("您使用信用卡预付款"+amount+"元"); 

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

    int balance = 1000;//可用余额
    int debt;//欠额
    int amtToSubtract = amount;//实刷额度 

    jedis.set("balance", String.valueOf(balance));
    jedis.watch("balance");
    jedis.set("balance", "1100");//此句不该出现,为了模拟其他程序已经修改了该条目
    balance = Integer.parseInt(jedis.get("balance"));
    if(balance < amtToSubtract){//可用余额小于实刷金额,拒绝交易
      jedis.unwatch();
      System.out.println("可用余额不足!");
      return false;
    }else{//可用余额够用的时候再去执行扣费操作
      System.out.println("扣费transaction事务开始执行...");
      Transaction transaction = jedis.multi();
      transaction.decrBy("balance",amtToSubtract);//余额减去amtToSubtract的钱数
      transaction.incrBy("debt", amtToSubtract);//信用卡欠款增加amtToSubtract的钱数
      List<Object> result = transaction.exec();//执行事务 

      if(result==null){//事务提交失败,说明在执行期间数据被修改过 

        System.out.println("扣费transaction事务执行中断...");
        throw new InterruptedException(); 

      }else{//事务提交成功
        balance = Integer.parseInt(jedis.get("balance"));
        debt = Integer.parseInt(jedis.get("debt"));
        System.out.println("扣费transaction事务执行结束..."); 

        System.out.println("您的可用余额:"+balance);
        System.out.println("您目前欠款:"+debt); 

        return true;
      }
    }
  } 

}

再运行一下,看一下效果:

这就说明了,如果在watch命令执行后和事务提交之前,如果数据发生了修改操作,事务执行就不会成功,此举保证了数据的安全性。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Redis 事务与过期时间详细介绍

    Redis 事务与过期时间详细介绍 一.Redis事务: Redis中支持事务,事务即为当我们需要执行几条命令时,要么这几条命令都不执行,要么都执行: 1.开始事务写入: multi 2.然后写入命令,注意写完事务要执行的每条命令之后回车即可,命令会自动入队: lpush art:1 hello lpush art:1 nihao 3.执行事务: exec Redis则会保证事务中的所有命令要么都执行,要么都不执行. 二.Redis过期时间: 实际开发中经常会遇到一些有时效性的数据,比如缓存,过

  • Redis教程(八):事务详解

    一.概述: 和众多其它数据库一样,Redis作为NoSQL数据库也同样提供了事务机制.在Redis中,MULTI/EXEC/DISCARD/WATCH这四个命令是我们实现事务的基石.相信对有关系型数据库开发经验的开发者而言这一概念并不陌生,即便如此,我们还是会简要的列出Redis中事务的实现特征: 1). 在事务中的所有命令都将会被串行化的顺序执行,事务执行期间,Redis不会再为其它客户端的请求提供任何服务,从而保证了事物中的所有命令被原子的执行. 2). 和关系型数据库中的事务相比,在Red

  • redis中事务机制及乐观锁的实现

    Redis事务机制 在MySQL等其他数据库中,事务表示的是一组动作,这组动作要么全部执行,要么全部不执行. Redis目前对事物的支持相对简单.Redis只能保证一个client发起的事务中的命令可以连续的执行,而中间不会插入其他的client命令.当一个client在一个链接中发出multi命令时,这个链接会进入一个事务上下文,该连接后续的命令不会立即执行,而是先放到一个队列中,当执行exec命令时,redis会顺序的执行队列中的所有命令. Multi 开启事务: 127.0.0.1:637

  • 【Redis缓存机制】详解Java连接Redis_Jedis_事务

    Jedis事务 我们使用JDBC连接Mysql的时候,每次执行sql语句之前,都需要开启事务:在MyBatis中,也需要使用openSession()来获取session事务对象,来进行sql执行.查询等操作.当我们对数据库的操作结束的时候,是事务对象负责关闭数据库连接. 事务对象用于管理.执行各种数据库操作的动作.它能够开启和关闭数据库连接,执行sql语句,回滚错误的操作. 我们的Redis也有事务管理对象,其位于redis.clients.jedis.Transaction下. Jedis事

  • C基础 redis缓存访问详解

    引言 先说redis安装, 这里采用的环境是. Linux version 4.4.0-22-generic (buildd@lgw01-41) (gcc version 5.3.1 20160413 (Ubuntu 5.3.1-14ubuntu2) ) #40-Ubuntu SMP Thu May 12 22:03:46 UTC 2016 对于 ubuntu 安装 redis是非常简单的. 这里采用源码安装. 安装代码如下 wget http://download.redis.io/relea

  • 详解java连接mysql数据库的五种方式

    第一种方式:将用户名和密码封装在Properties类中 首先,导入数据库连接包这个是毋庸置疑的.创建一个jdbc驱动dirver.将数据库(以MySQL为例)的url保存在所创建的字符串url中.如果mysql版本低于8.0,则url保存形式应该为: String url = "jdbc:mysql://localhost:3306/test" 如果mysql版本为8.0版本或以上,url保存形式为: String url = "jdbc:mysql://localhost

  • 详解Java TCC分布式事务实现原理

    概述 之前网上看到很多写分布式事务的文章,不过大多都是将分布式事务各种技术方案简单介绍一下.很多朋友看了还是不知道分布式事务到底怎么回事,在项目里到底如何使用. 所以这篇文章,就用大白话+手工绘图,并结合一个电商系统的案例实践,来给大家讲清楚到底什么是 TCC 分布式事务. 业务场景介绍 咱们先来看看业务场景,假设你现在有一个电商系统,里面有一个支付订单的场景. 那对一个订单支付之后,我们需要做下面的步骤: 更改订单的状态为"已支付" 扣减商品库存 给会员增加积分 创建销售出库单通知仓

  • Smarty模板引擎缓存机制详解

    本文实例讲述了Smarty模板引擎缓存机制.分享给大家供大家参考,具体如下: 首先说下smarty缓存和编译,这是两个不同的概念,编译默认情况下是启动的,而缓存机制需要人为开启,smarty编译过的文件还是php文件,所以执行的时候还是编译的,如果涉及到数据库,还是要访问数据库的所以开销也不小啦,所以需要smarty缓存来解决! 1.开启全局缓存 $smarty->cache_dir = "/caches/"; //缓存目录 $smarty->caching = true;

  • Java SpringCache+Redis缓存数据详解

    目录 前言 一.什么是SpringCache 二.项目集成Spring Cache + Redis 1.配置方式 三.使用Spring Cache 四.SpringCache原理与不足 1.读模式 2.写模式:(缓存与数据库一致) 五.总结 前言 这几天学习谷粒商城又再次的回顾了一次SpringCache,之前在学习谷粒学院的时候其实已经学习了一次了!!! 这里就对自己学过来的内容进行一次的总结和归纳!!! 一.什么是SpringCache Spring Cache 是一个非常优秀的缓存组件.自

  • Spring Boot 基于注解的 Redis 缓存使用详解

    看文本之前,请先确定你看过上一篇文章<Spring Boot Redis 集成配置>并保证 Redis 集成后正常可用,因为本文是基于上文继续增加的代码. 一.创建 Caching 配置类 RedisKeys.Java package com.shanhy.example.redis; import java.util.HashMap; import java.util.Map; import javax.annotation.PostConstruct; import org.springf

  • 详解Java 连接MongoDB集群的几种方式

    先决条件 先运行mongodb肯定是必须的,然后导入以下包: import com.mongodb.MongoClient; import com.mongodb.MongoClientURI; import com.mongodb.ServerAddress; import com.mongodb.MongoCredential; import com.mongodb.MongoClientOptions; MongoClient MongoClient()实例表示到数据库的连接池; 你将只需

  • 详解Java redis中缓存穿透 缓存击穿 雪崩三种现象以及解决方法

    目录 前言 一.缓存穿透 二.缓存击穿 三.雪崩现象 总结 前言 本文主要阐述redis中的三种现象 1.缓存穿透 2.缓存击穿 3.雪崩现象 本文主要说明本人对三种情况的理解,如果需要知道redis基础请查看其他博客,加油! 一.缓存穿透 理解:何为缓存穿透,先要了解穿透,这样有助于区分穿透和击穿,穿透就类似于伤害一点一点的累计,最终打到穿透的目的,类似于射手,一下一下普通攻击,最终杀死对方,先上图 先来描述一下缓存穿透的过程: 1.由于我们取数据的原则是先查询redis上,如果redis上有

  • 详解JAVA类加载机制

    1.一段简单的代码 首先来一段代码,这个是单例模式,可能有的人不知道什么是单例模式,我就简单说一下 单例模式是指一个类有且只有一种对象实例.这里用的是饿汉式,还有懒汉式,双检锁等等.... 写这个是为了给大家看一个现象 class SingleTon{ public static int count1; public static int count2=0; private static SingleTon instance=new SingleTon(); public SingleTon()

随机推荐