记录444

Nginx

在这里插入图片描述

JVM

请添加图片描述
所有的对象实例以及数组都要在堆上分配。堆是垃圾收集器管理的主要区域,也被称为GC
堆”;也是我们优化最多考虑的地方。
堆可以细分为:

  • 新生代
  • Eden 空间
  • From Survivor 空间
  • To Survivor 空间
  • 老年代
  • 永久代/元空间
  • Java8 以前永久代,受 jvm 管理,java8 以后元空间,直接使用物理内存。因此,
    默认情况下,元空间的大小仅受本地内存限制

请添加图片描述
请添加图片描述

缓存&分布式锁

整合redis

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
     <exclusions>
          <exclusion>
                <groupId>io.lettuce</groupId>
                <artifactId>lettuce-core</artifactId>
          </exclusion>
     </exclusions>
</dependency>

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>
spring:
	redis:
		host: ***.***.***.***

缓存穿透

查询一个一定不存在的数据,缓存中没有所有去查询数据库,但数据库也没有,将查询的null写入缓存中,导致以后的结果全是null

风险

利用不存在的数据进行攻击,数据库压力增大,导致崩溃

缓存雪崩

在设置key时使用了相同的过期时间,导致缓存中某一时刻全部失效,请求全部查询数据库

解决

设置key的过期时间为随机

缓存击穿 –热点key失效

对于设置了过期时间的key,如果key可能会在某个时间点被高频访问,称为热点数据
如果这个key正好在大量请求同时进来的时候正好失效,则会大量访问数据库

解决

加锁

时序问题
请添加图片描述

分布式锁

请添加图片描述

    //分布式锁
    public Map<String, List<Catelog2Vo>> getCatalogJsonFromDBWithRedisLock() {

        String uuid = UUID.randomUUID().toString();
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid,20,TimeUnit.SECONDS);
        if(lock){
            //加锁成功
            Map<String, List<Catelog2Vo>> dataFromDB;
            try{
                dataFromDB = getDataFromDB();
            }finally {
                //lua脚本解锁
                String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
                Integer execute = redisTemplate.execute(new DefaultRedisScript<Integer>(script, Integer.class),
                        Arrays.asList("lock"), uuid);
            }
            //删锁必须是原子操作
//            String lockValue = redisTemplate.opsForValue().get("lock");
//            if(uuid.equals(lockValue)){
//                redisTemplate.delete("lock");
//            }
            return dataFromDB;
        }else {
            //重试  自旋锁
            return getCatalogJsonFromDBWithRedisLock();
        }

    }

Redisson

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
     <version>3.17.0</version>
</dependency>
@Configuration
public class MyRedissonConfig {

    @Bean(destroyMethod = "shutdown")
    public RedissonClient redissonClient() throws IOException{
        Config config = new Config();
        config.useSingleServer().setAddress("redis://192.168.***.***:6379");
        return Redisson.create(config);
    }
    
}
    @Autowired
    RedissonClient redissonClient;

    @Test
    void testRedisson(){
        System.out.println(redissonClient);
    }

Redisson的锁为 阻塞时等待,默认30S
lock.lock() 自己未指定锁的超时时间
占锁成功后,会启动定时任务,重新给锁设置过期时间(看门狗的默认时间)(每10s续期)
如果业务超长,锁会自动续期,自动续期30s
加锁的业务一完成,就不会续期,即使不手动解锁,也会在30s内自动过期
Redisson不会出现死锁

	    RLock lock = redissonClient.getLock("lock");
        lock.lock();
        try{
            //执行业务
        }catch(Exception e){
            //异常处理
        }finally{
            //解锁
            lock.unlock();
        }

lock.lock(10,TimeUnit.SECOUNDS);
自己指定时间时,底层时给redis发送lua脚本进行占锁,且锁到期后,不会自动续期


读写锁

保证一定能读到最新数据,修改期间,写锁是一个排它锁(互斥锁、独享锁),读锁是一个共享锁

写锁没有释放时读锁必须等待

读 + 读 :相当于无锁,并发读,只会在Redis中记录好,所有当前的读锁。他们都会同时加锁成功
写 + 读 :必须等待写锁释放
写 + 写 :阻塞方式
读 + 写 :有读锁。写也需要等待
只要有读或者写的存都必须等待
public void write(){
	RReadWriteLock readWriteLock = redisson.getReadWriteLock("rw-lock");
	Rlock rLock = readWriteLock.writeLock();
	try{
		rLock.lock();
		//写入数据
	}finally{
		rLock.unlock();
	}
}


public void read(){
	RReadWriteLock readWriteLock = redisson.getReadWriteLock("rw-lock");
	Rlock rLock = readWriteLock.readLock();
	try{
		rLock.lock();
		//读数据
	}finally{
		rLock.unlock();
	}
}

Semaphore

信号量也可以做分布式限流

public void park(){
	RSemaphore park = redisson.getSemaphore("park");
	park.acquire();//获取信号量,信号量-1,等待式获取
}

public void go(){
	RSemaphore park = redisson.getSemaphore("park");
	park.relase();//释放信号量,信号量+1
}

boolean park.tryAcquire() 尝试获取信号量,如果获取不到则立刻返回false

CountDownLatch

public void lock(){
	RCountDownLatch latch = redisson.getCountDownLatch("latch");
	latch.trySetCount(5);
	latch.await();
}

public void down(){
	RCountDownLatch latch = redisson.getCountDownLatch("latch");
	latch.countDown();//count -1 需五个都减完
}

缓存数据一致性

请添加图片描述
请添加图片描述无论是双写模式还是失效模式,都会导致缓存的不一致问题。即多个实例同时更新会出事。怎么办?
1、如果是用户纬度数据(订单数据、用户数据),这种并发几率非常小,不用考虑这个问题,缓存数据加 上过期时间,每隔一段时间触发读的主动更新即可
2、如果是菜单,商品介绍等基础数据,也可以去使用canal订阅binlog的方式。
3、缓存数据+过期时间也足够解决大部分业务对于缓存的要求。
4、通过加锁保证并发读写,写写的时候按顺序排好队。读读无所谓。所以适合使用读写锁。(业务不关心 脏数据,允许临时脏数据可忽略);

总结:
我们能放入缓存的数据本就不应该是实时性、一致性要求超高的。所以缓存数据的时候加上过期时间,保 证每天拿到当前最新数据即可。
我们不应该过度设计,增加系统的复杂性
遇到实时性、一致性要求高的数据,就应该查数据库,即使慢点。

Spring Cache

spring cache + redis
spring-boot-starter-cache
spring-boot-starter-data-redis

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
spring:
  cache:
    type: redis

在启动类上加上 @EnableCaching
使用注解
当前如果缓存中存在为 name 的缓存,则不会调用方法直接返回缓存数据

@Cacheable("name")
public void get(){
	//查询数据库
}

延时双删

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值