当前位置:网站首页>[redis]Redis6的事务操作

[redis]Redis6的事务操作

2022-06-22 19:45:00 fate _zore

Redis6的事务操作

Redis的事务定义

image-20220619152757114

Redis事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。

Redis事务的主要作用就是串联多个命令防止别的命令插队。

Multi、Exec、discard

从输入Multi命令开始,输入的命令都会依次进入命令队列中,但不会执行,直到输入Exec后,Redis会将之前的命令队列中的命令依次执行。

组队的过程中可以通过discard来放弃组队。

image-20220619152848021

事务的错误处理

  • 组队中某个命令出现了报告错误,执行时整个的所有队列都会被取消

image-20220619152910457

  • 如果执行阶段某个命令报出了错误,则只有报错的命令不会被执行,而其他的命令都会执行,不会回滚。

image-20220619152936595

事务冲突

例子

一个请求想给金额减8000

一个请求想给金额减5000

一个请求想给金额减1000

image-20220619164733463

两种锁的思想

image-20220619153547188

悲观锁

悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁表锁等,读锁写锁等,都是在做操作之前先上锁。

乐观锁

乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。Redis就是利用这种check-and-set机制实现事务的。

WATCHkey [key …]

在执行multi之前,先执行watch key1 [key2],可以监视一个(或多个) key ,如果在事务执行之前这个(或这些) key被其他命令所改动,那么事务将被打断。

unwatch

取消 WATCH 命令对所有 key 的监视。

如果在执行 WATCH 命令之后,EXEC 命令或DISCARD 命令先被执行了的话,那么就不需要再执行UNWATCH 了。

Redis事务三特性

Ø 单独的隔离操作

  • 事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。

Ø 没有隔离级别的概念

  • 队列中的命令没有提交之前都不会实际被执行,因为事务提交前任何指令都不会被实际执行

Ø 不保证原子性

  • 事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚

秒杀案例

思路

image-20220619173036319

  1. 传入uid和prodid(并验证)
  2. 根据uid和prodid生成key
  3. 库存判断(null未开始,0结束),重复秒杀判断
  4. 用户秒杀,库存–
  5. 用户进入到秒杀成功清单

代码:

public static boolean doSecKill(String uid,String prodid) throws IOException {
    
   //1 uid和prodid非空判断
   if(uid == null || prodid == null) {
    
      return false;
   }

   //2 连接redis
   //Jedis jedis = new Jedis("192.168.44.168",6379);
   //通过连接池得到jedis对象
   JedisPool jedisPoolInstance = JedisPoolUtil.getJedisPoolInstance();
   Jedis jedis = jedisPoolInstance.getResource();

   //3 拼接key
   // 3.1 库存key
   String kcKey = "sk:"+prodid+":qt";
   // 3.2 秒杀成功用户key
   String userKey = "sk:"+prodid+":user";

   //监视库存
   jedis.watch(kcKey);

   //4 获取库存,如果库存null,秒杀还没有开始
   String kc = jedis.get(kcKey);
   if(kc == null) {
    
      System.out.println("秒杀还没有开始,请等待");
      jedis.close();
      return false;
   }

   // 5 判断用户是否重复秒杀操作
   if(jedis.sismember(userKey, uid)) {
    
      System.out.println("已经秒杀成功了,不能重复秒杀");
      jedis.close();
      return false;
   }

   //6 判断如果商品数量,库存数量小于1,秒杀结束
   if(Integer.parseInt(kc)<=0) {
    
      System.out.println("秒杀已经结束了");
      jedis.close();
      return false;
   }

   //7 秒杀过程
   //使用事务
   Transaction multi = jedis.multi();

   //组队操作
   multi.decr(kcKey);
   multi.sadd(userKey,uid);

   //执行
   List<Object> results = multi.exec();

   if(results == null || results.size()==0) {
    
      System.out.println("秒杀失败了....");
      jedis.close();
      return false;
   }

   //7.1 库存-1
   //jedis.decr(kcKey);
   //7.2 把秒杀成功用户添加清单里面
   //jedis.sadd(userKey,uid);

   System.out.println("秒杀成功了..");
   jedis.close();
   return true;
}

可以通过线程池解决链接超时问题

public class JedisPoolUtil {
    
   private static volatile JedisPool jedisPool = null;

   private JedisPoolUtil() {
    
   }

   public static JedisPool getJedisPoolInstance() {
    
      if (null == jedisPool) {
    
         synchronized (JedisPoolUtil.class) {
    
            if (null == jedisPool) {
    
               JedisPoolConfig poolConfig = new JedisPoolConfig();
               poolConfig.setMaxTotal(200);
               poolConfig.setMaxIdle(32);
               poolConfig.setMaxWaitMillis(100*1000);
               poolConfig.setBlockWhenExhausted(true);
               poolConfig.setTestOnBorrow(true);  // ping PONG
             
               jedisPool = new JedisPool(poolConfig, "192.168.44.168", 6379, 60000 );
            }
         }
      }
      return jedisPool;
   }

   public static void release(JedisPool jedisPool, Jedis jedis) {
    
      if (null != jedis) {
    
         jedisPool.returnResource(jedis);
      }
   }

}

可以使用lua来解决库存遗留问题

public class SecKill_redisByScript {
    
   
   private static final  org.slf4j.Logger logger =LoggerFactory.getLogger(SecKill_redisByScript.class) ;

   public static void main(String[] args) {
    
      JedisPool jedispool =  JedisPoolUtil.getJedisPoolInstance();
 
      Jedis jedis=jedispool.getResource();
      System.out.println(jedis.ping());
      
      Set<HostAndPort> set=new HashSet<HostAndPort>();

   // doSecKill("201","sk:0101");
   }
   
   static String secKillScript ="local userid=KEYS[1];\r\n" + 
         "local prodid=KEYS[2];\r\n" + 
         "local qtkey='sk:'..prodid..\":qt\";\r\n" + 
         "local usersKey='sk:'..prodid..\":usr\";\r\n" + 
         "local userExists=redis.call(\"sismember\",usersKey,userid);\r\n" + 
         "if tonumber(userExists)==1 then \r\n" + 
         " return 2;\r\n" + 
         "end\r\n" + 
         "local num= redis.call(\"get\" ,qtkey);\r\n" + 
         "if tonumber(num)<=0 then \r\n" + 
         " return 0;\r\n" + 
         "else \r\n" + 
         " redis.call(\"decr\",qtkey);\r\n" + 
         " redis.call(\"sadd\",usersKey,userid);\r\n" + 
         "end\r\n" + 
         "return 1" ;
          
   static String secKillScript2 = 
         "local userExists=redis.call(\"sismember\",\"{sk}:0101:usr\",userid);\r\n" +
         " return 1";

   public static boolean doSecKill(String uid,String prodid) throws IOException {
    

      JedisPool jedispool =  JedisPoolUtil.getJedisPoolInstance();
      Jedis jedis=jedispool.getResource();

       //String sha1= .secKillScript;
      String sha1=  jedis.scriptLoad(secKillScript);
      Object result= jedis.evalsha(sha1, 2, uid,prodid);

        String reString=String.valueOf(result);
      if ("0".equals( reString )  ) {
    
         System.err.println("已抢空!!");
      }else if("1".equals( reString )  )  {
    
         System.out.println("抢购成功!!!!");
      }else if("2".equals( reString )  )  {
    
         System.err.println("该用户已抢过!!");
      }else{
    
         System.err.println("抢购异常!!");
      }
      jedis.close();
      return true;
   }
}

原网站

版权声明
本文为[fate _zore]所创,转载请带上原文链接,感谢
https://blog.csdn.net/weixin_60223449/article/details/125397961