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;
    }
}
相关推荐
指尖上跳动的旋律31 分钟前
shell脚本定义特殊字符导致执行mysql文件错误的问题
数据库·mysql
一勺菠萝丶42 分钟前
MongoDB 常用操作指南(Docker 环境下)
数据库·mongodb·docker
m0_748244831 小时前
StarRocks 排查单副本表
大数据·数据库·python
C++忠实粉丝2 小时前
Redis 介绍和安装
数据库·redis·缓存
wmd131643067122 小时前
将微信配置信息存到数据库并进行调用
数据库·微信
是阿建吖!2 小时前
【Linux】基础IO(磁盘文件)
linux·服务器·数据库
凡人的AI工具箱2 小时前
每天40分玩转Django:Django国际化
数据库·人工智能·后端·python·django·sqlite
ClouGence2 小时前
Redis 到 Redis 数据迁移同步
数据库·redis·缓存
m0_748236582 小时前
《Web 应用项目开发:从构思到上线的全过程》
服务器·前端·数据库
苏三说技术2 小时前
Redis 性能优化的18招
数据库·redis·性能优化