前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >redis(三)redis锁的使用

redis(三)redis锁的使用

作者头像
杨小杰
发布2020-02-18 23:00:43
5640
发布2020-02-18 23:00:43
举报

减少库存

不做处理的代码

编写最普通的程序,不考虑任何并发的代码

需求

  1. 假定现在有10000库存,访问一次接口库存减1
  2. 当库存等于0,返回库存不足

需求分析

  1. 因为需要模拟接口访问,所以需要一个web项目,我这里使用的是一个可以运行起来的springboot项目
  2. 之前有讲过如何快速创建springboot项目,这里不重复赘述了。快速通道
  3. 只有让接口被调用10000次或者更多,才能获得较准确的结果
  4. ok,多余的没什么了,先编写代码试试

代码

新建controller->StringRedisTestController

  1. 建立一个static变量->nowCount,用来做全局值统一
  2. 访问一次,让nowCount减一,直到小于等于0为止

编写完成的代码如下:

代码语言:javascript
复制
package com.yxj.spring.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @version v1.0
 * @Copyright(c): 2020-2020
 * @FileName: RedisTestController.java
 * @Description:
 * @autho Admin
 * @date 2020/1/17 13:55
 */
@RestController
@RequestMapping("/test")
public class StringRedisTestController {
   
    private static Integer nowCount = 10000;
    /**
     * 减少,库存等等
     * @return
     */
    @GetMapping("/redis/reduce")
    public String lockTest(){
        if(nowCount<=0){
            System.out.println("------库存不足------");
            return "------库存不足------";
        }
        --nowCount;
        System.out.println("----库存剩余----"+nowCount);
        return String.valueOf(nowCount);
    }
}

测试

  1. 这里为了测试方便,特意下载了jmeter,我的版本号apache-jmeter-2.12
  2. 启动一个线程,访问10000
  1. 编写http请求,这里写controller的请求路径
  1. 点击启动按钮(第一个绿色启动按钮就可以)

测试结果(单线程)

因为代码较多,这里我只贴出最终结果

第一次测试

代码语言:javascript
复制
----库存剩余----4
----库存剩余----3
----库存剩余----2
----库存剩余----1
----库存剩余----0

结果正常

第二次测试(需要重启项目,重新给static变量赋值)

代码语言:javascript
复制
----库存剩余----4
----库存剩余----3
----库存剩余----2
----库存剩余----1
----库存剩余----0

结果正常

第三次测试(最后一次)

代码语言:javascript
复制
----库存剩余----4
----库存剩余----3
----库存剩余----2
----库存剩余----1
----库存剩余----0

结果正常

综上所诉,单线程访问代码,是没有什么问题

启动多个线程,进行访问

  1. 打开jemeter设置的线程组,将线程数更改为两个或者更多
  2. 修改循环次数,这里的循环次数指的是每个线程的循环次数,两个线程或者更多线程去访问,让他们的乘积等于10000
  3. 这里我设置2个线程,循环次数5000

测试结果(多线程)

测试(别忘记重启项目,重新获取static的值)

代码语言:javascript
复制
----库存剩余----2699
----库存剩余----2698
----库存剩余----2696
----库存剩余----2696

可以看到,数据已经错乱了,还有重复值

增加synchronized的代码

使用JAVA原生锁

修改代码,增加synchronized

代码语言:javascript
复制
package com.yxj.spring.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;

/**
 * @version v1.0
 * @Copyright(c): 2020-2020
 * @FileName: RedisTestController.java
 * @Description:
 * @autho Admin
 * @date 2020/1/17 13:55
 */
@RestController
@RequestMapping("/test")
public class StringRedisTestController {

    private static Integer nowCount = 10000;
    /**
     * 减少,库存等等
     * @return
     */
    @GetMapping("/redis/reduce")
    public synchronized String lockTest(){
        if(nowCount<=0){
            System.out.println("------库存不足------");
            return "------库存不足------";
        }
        --nowCount;
        System.out.println("----库存剩余----"+nowCount);
        return String.valueOf(nowCount);
    }
}

测试结果

代码语言:javascript
复制
----库存剩余----3
----库存剩余----2
----库存剩余----1
----库存剩余----0

可以看到,结果又正常了

使用redis锁的代码

synchronized确实可以解决库存不一致问题,但是,因为线上服务大部分都是多节点部署,两台或者两天以上的服务器,代码加synchronized肯定是不好使的

所以这里推荐使用redis

redis锁的优势

  1. redis锁基于redis实现
  2. 数据存储在内存,操作较快
  3. redis是单线程,安全

修改后的代码

  1. 使用setIfAbsent来判断key是否存在
  2. 使用expire来设置超时时间
  3. 使用delete来删除key
代码语言:javascript
复制
package com.yxj.spring.controller;

import com.yxj.spring.utils.RedisUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * @version v1.0
 * @Copyright(c): 2020-2020
 * @FileName: RedisTestController.java
 * @Description:
 * @autho Admin
 * @date 2020/1/17 13:55
 */
@RestController
@RequestMapping("/test")
public class StringRedisTestController {

    @Autowired
    private RedisUtils redisUtils;

    private static Integer nowCount = 10000;
    /**
     * 减少,库存等等
     * @return
     */
    @GetMapping("/redis/reduce")
    public String lockTest(){
        if(nowCount<=0){
            System.out.println("------库存不足------");
            return "------库存不足------";
        }
        if(redisUtils.setIfAbsent(String.valueOf(nowCount), "true")){
            redisUtils.expire(String.valueOf(nowCount),1,TimeUnit.SECONDS);
            Integer count = nowCount;
            --nowCount;
            redisUtils.delete(String.valueOf(count));
            System.out.println("----库存剩余----"+nowCount);
            return String.valueOf(nowCount);
        }else{
            System.out.println("系统繁忙");
            return "系统繁忙";
        }
    }
}

测试结果

两个线程再跑,第一个线程redis key还没有删除,第二个线程已经进入方法了,所以被拦截,无法执行

代码语言:javascript
复制
----库存剩余----4525
系统繁忙
----库存剩余----4524
系统繁忙
----库存剩余----4523
系统繁忙
----库存剩余----4522
系统繁忙
----库存剩余----4521
系统繁忙
----库存剩余----4520
系统繁忙
----库存剩余----4519
系统繁忙
----库存剩余----4518
系统繁忙
----库存剩余----4517
系统繁忙
----库存剩余----4516
系统繁忙
----库存剩余----4515
----库存剩余----4514
----库存剩余----4513
系统繁忙
----库存剩余----4512
系统繁忙
----库存剩余----4511
系统繁忙
----库存剩余----4510
----库存剩余----4509
----库存剩余----4508
----库存剩余----4507
----库存剩余----4506

总结

  1. 快过年了,提前祝大家新年快乐
  2. 因为快过年的原因,大脑无法正常运转,导致本文有点别扭(不是有点,我感觉别扭的很 o(╥﹏╥)o
  3. redis锁主要用作分布式的安全方面,可以通过最后的redis锁测试的结果看出,虽然库存没有减到0,但是安全得到了保障,每个数,只被用了一次
  4. 我这是第一次使用StringRedisTemplate(之前都是使用Jedis),没有踩过太大的坑,所以不敢保障这个redis使用一定正确,但是效果达到了
  5. 本文使用的redisUtils,点击下面的阅读原文,即可找着
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-01-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 JAVA知识总结与分享 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 不做处理的代码
    • 需求
      • 需求分析
        • 代码
          • 测试
            • 测试结果(单线程)
              • 第一次测试
              • 第二次测试(需要重启项目,重新给static变量赋值)
              • 第三次测试(最后一次)
            • 启动多个线程,进行访问
              • 测试结果(多线程)
                • 测试(别忘记重启项目,重新获取static的值)
            • 增加synchronized的代码
              • 修改代码,增加synchronized
                • 测试结果
            • 使用redis锁的代码
              • redis锁的优势
                • 修改后的代码
                  • 测试结果
                  相关产品与服务
                  云数据库 Redis
                  腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档