【Redis缓存机制】13.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命令执行后和事务提交之前,如果数据发生了修改操作,事务执行就不会成功,

此举保证了数据的安全性。

转载请注明出处:http://blog.csdn.net/acmman/article/details/53579378

时间: 2024-11-10 17:10:40

【Redis缓存机制】13.Java连接Redis_Jedis_事务的相关文章

Redis缓存应用之Java

想必Redis这个缓存已经无人不知了,在代理的配合下已经在分布式缓存中的得到了很好的肯定,现在我们一起通过一个Java的例子全面了解Redis缓存. 首先需要安装redis,下载地址: https://github.com/MSOpenTech/redis 安装完后,在自己的安装目录 下进入cmd输入 redis-server --maxmemroy 200m :之所以需要加这个指令"--maxmemroy 200m",因为不少人会出现莫名的内存问题,正常打开后的界面是: 下面需要下载

redis缓存机制和底层实现

说 Redis 的缓存机制实现之前,我想先回顾一下 mysql mysql 存储在哪儿呢? 以 windows 为例,mysql 的表和数据,存储在data 目录下frm ibd 后缀的文件中 mysql存储在机器/服务器的 硬盘中 所以 mysql 读写数据都需要从磁盘读取 .磁盘的容量,带宽的大小就影响了网站的访问速度,读取的方式,也就是 sql 语句,次数和效率也会影响读取效率. 当访问量和并发很大的时候,mysql 就撑不住了,据统计,mysql的连接池并发数max为 500-1000

如何利用缓存机制实现JAVA类反射性能提升30倍

一次性能提高30倍的JAVA类反射性能优化实践 文章来源:宜信技术学院 & 宜信支付结算团队技术分享第4期-支付结算部支付研发团队高级工程师陶红<JAVA类反射技术&优化> 分享者:宜信支付结算部支付研发团队高级工程师陶红 原文首发于宜信支付结算技术团队公号:野指针 在实际工作中的一些特定应用场景下,JAVA类反射是经常用到.必不可少的技术,在项目研发过程中,我们也遇到了不得不运用JAVA类反射技术的业务需求,并且不可避免地面临这个技术固有的性能瓶颈问题. 通过近两年的研究.尝

我的redis缓存机制和redis数据结构整理(一)

摘要: 0.redis安装 1.redis的常用数据结构类型 1.1  String 1.2  List 1.3  Set 1.4  Sorted Set 1.5  Hash 2.redis是单进程单线程的 3.管道 4.我的测试代码 内容总结 0.redis安装 官网地址 http://redis.io/ 下载:wget http://download.redis.io/releases/redis-3.2.4.tar.gz 解压:tar xzf redis-3.2.4.tar.gz 编译:c

Spring(五)Spring缓存机制与Redis的结合

一.Redis和数据库的结合 使用Redis可以优化性能,但是存在Redis的数据和数据库同步的问题. 例如,T1时刻以将 key1 保存数据到 Redis,T2时刻刷新进入数据库,但是T3时刻发生了其他业务需要改变数据库同一条记录的数据,但是采用了 key2 保存到Redis中,然后又写入了更新数据到数据库中,这就导致 Redis 中key1 的数据是脏数据,和数据库中的数据不一致. 1.Redis和数据库读操作 数据缓存往往会在 Redis 上设置超时时间,当设置 Redis 的数据超时后,

java框架篇---hibernate之缓存机制

一.why(为什么要用Hibernate缓存?) Hibernate是一个持久层框架,经常访问物理数据库. 为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能. 缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写数据,在特定的时刻或事件会同步缓存和物理数据源的数据. 二.what(Hibernate缓存原理是怎样的?)Hibernate缓存包括两大类:Hibernate一级缓存和Hibernate二级缓存. 1.Hibernate一级缓存又称为“Session的

Java缓存学习之二:浏览器缓存机制

浏览器端的九种缓存机制介绍 浏览器缓存是浏览器端保存数据用于快速读取或避免重复资源请求的优化机制,有效的缓存使用可以避免重复的网络请求和浏览器快速地读取本地数据,整体上加速网页展示给用户.浏览器端缓存的机制种类较多,总体归纳为九种,这里详细分析下这九种缓存机制的原理和使用场景.打开浏览器的调试模式->resources左侧就有浏览器的8种缓存机制. 一.http缓存 http缓存是基于HTTP协议的浏览器文件级缓存机制.即针对文件的重复请求情况下,浏览器可以根据协议头判断从服务器端请求文件还是从

Java三大框架之——Hibernate中的三种数据持久状态和缓存机制

Hibernate中的三种状态   瞬时状态:刚创建的对象还没有被Session持久化.缓存中不存在这个对象的数据并且数据库中没有这个对象对应的数据为瞬时状态这个时候是没有OID. 持久状态:对象经过Session持久化操作,缓存中存在这个对象的数据为持久状态并且数据库中存在这个对象对应的数据为持久状态这个时候有OID. 游离状态:当Session关闭,缓存中不存在这个对象数据而数据库中有这个对象的数据并且有OID为游离状态. 注:OID为了在系统中能够找到所需对象,我们需要为每一个对象分配一个

Java 使用Redis缓存工具的图文详细方法

开始在 Java 中使用 Redis 前, 我们需要确保已经安装了 redis 服务及 Java redis 驱动,且你的机器上能正常使用 Java. (1)Java的安装配置可以参考我们的 Java开发环境配置 (2)安装了 redis 服务: 请参考:Windows环境下使用Redis缓存工具的图文详细方法 或是: 首先你需要下载驱动包,下载 jedis.jar,确保下载最新驱动包. 在你的classpath中包含该驱动包. 一.新建一个javaweb项目. 1. 新建一个Jedis的项目.