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;
    }
}
相关推荐
Channing Lewis1 小时前
sql server如何创建表导入excel的数据
数据库·oracle·excel
秃头摸鱼侠1 小时前
MySQL安装与配置
数据库·mysql·adb
UGOTNOSHOT1 小时前
每日八股文6.3
数据库·sql
行云流水行云流水2 小时前
数据库、数据仓库、数据中台、数据湖相关概念
数据库·数据仓库
John Song2 小时前
Redis 集群批量删除key报错 CROSSSLOT Keys in request don‘t hash to the same slot
数据库·redis·哈希算法
IvanCodes2 小时前
七、Sqoop Job:简化与自动化数据迁移任务及免密执行
大数据·数据库·hadoop·sqoop
tonexuan2 小时前
MySQL 8.0 绿色版安装和配置过程
数据库·mysql
JohnYan2 小时前
工作笔记- 记一次MySQL数据移植表空间错误排除
数据库·后端·mysql
我最厉害。,。3 小时前
Windows权限提升篇&数据库篇&MYSQL&MSSQL&ORACLE&自动化项目
数据库·mysql·sqlserver
远方16093 小时前
20-Oracle 23 ai free Database Sharding-特性验证
数据库·人工智能·oracle