minhas informações de contato
Correspondência[email protected]
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Refere-se à consulta de dados que não devem existir. Como o cache não foi atingido, o banco de dados será consultado, mas o banco de dados não possui tal registro. Não gravamos o valor nulo desta consulta no cache, o que resultará. em cada solicitação desses dados inexistentes, você precisa ir até a camada de armazenamento para consultar, o que perde o sentido do armazenamento em cache.
risco:
Usar dados inexistentes para realizar ataques aumentará a pressão instantânea sobre o banco de dados e eventualmente levará ao colapso.
resolver:
O resultado nulo é armazenado em cache e um curto período de expiração é adicionado.
Avalanche de cache: Avalanche de cache significa que quando configuramos o cache, a Chave adota o mesmo tempo de expiração, fazendo com que o cache se torne inválido em determinado momento, todas as solicitações são encaminhadas para o BD, e o BD fica sob pressão e avalanches instantâneas.
Solução: Adicione um valor aleatório ao tempo de expiração original, como 1 a 5 minutos aleatoriamente, para que a taxa de repetição de cada tempo de expiração armazenado em cache seja reduzida, dificultando a ocorrência de eventos de falha coletiva.
Divisão de cache:
resolver:
Trancar
Uma grande quantidade de simultaneidade permite que apenas um verifique, e os outros esperem. Após a verificação, o bloqueio é liberado, e os demais adquirem o bloqueio, passam o mouse, verificam o cache primeiro, e haverá dados sem ir para o banco de dados.
Resolva primeiro a penetração do cache e a avalanche
- private static final String CATALOG_JSON="CATALOG_JSON";
- @Override
- public Map<String, List<Catelog2Vo>> getCatalogJson() {
-
- /**
- * 空结果缓存:解决缓存穿透
- * 设置过期时间(加随机值) 缓存雪崩
- * 加锁 解决缓存击穿
- */
- Object result = redisTemplate.opsForValue().get(CATALOG_JSON);
- if(result!=null){
- return (Map<String, List<Catelog2Vo>>) result;
- }
-
- Map<String, List<Catelog2Vo>> map = getCatalogJsonFromDB();
- if (map==null){
- /**
- * 解决缓存穿透
- */
- map=new HashMap<>();
- }
- redisTemplate.opsForValue().set(CATALOG_JSON,map, Duration.ofDays(1));
- return map;
- }
O objeto contêiner springboot está no modo singleton por padrão, portanto, o mesmo objeto pode ser sincronizado e bloqueado. Ao usar o modo de detecção dupla, ele pode ser executado simultaneamente.
- public synchronized Map<String, List<Catelog2Vo>> getCatalogJsonFromDB() {
-
- Object result = redisTemplate.opsForValue().get(CATALOG_JSON);
- if (result != null) {
- return (Map<String, List<Catelog2Vo>>) result;
- }
-
-
- //1.查出所有1级分类
- List<CategoryEntity> selectList = baseMapper.selectList(null);
- /**
- * 将数据库的多次查询变成一次
- */
-
- //2. 封装数据
- List<CategoryEntity> level1Category = selectList.stream().filter(s -> s.getParentCid().equals(0L)).collect(Collectors.toList());
- Map<String, List<Catelog2Vo>> map = level1Category.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
- //1.每一个的一级分类,查到1级分类的所有二级分类
- List<CategoryEntity> categoryEntities = selectList.stream().filter(s -> s.getParentCid().equals(v.getCatId())).collect(Collectors.toList());
-
- List<Catelog2Vo> catelog2VoList = categoryEntities.stream().map(c -> {
- Catelog2Vo catelog2Vo = new Catelog2Vo();
- catelog2Vo.setId(c.getCatId().toString());
- catelog2Vo.setName(c.getName());
- catelog2Vo.setCatalog1Id(v.getCatId().toString());
-
- List<CategoryEntity> categoryEntities1 = selectList.stream().filter(s -> s.getParentCid().equals(c.getCatId())).collect(Collectors.toList());
- List<Catelog2Vo.Catelog3Vo> collect = categoryEntities1.stream().map(c3 -> {
- Catelog2Vo.Catelog3Vo catelog3Vo = new Catelog2Vo.Catelog3Vo();
- catelog3Vo.setId(c3.getCatId().toString());
- catelog3Vo.setName(c3.getName());
- catelog3Vo.setCatalog2Id(c.getCatId().toString());
- return catelog3Vo;
- }).collect(Collectors.toList());
-
- catelog2Vo.setCatalog3List(collect);
-
- return catelog2Vo;
- }).collect(Collectors.toList());
-
-
- return catelog2VoList;
- }));
- return map;
- }
- public Map<String, List<Catelog2Vo>> getCatalogJson() {
-
- /**
- * 空结果缓存:解决缓存穿透
- * 设置过期时间(加随机值) 缓存雪崩
- * 加锁 解决缓存击穿
- */
- Object result = redisTemplate.opsForValue().get(CATALOG_JSON);
- if (result != null) {
- return (Map<String, List<Catelog2Vo>>) result;
- }
-
- Map<String, List<Catelog2Vo>> map = getCatalogJsonFromDB();
- if (map == null) {
- /**
- * 解决缓存穿透
- */
- map = new HashMap<>();
- }
- redisTemplate.opsForValue().set(CATALOG_JSON, map, Duration.ofDays(1));
- return map;
- }
Ainda haverá problemas com a lógica do código acima. Ao ser executado simultaneamente, o primeiro thread terminará a verificação do banco de dados e liberará o bloqueio antes de colocá-lo no cache. e não há dados e, em seguida, verifique o banco de dados novamente. Não há garantia de que haverá apenas um Thread para verificar o banco de dados.
Abordagem correta
- public synchronized Map<String, List<Catelog2Vo>> getCatalogJsonFromDB() {
-
- Object result = redisTemplate.opsForValue().get(CATALOG_JSON);
- if (result != null) {
- return (Map<String, List<Catelog2Vo>>) result;
- }
-
-
- //1.查出所有1级分类
- List<CategoryEntity> selectList = baseMapper.selectList(null);
- /**
- * 将数据库的多次查询变成一次
- */
-
- //2. 封装数据
- List<CategoryEntity> level1Category = selectList.stream().filter(s -> s.getParentCid().equals(0L)).collect(Collectors.toList());
- Map<String, List<Catelog2Vo>> map = level1Category.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
- //1.每一个的一级分类,查到1级分类的所有二级分类
- List<CategoryEntity> categoryEntities = selectList.stream().filter(s -> s.getParentCid().equals(v.getCatId())).collect(Collectors.toList());
-
- List<Catelog2Vo> catelog2VoList = categoryEntities.stream().map(c -> {
- Catelog2Vo catelog2Vo = new Catelog2Vo();
- catelog2Vo.setId(c.getCatId().toString());
- catelog2Vo.setName(c.getName());
- catelog2Vo.setCatalog1Id(v.getCatId().toString());
-
- List<CategoryEntity> categoryEntities1 = selectList.stream().filter(s -> s.getParentCid().equals(c.getCatId())).collect(Collectors.toList());
- List<Catelog2Vo.Catelog3Vo> collect = categoryEntities1.stream().map(c3 -> {
- Catelog2Vo.Catelog3Vo catelog3Vo = new Catelog2Vo.Catelog3Vo();
- catelog3Vo.setId(c3.getCatId().toString());
- catelog3Vo.setName(c3.getName());
- catelog3Vo.setCatalog2Id(c.getCatId().toString());
- return catelog3Vo;
- }).collect(Collectors.toList());
-
- catelog2Vo.setCatalog3List(collect);
-
- return catelog2Vo;
- }).collect(Collectors.toList());
-
-
- return catelog2VoList;
- }));
- if (map == null) {
- /**
- * 解决缓存穿透
- */
- map = new HashMap<>();
- }
- redisTemplate.opsForValue().set(CATALOG_JSON, map, Duration.ofDays(1));
- return map;
- }
Coloque a operação de salvar no cache no bloco de código de sincronização
- @Override
- public Map<String, List<Catelog2Vo>> getCatalogJson() {
-
- /**
- * 空结果缓存:解决缓存穿透
- * 设置过期时间(加随机值) 缓存雪崩
- * 加锁 解决缓存击穿
- */
- Object result = redisTemplate.opsForValue().get(CATALOG_JSON);
- if (result != null) {
- return (Map<String, List<Catelog2Vo>>) result;
- }
-
- Map<String, List<Catelog2Vo>> map = getCatalogJsonFromDB();
-
- return map;
- }
Os bloqueios locais só podem bloquear o processo atual, portanto, precisamos de bloqueios distribuídos
Ou seja, cada bloqueio só pode bloquear o processo atual, ou seja, cada serviço irá verificar o banco de dados.