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;
    }
}
相关推荐
m0_470857646 分钟前
php中的foreach循环?_?PHP中foreach循环的语法结构与遍历数组对象详解.txt
jvm·数据库·python
彳亍1016 分钟前
HTML5中Canvas局部刷新区域重绘的算法优化
jvm·数据库·python
拾起零碎8 分钟前
U8/中途启用批次管理-批次档案无效
数据库
2301_7796224110 分钟前
为什么宝塔面板网站无法正常连接外部远程数据库_检查服务器安全组放行端口并开启IP授权
jvm·数据库·python
2401_8330336211 分钟前
Go语言怎么做密码加密_Go语言bcrypt密码哈希教程【总结】
jvm·数据库·python
X566114 分钟前
mysql索引基数统计更新不及时_mysqlANALYZE分析表操作
jvm·数据库·python
2301_7756398916 分钟前
React 中的渲染(Rendering)机制详解
jvm·数据库·python
JSON_L20 分钟前
MySQL 时间字段避坑指南:TIMESTAMP
数据库·mysql
m0_7403524220 分钟前
测试库与生产库怎么应对同步中断断点续传_无损发布与更新方案
jvm·数据库·python
m0_4954964128 分钟前
SQL批量更新状态机字段_使用CASE表达式一次性处理
jvm·数据库·python