性能优化小结

 2020-05-23 

性能优化

nginx 动静分离

emall.conf :
image-20200515150459865

数据库多次查询变为一次

  1. 查出所有数据
1
List<CategoryEntity> selectList = baseMapper.selectList(null);
  1. 抽取方法,根据条件在 LIST 中过滤数据
1
2
3
4
private List<CategoryEntity> getParent_cid(List<CategoryEntity> selectList, Long parent_cid) {
        List<CategoryEntity> collect = selectList.stream().filter(item -> item.getParentCid() == parent_cid).collect(Collectors.toList());
        return collect;
    }

缓存 : Redis

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Override
    public Map<String, List<Catalog2Vo>> getCatalogJson() {

        String catalogJSON = redisTemplate.opsForValue().get("catalogJSON");
        if (StringUtils.isEmpty(catalogJSON)) {
            // select
            Map<String, List<Catalog2Vo>> catalogJsonFromDB = getCatalogJsonFromDB();

            // put in cache
            String s = JSON.toJSONString(catalogJsonFromDB);
            redisTemplate.opsForValue().set("catalogJSON", s);
        }
        Map<String, List<Catalog2Vo>> result = JSON.parseObject(catalogJSON, new TypeReference<Map<String, List<Catalog2Vo>>>() {
        });

        return result;
    }

缓存穿透

查询一个不存在的数据, 导致查询数据库

解决: 将查询的null值写入缓存

缓存血崩

设置缓存时 key 采用了相同的过期时间, 缓存在同一时刻失效

解决: 随机的失效时间

缓存击穿

对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,是一-种非常"热点”的数据。如果这个key在大量请求同时进来前正好失效,那么所有对这个key的数据查询都落到db。

击穿。

解决: 加锁。大量并发只让一个去查,其他人等待,查到以后释放锁,其他人获取到锁,先查缓存,就会有数据,不用查询db

分布式锁 : Redisson

通过 RedissonClient 对象, 操作 Redisson

锁续期

  1. 锁自动续期, 如果业务时间长,会自动续上30s
  2. 加锁业务完成后,不会给锁续期。即时不执行解锁代码, 也可以解锁

超时时间

  • 如果传递了锁的超时时间, 就发送给redis执行脚本,进行占锁
  • 如果未指定锁的超时时间,则使用默认 30 s 【lockWatchDogTimeout】。只要加锁成功,就会启动定时任务,重新给 锁设置过期时间(30 s),定时任务的时间默认是 看门狗的 1/3

缓存数据一致性

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

总结:

  • 能放入缓存的数据本就不应该是实时性、- 致性要求超高的。所以缓存数据的时候加上过期时间,保证每天拿到当前最新数据即可。

  • 不应该过度设计,增加系统的复杂性

  • 遇到实时性、一 致性要求高的数据,就应该查数据库,即使慢点。

解决方案

  1. 设置缓存数据过期时间, 过期后下一次查询主动更新
  2. 读写数据时加入分布式读写所

SpringCache

自定义

  1. 指定生成缓存的 key

  2. 指定 缓存失效时间

  3. 指定缓存保存为JSON
    修改默认 redisConfig

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Configuration
    @EnableCaching
    public class CacheConfig {
        @Bean
        RedisCacheConfiguration redisCacheConfiguration(){
            RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
            config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
            config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericFastJsonRedisSerializer()));

            return config;
        }
    }