Redis 场景

1.分布式锁实现

上锁:setnx key value

解锁:del key

超时机制:set key value nx ex time

基于此思路实现伪代码:

java 复制代码
public class RedisLock{
  
  @Autowired
  private static RedisTemplate rt;  
  private static final LOCK_WORD = "lock";
  private static final int TIME_OUT = 3;
  
  public void tryLock(String uuid ){
    	
    	//String uuid = UUID.get();
    	boolean lock = rt.setnx(LOCK_WORD,uuid,TIME_OUT,TimeUnit.SECOND);
    	
    	if(lock){			//	没有考虑可重入锁设计
        try{
          do(业务逻辑)
        }finally{	//	需要保证原子操作
          String tempLock = rt.get(LOCK_WORD);
          if(uuid.equals(tempLock)){
            rt.del(LOCK_WORD);
          }
        }       
      }else {
         try{
           Thread.sleep(100);
           tryLock();	//	递归调用可能会爆栈,应该实现一个等待队列
         }catch(InterruptedException e){
           	e.printStackTrace();
         }
      }
  }
}

实际实现代码

java 复制代码
@GetMapping("testLock")
public void testLock() {
    // 1 获取锁,setIfAbsent,同时设置3秒过期,以避免中间出现异常,导致锁一直无法释放
    Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", "111", 3, TimeUnit.SECONDS);

    // 2 获取锁成功,查询num的值
    if (lock) {
        Object value = redisTemplate.opsForValue().get("num");

        // 2.1 判断num为空,return
        if (StringUtils.isEmpty(value)) {
            return;
        }

        // 2.2 有值就转成int
        int num = Integer.parseInt(value + "");

        // 2.3 把redis的num加1
        redisTemplate.opsForValue().set("num", ++num);

        // 2.4 释放锁,删除lock
        String lockUuid = (String) redisTemplate.opsForValue().get("lock");
        if ("111".equals(lockUuid)) {
            redisTemplate.delete("lock");
        }
    } else {
        // 3 获取锁失败,每隔0.1秒再获取
        try {
            Thread.sleep(100);
            testLock();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

2.秒杀场景(解决超卖)

思路:

  1. 维护一个用户 id,商品 id,库存信息,秒杀成功名单
  2. 先确认库存正常,即秒杀开始
  3. 先查询 uid 是否在成功名单,避免重复秒杀
  4. 再查询库存是否充足,
  5. 利用事务完成: 扣除库存、添加名单,秒杀成功

伪代码

java 复制代码
@Autowired
private RedisTemplate rt;

public boolean doSecondKill(String uid,String productId){
  
  String successListKey = "User-"+productId;
  String invenKey = "Inventory-"+productId;
  
  if(rt.get(invenKey)==null){
    print("秒杀尚未开启");
  	return false;
  }
  
  if(rt.sismember(successListKey,uid)){
    print("不可重复秒杀");
    return false;
  }
  
  if(Integer.parseInt(rt.get(invenKey))<=0){
    print("秒杀已结束");
    return false;
  }
  
  Transaction multi = rt.multi();
  multi.decr(invenKey);		//	扣除库存
  multi.sadd(successListKey,uid);	//	添加成功秒杀
  
  List<Object> result = multi.exec();
  if(result==null || result.size()==0){
    print("秒杀失败");
    return false;
  }
  
  print("秒杀成功");
  return true;
}

代码实现

java 复制代码
public class SecKill {

    public static void main(String[] args) {
        Jedis jedis = new Jedis();

    }

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

        // 2.连接jedis,使用连接池,避免超时
        JedisPool jedisPool = new JedisPool();
        Jedis jedis = jedisPool.getResource();

        //  3. 拼接key
        String invenKey = "sk:" + proid + "qt";
        String userKey = "sk:" + proid + "qt";

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

        //  4. 判断是否开始
        String inven = jedis.get(invenKey);
        if (inven == null) {
            System.out.println("秒杀尚未开始");
            jedis.close();
            return false;
        }

        //  5.判断是否抢过
        if (jedis.sismember(userKey, uid)) {
            System.out.println("不可重复参与!");
            jedis.close();
            return false;
        }

        //  6.判断库存是否充足
        if (Integer.parseInt(jedis.get(invenKey)) <= 0) {
            System.out.println("秒杀已结束,失败");
            jedis.close();
            return false;
        }

        //  multi事务操作
        Transaction multi = jedis.multi();
        multi.decr(invenKey);
        multi.sadd(userKey, uid);

        List<Object> result = multi.exec();
        if (result == null || result.size() == 0) {
            System.out.println("秒杀失败");
            jedis.close();
            return false;
        }

        System.out.println("秒杀成功");
        jedis.close();
        return true;
    }
}
相关推荐
隔壁老王1567 分钟前
tidb实时同步到mysql
数据库·mysql·tidb
2501_9032386513 分钟前
深入理解 JUnit 的 @RunWith 注解与自定义 Runner
数据库·junit·sqlserver·个人开发
小光学长23 分钟前
基于flask+vue框架的的医院预约挂号系统i1616(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库
听封31 分钟前
✨ 索引有哪些缺点以及具体有哪些索引类型
数据库·mysql
利瑞华36 分钟前
数据库索引:缺点与类型全解析
数据库·oracle
V+zmm1013439 分钟前
自驾游拼团小程序的设计与实现(ssm论文源码调试讲解)
java·数据库·微信小程序·小程序·毕业设计
ChinaRainbowSea1 小时前
1. Linux下 MySQL 的详细安装与使用
linux·数据库·sql·mysql·adb
HUNAG-DA-PAO2 小时前
Redis存在线程安全吗?为什么?
redis·安全·php
jay丿2 小时前
Redis 中列表(List)常见命令详解
数据库·redis·list
小林熬夜学编程2 小时前
【MySQL】第八弹---全面解析数据库表的增删改查操作:从创建到检索、排序与分页
linux·开发语言·数据库·mysql·算法