精华内容
下载资源
问答
  • redis乐观锁
    2022-04-24 16:22:58

    乐观锁:乐观,认为不会出问题,所以不上锁。当更新数据时,会判断是否有人在这个期间修改数据。相对性能好。

    Redis使用 watch 命令监听key, 如果这个key发生了变化则不执行事务

    127.0.0.1:6379> watch money ##监听money 
    OK
    127.0.0.1:6379> multi ##开启事务
    OK
    127.0.0.1:6379> decrby money 2 ##money = money - 2
    QUEUED
    127.0.0.1:6379> exec  ##如果有另外的线程在这个事务监听的同时修改过,则不执行这个事务,如果没有修改过 则执行事务
    (nil)
    

    更多相关内容
  • Redis乐观锁悲观锁

    2022-05-02 17:33:27
    悲观锁 很悲观,什么时候都会出问题,无论做什么...乐观锁 很乐观,认为什么时候都不会出问题,所以不会上锁,更新数据的时候去判断一下,在此期间是否有人修改过这个数据 获取 version 更新的时候比较 version ...

    悲观锁

    • 很悲观,什么时候都会出问题,无论做什么都会加锁

    乐观锁(监视!Watch,面试常问)

    • 很乐观,认为什么时候都不会出问题,所以不会上锁,更新数据的时候去判断一下,在此期间是否有人修改过这个数据
    • 获取 version
    • 更新的时候比较 version

    Redis检测测试

    • 正常执行成功
    127.0.0.1:6379> set money 100
    OK
    127.0.0.1:6379> set out 0
    OK
    127.0.0.1:6379> watch money
    OK
    127.0.0.1:6379> multi
    OK
    127.0.0.1:6379(TX)> decrby money 20
    QUEUED
    127.0.0.1:6379(TX)> incrby out 20
    QUEUED
    127.0.0.1:6379(TX)> exec
    1) (integer) 80
    2) (integer) 20
    127.0.0.1:6379> 
    
    • 测试多线程修改值,使用 watch 可以当作redis的乐观锁操作
    127.0.0.1:6379> watch money # 监视 money
    OK
    127.0.0.1:6379> multi
    OK
    127.0.0.1:6379(TX)> decrby money 10
    QUEUED
    127.0.0.1:6379(TX)> incrby out 10
    QUEUED
    127.0.0.1:6379(TX)> exec # 执行之前,另外一个线程,修改了money值,这个时候,就会导致事务执行失败!
    (nil)
    
    • 如果修改失败,获取最新的值即可
    127.0.0.1:6379> UNWATCH # 1.如果发现事务执行失败,就先解锁
    OK
    127.0.0.1:6379> WATCH money # 2.获取最新的值,再次监视,select version
    OK
    127.0.0.1:6379> multi
    OK
    127.0.0.1:6379(TX)> DECRBY money 1
    QUEUED
    127.0.0.1:6379(TX)> incrby money 1
    QUEUED
    127.0.0.1:6379(TX)> exec # 3.对比监视的值是否发生了变化,如果没有发生变化,那么可以执行成功,如果变化,就执行失败
    1) (integer) 79
    2) (integer) 80
    127.0.0.1:6379> 
    
    展开全文
  • 1 Redis事务介绍 Redis事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。 Redis的事务是通过multi、exec、discard和watch这四...

    1 Redis事务介绍

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

    • Redis的事务是通过multi、exec、discard和watch这四个命令来完成的。
    • Redis的单个命令都是原子性的,所以这里需要确保事务性的对象是命令集合。
    • Redis将命令集合序列化并确保处于同一事务的命令集合连续且不被打断的执行。
    • Redis不支持回滚操作

    1.1 命令介绍

    • multi:用于标记事务块的开始,Redis会将后续的命令逐个放入队列中,然后使用exec原子化执行这个命令队列 。
    • exec:执行命令队列
    • discard:清除命令队列
    • watch:在执行multi之前,先执行watch key1 [key2],可以监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断(可以利用Watch特性实现Redis乐观锁)
    • unwatch:取消 WATCH 命令对所有 key 的监视(如果在执行 WATCH 命令之后,EXEC 命令或DISCARD 命令先被执行了的话,那么就不需要再执行UNWATCH 了)。

    1.2 事务流程

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

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

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

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

    在这里插入图片描述

    在这里插入图片描述

    上图说明:

    • 1.客户端1watch user:001 ,
    • 2.客户端1 开启事务multi
    • 3.客户端1,执行命令set user:001 lisi
    • 4.在客户端1,执行exec之前,客户端2,执行set user:001 xiaoming
    • 5.客户端1,执行exec出错,事务被打断

    命令演示:

    127.0.0.1:6379> multi
    OK
    127.0.0.1:6379> set user:001 zhangsan
    QUEUED
    127.0.0.1:6379> set user:002 lisi
    QUEUED
    127.0.0.1:6379> get user:001
    QUEUED
    127.0.0.1:6379> exec
    1) OK
    2) OK
    3) "zhangsan"
    
    
    127.0.0.1:6379> multi
    OK
    127.0.0.1:6379> set user:001 xiaoming
    QUEUED
    127.0.0.1:6379> set user:002 xiaozhang
    QUEUED
    127.0.0.1:6379> discard    # 使用discard命令取消队列
    OK
    127.0.0.1:6379> exec    # 执行exec报错
    (error) ERR EXEC without MULTI
    
    # watch 命令演示
    
    # 客户端2,在客户端1执行exec之前,执行 set user:001 xiaoming,客户端1的事务被打断
    127.0.0.1:6379> get user:001
    "zhangsan"
    127.0.0.1:6379> watch user:001
    OK
    127.0.0.1:6379> multi
    OK
    127.0.0.1:6379> set user:001 lisi
    QUEUED
    127.0.0.1:6379> exec   # 客户端1的事务被打断
    (nil)
    127.0.0.1:6379> get user:001
    "xiaoming"
    
    
    

    2 Redis实现乐观锁

    2.1 乐观锁与悲观锁介绍

    • 悲观锁

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

    • 乐观锁

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

    2.2 Redis乐观锁实现原理

    Redis乐观锁的实现,是利用watch命令特性。数据进行提交更新的时候,对数据的冲突与否进行检测,如果发现冲突了,则让返回用户错误的信息,让用户决定如何去做。

    Redis通过数据版本(Version)记录机制实现乐观锁,这是乐观锁最常用的一种实现方式。

    客户端1客户端2
    age字段初始版本为1age字段初始版本为1
    watch age multi
    set age 25 版本加一,目前数据库版本为2
    set age 30 exec 当前操作版本为1,小于数据中版本,提交失败。

    客户端2客户端1提交事务之前,对据库版本version进行更新一次,客户端1事务提交的时候对比版本号,要是此次版本号低于数据库当前版本号,就会提交失败。

    2.3 Redis乐观锁秒杀案例

    1. 创建Spring boot项目引入以下依赖

      <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-data-redis</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.apache.commons</groupId>
                  <artifactId>commons-pool2</artifactId>
              </dependency>
      
    2. 配置文件

      spring:
        redis:
          host: 192.168.235.131
          port: 6379
          database: 0
          connect-timeout: 1800000
          password: 123456
          lettuce:
            pool:
              #连接池最大连接数(使用负值表示没有限制)
              max-active: 20
              #最大阻塞等待时间(负数表示没限制)
              max-wait: -1
              #连接池中的最大空闲连接
              max-idle: 8
              #连接池中的最小空闲连接
              min-idle: 0
      
    3. service 代码

      @Service
      @Slf4j
      public class OrderServiceImpl implements OrderService {
      
          @Autowired
          private StringRedisTemplate redisTemplate;
          //库存
          private final String STOCK_KEY="stock:num";
          //秒杀成功的用户
          private final String USER_KEY="success:user";
          /**
           * Redis乐观锁秒杀案例
           * @param userId  用户ID
           * @return
           */
          @Override
          public boolean secKill(String userId) {
              Object stockObj = redisTemplate.opsForValue().get(STOCK_KEY);
              if (stockObj==null){
                  log.info("库存为空,秒杀还未开始!");
                  return false;
              }
              int stockNum=Integer.parseInt(stockObj.toString());
              if (stockNum<=0){
                  log.info("库存为0,秒杀已经结束!");
                  return false;
              }
              //判断当前用户是否已经秒杀成功
              Boolean member = redisTemplate.opsForSet().isMember(USER_KEY, userId);
              if (member){
                  log.info("您已经秒杀成功,不能重复参与!");
                  return false;
              }
      
              List txList =redisTemplate.execute(new SessionCallback<List<Object>>() {
                 @Override
                 public List<Object> execute(RedisOperations operations) throws DataAccessException {
                     //监听库存
                      operations.watch(STOCK_KEY);
                     //开启事务
                     operations.multi();
                     //扣减库存
                     operations.opsForValue().decrement(STOCK_KEY);
                     //把秒杀成功的用户加入到set集合
                     operations.opsForSet().add(USER_KEY,userId);
                     //执行事务
                     List<Object> result=operations.exec();
                     return result;
                 }
             });
              if (txList==null||txList.size()==0){
                  log.info("用户:{},秒杀失败",userId);
                  return false;
              }
              log.info("用户:{},秒杀成功",userId);
              return true;
          }
      }
      
    4. Controller代码

      /**
       * 秒杀
       * @return
       */
      @RequestMapping("secKill")
      public String secKill(){
          String userId= UUID.randomUUID().toString();
          boolean res = orderService.secKill(userId);
          if (res){
              return "秒杀成功";
          }else {
              return "秒杀失败";
          }
      }
      

    使用linux上的ab进行并发测试:

    ab -n 500 -c 100  http://192.168.1.171/order/secKill
    
    展开全文
  • 乐观锁存在多种实现方式:mysql数据库版本号,redis实现,CAS实现等。 在并发情况下,使用锁机制,防止争抢资源。 悲观锁是对数据的修改持悲观态度(认为数据在被修改的时候一定会存在并发问题),因此在整个数据...

    锁机制

    乐观锁:1)通过版本号来实现,先查询获取版本号,在更新的时候校验版本号并修改。

    悲观锁:同步关键字就是悲观锁,也称为排它锁。

    乐观锁还让用户查询当前版本号,悲观锁如果不释放,查都不让查询。

    乐观锁存在多种实现方式:mysql数据库版本号,redis实现,CAS实现等。

    在并发情况下,使用锁机制,防止争抢资源。

    悲观锁是对数据的修改持悲观态度(认为数据在被修改的时候一定会存在并发问题),因此在整个数据处理过程中将数据锁定。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在应用层中实现了加锁机制,也无法保证外部系统不会修改数据)

    在限量秒杀抢购的场景,一定会遇到抢购成功数超过限量的问题和高并发的情况影响系统性能

    1、虽然能用数据库的锁避免,超过限量的问题。但是在大并发的情况下,大大影响数据库性能
    2、为了避免并发操作数据库,我们可以使用队列来限制,但是并发量会让队列内存瞬间升高
    3、我们又可以用悲观锁来实现,但是这样会造成用户等待,响应慢体验不好

    因此我们可以利用redis来实现乐观锁

    代码实现

    案例一

    这里我用java实现,我开20个线程模拟10000个人并发来抢购,其它语言也是一样的实现原理。

    public static void main(String[] arg){
        String redisKey = "redisTest";
        ExecutorService executorService = Executors.newFixedThreadPool(20);
        try {
            Jedis jedis = new Jedis("47.107.221.219",6379);
            jedis.set(redisKey,"0");
            jedis.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    
        for (int i=0;i<10000;i++){
            executorService.execute(()->{
                Jedis jedis1 = new Jedis("47.107.221.219",6379);
                try {
                    jedis1.watch(redisKey);
                    String redisValue = jedis1.get(redisKey);
                    int valInteger = Integer.valueOf(redisValue);
                    String userInfo = UUID.randomUUID().toString();
                    if (valInteger<20){
                        Transaction transaction = jedis1.multi();
                        transaction.incr(redisKey);
                        List list = tx.exec();
                        if (list!=null){
                            System.out.println("用户:"+userInfo+",秒杀成功!当前成功人数:"+(valInteger+1));
                        }else {
                            System.out.println("用户:"+userInfo+",秒杀失败");
                        }
                    }else {
                        System.out.println("已经有20人秒杀成功,秒杀结束");
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    jedis1.close();
                }
            });
        }
        executorService.shutdown();
    }

    案例二

    public void testRedisSyn(int clientName,String clientList) {
     
            //redis中存储商品数量为(goodsNum:100)
            String key = "goodsNum";
            Jedis jedis = new Jedis("192.168.140.98", 6379);
            jedis.auth("redis密码");
     
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
     
            while (true) {
                try {
                    jedis.watch(key);
                    System.out.println("顾客:" + clientName + "开始抢商品");
                    System.out.println("当前商品的个数:" + jedis.get(key));
                    //当前商品个数
                    int prdNum = Integer.parseInt(jedis.get(key));
                    if (prdNum > 0) {
     
                        //开启事务,返回一个事务控制对象
                        Transaction transaction = jedis.multi();
                        //预先在事务对象中装入要执行的操作
                        transaction.set(key, String.valueOf(prdNum - 1));
                        List<Object> exec = transaction.exec();
                        if (exec == null || exec.isEmpty()) {
                            //可能是watch-key被外部修改,或者是数据操作被驳回
                            System.out.println("悲剧了,顾客:" + clientName + "没有抢到商品");
                        } else {
                            //这个命令是做啥的。//抢到商品记录一下
                            jedis.sadd(clientList, clientName+"");
                            System.out.println("好高兴,顾客:" + clientName + "抢到商品");
                            break;
                        }
                    }
                } catch (NumberFormatException e) {
                    e.printStackTrace();
                }finally {
                    jedis.unwatch();
                }
            }
     
        }
    展开全文
  • 本文简简单单描述,redis乐观锁如何去实现秒杀功能 提示:以下是本篇文章正文内容,下面案例可供参考 redis 乐观锁如何实现秒杀? 话不多说,直接上代码 代码如下(示例): header('content-type:text/html;...
  • Redis --- Redis 乐观锁操作演示

    千次阅读 2022-04-03 20:22:37
    这次,小编和大家分享一下 Redis乐观锁,用最通俗易懂的方式让各位了解到这种锁,怎么出现,怎么用的。乐观锁是认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行...
  • 基于SpringBoot使用Redis事务、Redis入门(四)—— 乐观锁尝试构建了一个简单的秒杀demo。 目录一、逻辑设计二、编码2.1 Controller2.2 Service三、测试 一、逻辑设计 发起请求,购买对应的产品及数量 接收参数,...
  • 文章目录Redis乐观锁解决超卖问题CentOS7下载ab工具Windows下载ab工具 Redis乐观锁解决超卖问题 情景假设:现在华为最新手机在做活动,双十二 00:00 准时前十名抢购的用户可以1元秒杀。而数据库对这个秒杀的动作呢...
  • Redis的事务中,WATCH命令可用于提供CAS(check-and-set)功能。假设通过WATCH命令在事务执行之前监控了某个key,倘若在WATCH之后Key的值发生了变化,EXEC命令执行的事务将被放弃,同时返回nil以通知调用者事务执行...
  • 利用watch实现Redis乐观锁
  • Redis(十)——Redis乐观锁

    千次阅读 2021-01-11 16:33:42
    文章目录乐观锁、悲观锁乐观锁的有点Redis中的锁Redis乐观锁测试 乐观锁、悲观锁 锁有什么用? 在数据库执行数据操作时,为了保证数据的一致性,即:A用户更新数据时,B用户不能更新! 在没有锁的情况下: 如果A...
  • laravel redis乐观锁

    2022-04-23 22:52:17
    } } 排他 共享 start TRANSACTION SELECT * FROM `products` where id=1 FOR UPDATE SELECT * FROM `products` where id=1 LOCK in SHARE MODE UPDATE `product` set price = 55 WHERE id =1 delete from `...
  • redis 乐观锁,悲观锁

    2021-04-15 15:00:16
    乐观锁悲观锁 每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁。 场景:如果项目中使用了缓存且对缓存设置了超时时间。 当并发量比较大的时候,如果没有锁机制,如果有大量请求访问过期的数据...
  • Redis乐观锁

    2021-07-22 18:21:14
    Redis 乐观锁 乐观锁:乐观锁认为数据一般情况不会出现问题,所以不会上锁;只有在更新数据时才去判断一下在此期间是否有人修改过此数据; 实现:乐观锁一般是通过版本...redis乐观锁通过监视 WATCH 来实现的 : 使用 W
  • php+redis乐观锁实现商品秒杀

    千次阅读 2021-02-02 11:33:46
    $redis = new \Redis(); $redis->connect('127.0.0.1',6379); $redis->auth('qiusuo521.'); //监视销量,给key打上标签, $redis->watch("sales"); $sales = $redis->get('sales'); .
  • 目录 乐观锁: 使用乐观锁 任务类 ...测试多线程修改值,使用watch可以当作redis乐观锁操作 在事务执行之前先watch key,当事务前后key改变了事务失败,那就先取消监视,再重新监视进行事务操
  • /*乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会 上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用 于多...
  • springboot + redis 乐观锁支持 stringRedisTemplate.setEnableTransactionSupport(true); stringRedisTemplate.watch("count"); stringRedisTemplate.multi(); stringRedisTemplate.opsForValue()...
  • Redis乐观锁,事务multi,watch之间的先后关系 1:watch 必须在 multi之前,不然watch 无效,事务都会执行成功 $v=$rds->get("test"); $rds->set("test",333); //先起事务 $rds->multi(); //添加watch ...
  • 在限量秒杀抢购的场景,一定会遇到抢购成功数超过限量的问题和高并发的情况影响...因此我们可以利用redis来实现乐观锁: 1、利用redis的watch功能,监控这个redisKey的状态值 2、获取redisKey的值 3、创建redis事务
  • redis实现乐观锁

    千次阅读 2022-01-01 11:34:19
    我们先介绍一下悲观锁和乐观锁:我们要知道我们java程序去做的时候是一定会去加锁的,比如说我们多条线程在并发执行的时候,一定会去加锁的 悲观锁:顾名思义,很悲观,就是认为什么时候都会出问题,无论干什么都...
  • RedisRedis 乐观锁

    2021-04-10 21:46:39
    1.概述 2.案例 2.1 单线程正常执行 127.0.0.1:6379> set money 100 OK 127.0.0.1:6379> set out 0 OK 127.0.0.1:6379> watch money // 监视key OK 127.0.0.1:6379> MULTI ...127.0.0.1:.
  • Redis乐观锁-watch命令

    2021-04-02 09:29:50
    Redis乐观锁悲观锁和乐观锁Redis实现乐观锁注意点 悲观锁和乐观锁 悲观锁:在多线程操作中,认为每一个操作都有可能会发生线程问题,所以无论什么操作都会加锁 乐观锁:同样在多线程操作中,认为大部分情况下不会...
  • redis悲观锁和乐观锁

    2022-01-18 14:43:32
  • 前二章我们讲解的redis事务和redis乐观锁,您可以点击下面文章查看。 Redis事务系列之一Redis事务详解 Redis事务系列之二Redis实现乐观锁 本章我们继续在上面的基础上用乐观锁实现秒杀。 二、秒杀的实现 我们先来...
  • Redis乐观锁演示

    2021-11-25 10:23:25
    Redis乐观锁演示 在执行MULTI之前,先执行WATCH key1 key2 … keyn,以此可以监听一个或多个key,如果在事务之前这些个key被其他命令所改动,那么该事务将被打断。其实就是给这些key上了乐观锁。 注意:WATCH并不是...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 37,110
精华内容 14,844
关键字:

redis乐观锁