redis分布式锁

压测工具JMeter

 

 

 

 

 

测试

 

我们看到同一个库存被使用了n次。以及数据库中库存为负数。 线程安全问题导致。

 

使用 synchronized 或者lock锁 

解决方案: 使用 synchronized 或者lock锁

package com.ykq.distrinctlock.service.impl;

import com.ykq.distrinctlock.dao.ProductStockDao;
import com.ykq.distrinctlock.service.ProductStockService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class ProductStockServiceImpl2 implements ProductStockService {
    @Autowired
    private ProductStockDao productStockDao;

    @Override
    public  String decreaseStock(Integer productId) {
              synchronized (this) {
                  //查看该商品的库存数量
                  Integer stock = productStockDao.findStockByProductId(productId);
                  if (stock > 0) {
                      //修改库存每次-1
                      productStockDao.updateStockByProductId(productId);
                      System.out.println("扣减成功!剩余库存数:" + (stock - 1));
                      return "success";
                  } else {
                      System.out.println("扣减失败!库存不足!");
                      return "fail";
                  }
              }

    }
}

使用synchronized 或者lock锁 如果我们搭建了项目集群,那么该锁无效。

使用idea开集群项目

 

 

 

 

 

Win版nginx使用

 

 

 

重新测试

发现又出现: 重复数字以及库存为负数。

解决:

将redis作为锁使用

 

 

redis的解决分布式锁的bug。

Redis分布式锁不能解决超时的问题,分布式锁有一个超时时间,程序的执行如果超出了锁的超时时间就会出现问题。
 

解决方案:

可以使用:redission依赖,redission解决redis超时问题的原理。

redission分布式锁

为持有锁的线程开启一个守护线程,守护线程会每隔10秒检查当前线程是否还持有锁,如果持有则延迟生存时间。  

添加依赖

<dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.13.4</version>
        </dependency>

添加配置类

获取redisson对象并交于spring容器管理

 @Bean
    public Redisson redisson(){
        Config config =new Config();
        config.useSingleServer().
                setAddress("redis://localhost:6379").
                //redis默认有16个数据库
                setDatabase(0);
        return (Redisson) Redisson.create(config);
    }

 

加锁

@Service
public class ProductStockServiceImpl_redission implements ProductStockService {
    @Autowired
    private ProductStockDao productStockDao;
    @Autowired
    /*获取Redisson对象*/
    private Redisson redisson;

    @Override
    public  String decreaseStock(Integer productId) {
        /*获取锁对象*/
        RLock mylock = redisson.getLock("abc::"+productId);
                try {
                    /*设置锁时长*/
                    mylock.lock(30, TimeUnit.SECONDS);
                    //查看该商品的库存数量
                    Integer stock = productStockDao.findStockByProductId(productId);
                    if (stock > 0) {
                        //修改库存每次-1
                        productStockDao.updateStockByProductId(productId);
                        System.out.println("扣减成功!剩余库存数:" + (stock - 1));
                        return "success";
                    } else {
                        System.out.println("扣减失败!库存不足!");
                        return "fail";
                    }
                }finally {
                    /*释放锁*/
                    mylock.unlock();
                }
            }

}

 结果

 缓存穿透

1. 数据库中没有该记录,缓存中也没有该记录,这时由人恶意大量访问这样的数据。这样就会导致该请求绕过缓存,直接访问数据,从而造成数据库压力过大。

解决办法
   [1]在controller加数据校验。
   [2]我们可以在redis中存入一个空对象,而且要设置过期时间不能太长。超过5分钟
   [3]我们使用布隆过滤器。底层:有一个bitmap数组,里面存储了该表的所有id.

存入空对象方式

这样他就进不去数据库了 

布隆过滤器

//伪代码
String get(String key) { //布隆过滤器钟存储的是数据库表钟对应的id
    String value = redis.get(key);  //先从缓存获取。  
    if (value  == null) { //缓存没有命中
        if(!bloomfilter.mightContain(key)){//查看布隆过滤器钟是否存在
            return null; 
        }else{
            value = db.get(key); //查询数据库
            redis.set(key, value); 
        }    
    }
    return value;
}

缓存击穿

缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。

缓存击穿解决方案:
1.设置永久不过期。【这种只适合内存】
2.使用互斥锁(mutex key)业界比较常用的做法。

 

缓存雪崩

缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是, 缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
1.什么下会发生缓存雪崩:
  [1]项目刚上线,缓存中没有任何数据
  [2]缓存出现大量过期。
  [3]redis宕机
  
2.解决办法: 
   1.上线前预先把一些热点数据放入缓存。
   2.设置过期时间为散列值
   3.搭建redis集群

Redis 淘汰策略有哪些

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值