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;
    }
}
相关推荐
骆晨学长11 分钟前
基于springboot的智慧社区微信小程序
java·数据库·spring boot·后端·微信小程序·小程序
@月落17 分钟前
alibaba获得店铺的所有商品 API接口
java·大数据·数据库·人工智能·学习
楠枬27 分钟前
MySQL数据的增删改查(一)
数据库·mysql
goTsHgo32 分钟前
从底层原理上解释 clickhouse 保证完全的幂等性
数据库·clickhouse
阿华的代码王国2 小时前
MySQL ------- 索引(B树B+树)
数据库·mysql
码爸2 小时前
flink 批量压缩redis集群 sink
大数据·redis·flink
Hello.Reader2 小时前
StarRocks实时分析数据库的基础与应用
大数据·数据库
执键行天涯2 小时前
【经验帖】JAVA中同方法,两次调用Mybatis,一次更新,一次查询,同一事务,第一次修改对第二次的可见性如何
java·数据库·mybatis
yanglamei19623 小时前
基于GIKT深度知识追踪模型的习题推荐系统源代码+数据库+使用说明,后端采用flask,前端采用vue
前端·数据库·flask
工作中的程序员3 小时前
ES 索引或索引模板
大数据·数据库·elasticsearch