• 第八章Redis_ 事务_ 锁机制_ 秒杀


    第8章-Redis_ 事务_ 锁机制_ 秒杀

    8.1事物的定义

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

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

    8.2Multi、Exec、discard

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

    组队的过程中可以通过 discard 来放弃组队。
    在这里插入图片描述案例
    在这里插入图片描述在这里插入图片描述在这里插入图片描述

    如果执行阶段某个命令报出了错误,则只有报错的命令不会被执行,而其他的命令都会执行,不会回滚。
    在这里插入图片描述

    8.3为什么要做成事务

    想想一个场景:有很多人有你的账户,同时去参加双十一抢购

    8.4事物冲突的例子

    一个请求想给金额减 8000
    一个请求想给金额减 5000
    一个请求想给金额减 1000
    在这里插入图片描述

    8.4.1悲观锁

    悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会 block直到它拿到锁。

    传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
    在这里插入图片描述
    想拿10000元会先被上锁,只能被一个操作执行,假设第一个操作需要取8000元,最后面还剩2000元,200

    8.4.2乐观锁

    在这里插入图片描述

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

    8.4.3乐观锁在redis中的使用

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

    8.4.4unwatch

    取消 WATCH 命令对所有 key 的监视。
    如果在执行 WATCH 命令之后,EXEC 命令或 DISCARD 命令先被执行了的话,那么就不需要再执行 UNWATCH 了。
    http://doc.redisfans.com/transaction/exec.html

    8.5Redis事务三特性

    ➢ 单独的隔离操作
    ◼ 事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
    ➢ 没有隔离级别的概念
    ◼ 队列中的命令没有提交之前都不会实际被执行,因为事务提交前任何指令都不会被实际执行
    ➢ 不保证原子性
    ◼ 事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚

    8.6Redis_ 事务_秒杀案例

    8.6.1解决计数器和人员记录的事务操作

    在这里插入图片描述

    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;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    在这里插入图片描述

    8.6.2 Redis 事务–秒杀并发模拟

    使用工具 ab 模拟测试
    CentOS6 默认安装
    CentOS7 需要手动安装
    联网: yum install httpd-tools
    在这里插入图片描述
    测试及结果
    vim postfile 模拟表单提交参数,以&符号结尾;存放当前目录。
    内容:prodid=0101&ab -n 2000 -c 200 -k -p ~/postfile -T application/xwww-form-urlencodedhttp://192.168.2.115:8081/Seckill/doseckill
    超卖
    在这里插入图片描述超卖问题
    在这里插入图片描述利用乐观锁淘汰用户,解决超卖问题。
    在这里插入图片描述在这里插入图片描述在这里插入图片描述

    继续增加并发测试

    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述已经秒光,可是还有库存
    在这里插入图片描述
    在这里插入图片描述连接超时,通过连接池解决
    节省每次连接 redis 服务带来的消耗,把连接好的实例反复利用。
    通过参数管理连接的行为代码见项目中
    链接池参数
    ◼ MaxTotal:控制一个 pool 可分配多少个 jedis实例,通过 pool.getResource()来获取;如果赋值为-1,则表示不限制;如果 pool已经分配了 MaxTotal 个 jedis实例,则此时 pool 的状态为 exhausted。
    ◼ maxIdle:控制一个 pool最多有多少个状态为 idle(空闲)的 jedis实例;
    ◼ MaxWaitMillis :表示当 borrow 一个 jedis 实例时,最大的等待毫秒数,如果超过等待时间,则直 接抛 JedisConnectionException;
    ◼ testOnBorrow:获得一个 jedis实例的时候是否检查连接可用性(ping());如
    果为 true,则得到的 jedis实例均是可用的;
    解决库存遗留问题
    LUA 脚本
    在这里插入图片描述LUA 在 脚本在 Redis 中的优势
    在这里插入图片描述
    在这里插入图片描述

    返回Redis目录

  • 相关阅读:
    Windows安装mysql并且配置odbc
    CVL网卡的ADQ特性在SPDK的NVMF测试中的应用实例 - 上篇
    在Eclipse 中使用 Maven 创建雅加达 EE 应用程序
    ESP8266-Arduino编程实例-OLED显示QR码(二维码)
    node-sass改dart-sass and 一些七七八八
    python面向对象之继承相关知识
    视觉SLAM基础-李群和李代数
    如何远程配置linux系统和在linux系统安装软件
    vue项目中使用luckysheet
    java定时器实现的三种方式
  • 原文地址:https://blog.csdn.net/qq_52166656/article/details/126653715