目录

命令

组队Multi错误(命令此时不会真正执行):

执行exec错误:

事务冲突

解决方案

悲观锁:

乐观锁:

场景:

演示乐观锁,watch key监控

Redis事务总结:

秒杀案例

ab测压使用教程

连接超时问题:

超卖问题

Redis使用乐观锁库存遗留问题:


Redis事务是一个单独的隔离操作,事务中的所有命令都会序列化,按顺序的执行,就像上述图一样,不会被别的客户端送来的命令请求所打断;

目的:串联多个命令,防止别的命令插队;

命令

Multi(组队阶段):将命令按顺序放到队列中,但是不会执行

Exec(执行阶段):提交,类似于commit,提交完后就代表事务已经结束了

discard(回滚阶段):如果你觉得执行的命令有问题,可以使用该阶段来放弃组队;

discard:代表上述组队已经被放弃了 ,前面的命令作废

组队Multi错误(命令此时不会真正执行):

在组队时期(Multi)出现错误时,如果你提交事务exec,那么之前所有的命令都会作废;

执行exec错误:

我认为,Multi与exec发生的错误有点像编译错误和运行时错误,如果你语法啥的都错了,那程序直接挂了过不了

运行错误的话,你好歹还有class文件,所以说你能拿的东西还是能拿到,错的就死;


事务冲突

例子:

解决方案

悲观锁:

操作之前先上锁(别人不能进行操作,阻塞了),等执行的人操作完了之后,会将锁释放,然后另外的人就会上锁,并且执行操作。。。:效率低下

乐观锁:

顾名思义,就是很乐观,每次去拿数据的时候都会认为别人没有修改数据,所以不会上锁,但是自己在更新数据的时候会判断在此期间,别人到底有没有更新数据,(这里我们用版本号来表示),更新数据了的版本号和没更新数据的不一致;——>所以说,如果别人更新了,那么自己就更新不了了;但是别人还是可以继续更新的;

适用于多读类型,有利于提高吞吐量(因为时间快了,自然吞吐量就高了);

场景:

多个人抢一张票,可能都抢到票了,但是支付成功的就一个;

抢到票可以理解为入队操作Multi;

支付成功可以理解为事务提交exec;

 演示乐观锁,watch key监控

理解:一般是和事务一起用,当某个key进行watch之后,如果其他客户端对这个key进行了更改,那么本次事务将会被取消,事务的exec会返回null,jedis.watch(key)会返回ok

比如:

已经有第一个人对这个key进行更改了,所以其他人再对这个key执行时(exec)会返回 null


第一个用户对balance执行命令

1、watch balance:监控键balance

2、multi:开始排队

3、incrby 键 10:对键对应的value+10操作

4、exec:执行操作

此时反馈为110

第二个用户同样的操作会提示失败,为null,因为 第二个的版本号与第一个更新后的版本号不一致


Redis事务总结:

Redis是没有原子性的,在exec执行阶段如果出现错误,只是出现错误的命令不能执行;

秒杀案例

利用ab命令进行测压, 设置并发

 并发花费时间

两个键:用户秒杀成功key,储存商品key

设置仓库商品数量 set 键名 数量

并发完后,查询仓库商品信息,变成负数了

ab测压使用教程

(9条消息) ab压测工具使用教程_u011585609的专栏-CSDN博客_ab工具使用方法

连接超时问题:

当redis处理不了更多的请求,那么那些请求就会一直等待;

解决:用类似mysql中的连接池解决即可

在JedisUtils配置Jedis连接池配置信息

package com.atguigu;import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;public class JedisPoolUtil {//初始化Jedis连接池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  PONGjedisPool = new JedisPool(poolConfig, "192.168.44.168", 6379, 60000,"123456");}}}return jedisPool;}public static void release(JedisPool jedisPool, Jedis jedis) {if (null != jedis) {jedisPool.returnResource(jedis);}}}

超卖问题

明明东西卖完了, 但是还提示秒杀成功

当库存为1时,同时有n个连接一起执行减少库存操作时,-n+1;

超卖问题,需要用乐观锁方式解决:

watch监控key,然后将命令加入排队,执行;

package com.atguigu;import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.LoggerFactory;import ch.qos.logback.core.rolling.helper.IntegerTokenConverter;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.ShardedJedisPool;
import redis.clients.jedis.Transaction;/****/
public class SecKill_redis {public static void main(String[] args) {Jedis jedis =new Jedis("192.168.44.168",6379);System.out.println(jedis.ping());jedis.close();}//秒杀过程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.184.131", 6379);
//        jedis.auth("123456");//     2.2通过连接池获取jedis对象JedisPool jedisPoolInstance = JedisPoolUtil.getJedisPoolInstance();Jedis jedis = jedisPoolInstance.getResource();//      3、 拼接key,库存keyString kcKey="sk:"+prodid+":qt";//     3.1秒杀成功的用户idString userKey="sk:"+prodid+":user";//       监视watch一下库存jedis.watch(kcKey);//        4.获取库存,如果库存==null,秒杀还没开始String kc = jedis.get(kcKey);if(kc==null){System.out.println("秒杀还没开始,请等待");jedis.close();return false;}//       5.判断用户是否重复秒杀(用set),看用户秒杀清单(userKey)是否有该用户(uid)Boolean sismember = jedis.sismember(userKey, uid);if(sismember){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();//       组队操作:将库存中的商品数量-1,秒杀的用户uid添加到userkey中multi.decr(kcKey);multi.sadd(userKey,uid);//     执行List<Object> results = multi.exec();if(results==null||results.size()==0){System.out.println("秒杀失败.....");jedis.close();}//       7.1储存-1
//      jedis.decr(kcKey);//        7.2把秒杀成功的用户添加到秒杀清单中
//      jedis.sadd(userKey,uid);
//      System.out.println("秒杀成功..");
//      jedis.close();return true;}
}

Redis使用乐观锁库存遗留问题:

比如说:当一个用户购买成功后,而商品此时版本号变更,那么其他用户就不能往下面进行了;

利用lua脚本:

package com.atguigu;import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.LoggerFactory;import ch.qos.logback.core.joran.conditional.ElseAction;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.ShardedJedisPool;
import redis.clients.jedis.Transaction;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;}
}

Redis事务和锁机制(乐观锁+秒杀)相关推荐

  1. mysql锁机制——乐观锁、悲观锁;共享锁、排他锁、行表锁、间隔后码锁、MVCC 与 thinkphp的lock解析

    锁的引入 如果A有100元,同时对B.C转账,若处理是同时的,则此时同时读取A的余额为100元,在对两人转账后写回,A的余额不是0元而是50元.因此,为了防止这种现象的出现,要引入锁的概念,如只有在A ...

  2. 【Redis】事物和锁机制乐观锁悲观锁

    目录 1. Redis 的事务定义 2. Multi.Exec.discard 3. 事务的错误处理 4. 事务冲突的问题 悲观锁 乐观锁 1. Redis 的事务定义 Redis 事务是一个单独的隔 ...

  3. Java中的锁机制 -- 乐观锁、悲观锁、自旋锁、可重入锁、读写锁、公平锁、非公平锁、共享锁、独占锁、重量级锁、轻量级锁、偏向锁、分段锁、互斥锁、同步锁、死锁、锁粗化、锁消除

    文章目录 1. Java中的锁机制 1.1 乐观锁 1.2 悲观锁 1.3 自旋锁 1.4 可重入锁(递归锁) 1.5 读写锁 1.6 公平锁 1.7 非公平锁 1.8 共享锁 1.9 独占锁 1.1 ...

  4. 悲观锁和乐观锁的区别,怎么实现

    悲观锁 每次拿数据都以为别人会修改,所以每次拿数据时都会上锁. 实现:开启事务,启用锁机制 乐观锁 每次拿数据时候都认为别人不会修改,所以不会上锁,但是在更新数据时候会判断在此期间是否有人更新过. 实 ...

  5. Redis事务,Redis实现悲观锁,乐观锁

    Redis 事务 redis 事务可以一次执行多个命令,并带有三个保证 exec命令执行前,多个命令被放入队列缓存 exec命令执行后,缓存队列中的命令顺序执行,一旦有一个有误,不影响其它命令的执行 ...

  6. Redis事务控制|相关命令|队列失败两种情况|官方解释无回滚|悲观锁和乐观锁简单介绍

    相关命令 命令队列执行失败的两种情况 1.执行队列时失败:错误在入队时检测不出来,整个队列执行时有错的命令执行失败,但是其他命令并没有回滚. 加入队列时失败:遇到了入队时即可检测到的错误,整个队列都不 ...

  7. 大数据之Redis:悲观锁和乐观锁

    目录 1.悲观锁 2.乐观锁 3.事务冲突的问题 4.客户端事务操作 1.悲观锁 悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数 ...

  8. 数据库事务的悲观锁和乐观锁

    转载出处:http://www.hollischuang.com/archives/934 在数据库的锁机制中介绍过,数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务同时存取数据库中同一数 ...

  9. 同一事务多次加for_谈谈事务隔离级别,以及悲观锁和乐观锁的原理和应用场景...

    前言 在日常开发中,数据库我想大家一点都不陌生是吧,我想不管你写啥,数据库就算没用过你也听说过吧.做好数据方面的操作,不仅仅需要对Java相关框架的掌握,还需要对数据库自身体系结构的理解. 本文是补充 ...

  10. 锁机制有什么用?简述Hibernate的悲观锁和乐观锁机制

    有些业务逻辑在执行过程中要求对数据进行排他性的访问,于是需要通过一些机制保证在此过程中数据被锁住不会被外界修改,这就是所谓的锁机制. Hibernate支持悲观锁和乐观锁两种锁机制.悲观锁,顾名思义悲 ...

最新文章

  1. AIoT开放平台及应用
  2. 残差平方和ssr的计算公式为_如何为你的回归问题选择最合适的机器学习方法?...
  3. php ile_get_contents无法请求https连接的解决方法
  4. 使用Nessus漏扫
  5. 聊聊高并发系统之队列术
  6. java-第七章-数组-循环输出
  7. docker-ce私有仓库搭建
  8. php 父子进程通信,PHP 进程及进程间通信
  9. vue使用python_如何使用Python和Vue创建两人游戏
  10. linux 内核内核签名_24岁生日快乐,Linux内核
  11. java sql范围查询语句,java类中写sql语句,查询条件包含换行
  12. Oracle ODI 12c之多表联合查询以及定时任务设置
  13. 利用matlab自带函数快速提取二值图像的图像边缘 bwperim函数
  14. c语言除法保留1位小数,高精度除法小数点位数
  15. 你不知道的二手车分期购车背后的套路
  16. pdf转cad怎么弄_还在为cad转pdf烦恼吗?教你CAD批量转pdf
  17. 华硕天选笔记本电脑启动机器后搜索不到网络
  18. 怎么从光缆缆标志区别是单模光缆还是多模光缆
  19. python-合并两个列表并去重
  20. 墙裂推荐ShapeView二

热门文章

  1. 《机构投资的创新之路》读书笔记2(第4章):投资组合管理工具
  2. 飞young使用路由器教程
  3. 完整绘制echarts地图并实现两级联动(区-乡镇)
  4. 自动化专业考研可以计算机吗,自动化专业考研可以选择什么专业?
  5. 倚天屠龙--持续收集中
  6. 计蒜客 - T1284 夫子云游(递归)
  7. Ubuntu16安装OpenJDK7
  8. 涂鸦NBIOT OpenCPU开发快速入门(一)
  9. 基于B/S模式的学校教材信息管理系统设计
  10. 【Vue3中的响应式原理】