05-Redis 事务处理实践

本文详细介绍了Redis的事务处理,包括乐观锁的使用、事务的开启与提交、取消及错误处理。通过实例展示了转账和秒杀场景,强调了在并发操作中Redis如何保证数据一致性。并提供了Jedis客户端进行事务操作的代码示例。
摘要由CSDN通过智能技术生成

Redis事务简介
概述
Redis采用了乐观所方式进行事务控制,它使用watch命令监视给定的key,当exec(提交事务)时候如果监视的key从调用watch后发生过变化,则整个事务会失败。也可以调用watch多次监视多个key。注意watch的key是对整个连接有效的,如果连接断开,监视和事务都会被自动清除。当然exec,discard,unwatch命令都会清除连接中的所有监视。

基本指令
redis进行事务控制时,通常是基于如下指令进行实现,例如:

  • multi 开启事务
  • exec 提交事务
  • discard 取消事务
  • watch 监控,如果监控的值发生变化,则提交事务时会失败
  • unwatch 去掉监控
    Redis保证一个事务中的所有命令要么都执行,要么都不执行(原子性)。如果在发送EXEC命令前客户端断线了,则Redis会清空事务队列,事务中的所有命令都不会执行。而一旦客户端发送了EXEC命令,所有的命令就都会被执行,即使此后客户端断线也没关系,因为Redis中已经记录了所有要执行的命令。

Redis事务控制实践
exec提交事务
例如:模拟转账,tony 500,jack 200,tony转给jack100。过程如下:

127.0.0.1:6379> set tony 500
OK
127.0.0.1:6379> set jack 200
OK
127.0.0.1:6379> mget tony jack
1) "500"
2) "200"
127.0.0.1:6379> multi #开启事务
OK
127.0.0.1:6379(TX)> decrby tony 100 #所有指令操作会进入到队列
QUEUED
127.0.0.1:6379(TX)> incrby jack 100
QUEUED
127.0.0.1:6379(TX)> mget tony jack
QUEUED 
127.0.0.1:6379(TX)> exec  #提交事务
1) (integer) 400
2) (integer) 300
3) 1) "400"
   2) "300"
127.0.0.1:6379> mget tony jack
1) "400"
2) "300"
127.0.0.1:6379>

discard取消事务
注意redis事务太简单,没有回滚,而只有取消。

127.0.0.1:6379> mget tony jack
1) "400"
2) "300"
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incrby jack 100
QUEUED
127.0.0.1:6379> discard
OK
127.0.0.1:6379> get jack
"300"
127.0.0.1:6379> exec
(error) ERR EXEC without MULTI

当出现错误指令时,事务也会自动取消。

127.0.0.1:6379> mget tony jack
1) "400"
2) "300"
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> incrby jack 100
QUEUED
127.0.0.1:6379(TX)> abcd
(error) ERR unknown command `abcd`, with args beginning with:
127.0.0.1:6379(TX)> get jack
QUEUED
127.0.0.1:6379(TX)> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get jack
"300"
127.0.0.1:6379>

秒杀抢票事务处理
基于一个秒杀,抢购案例,演示redis乐观锁方式,例如

第一步:打开客户端1,执行如下操作

127.0.0.1:6379> set ticket 1
OK
127.0.0.1:6379> set money 0
OK
127.0.0.1:6379> watch ticket		#乐观锁,对值进行观察,改变则事务失败
OK
127.0.0.1:6379> multi				#开启事务
OK
127.0.0.1:6379> decr ticket
QUEUED
127.0.0.1:6379> incrby money 100
QUEUED

第二步:打开客户端2,执行如下操作,演示还没等客户端1提交事务,此时客户端2把票买到了。

127.0.0.1:6379> get ticket
"1"
127.0.0.1:6379> decr ticket
(integer) 0

第三步,回到客户端1:提交事务,检查ticket的值

127.0.0.1:6379> exec
(nil) #执行事务,失败
127.0.0.1:6379> get ticket
“0”
127.0.0.1:6379> unwatch #取消监控

Jedis 客户端事务操作
基于Jedis进行事务测试,代码如下:

package com.jt;
import org.junit.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;

public class JedisTransactionTests {

    @Test
    public void testTransaction(){
        Jedis jedis=new Jedis("192.168.126.130",6379);
        jedis.auth("123456");
        jedis.set("tony","300");
        jedis.set("jack","500");
        //实现操作,tony转账100给jack
        //开启事务
        Transaction multi = jedis.multi();
        //执行业务操作
        try {
            multi.decrBy("tony", 100);
            multi.incrBy("jack", 100);
            int n=100/0;//模拟异常
            //提交事务
            multi.exec();
        }catch(Exception e) {
            //出现异常取消事务
            multi.discard();
        }
        String tonyMoney=jedis.get("tony");
        String jackMoney=jedis.get("jack");
        System.out.println("tonyMoney="+tonyMoney);
        System.out.println("jackMoney="+jackMoney);
        jedis.close();
    }
}

Jedis 客户端秒杀操作实践

package com.jt.demos;

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

import java.util.List;

/**

  • redis秒杀练习:

  • 模拟两个线程都去抢购同一张票(考虑乐关锁)
    */
    public class SecondKillDemo02 {

    public static void secKill(){
    Jedis jedis=new Jedis(“192.168.126.130”,6379);
    jedis.auth(“123456”);
    jedis.watch(“ticket”,“money”);
    String ticket = jedis.get(“ticket”);
    if(ticket==null||Integer.valueOf(ticket)==0)
    throw new RuntimeException(“已无库存”);
    Transaction multi = jedis.multi();
    try {
    multi.decr(“ticket”);
    multi.incrBy(“money”, 100);
    List exec = multi.exec();
    System.out.println(exec);
    }catch (Exception e){
    e.printStackTrace();
    multi.discard();
    }finally {
    jedis.unwatch();
    jedis.close();
    }
    }
    public static void main(String[] args) {
    Jedis jedis=new Jedis(“192.168.126.130”,6379);
    jedis.auth(“123456”);
    jedis.set(“ticket”,“1”);
    jedis.set(“money”,“0”);

       Thread t1=new Thread(()->{
           secKill();
       });
       Thread t2=new Thread(()->{
           secKill();
       });
       t1.start();
       t2.start();
    

    }
    }

总结(Summary)
本小节重点讲解了redis中的事务,以及事务控制指令,控制机制,乐观锁实现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值