iis服务器助手广告广告
返回顶部
首页 > 资讯 > 数据库 >Redis缓存相关的几个问题
  • 826
分享到

Redis缓存相关的几个问题

Redis缓存相关的几个问题 2018-11-06 07:11:46 826人浏览 才女
摘要

1  缓存穿透 问题描述 缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,进而给数据库带来压力。 解决方案 缓存空值,即对于不存在的数

Redis缓存相关的几个问题

1  缓存穿透

问题描述

缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,进而给数据库带来压力。

解决方案

缓存空值,即对于不存在的数据,在缓存中放置一个空对象(注意,设置过期时间)

2  缓存击穿

问题描述

缓存击穿是指热点key在某个时间点过期的时候,而恰好在这个时间点对这个Key有大量的并发请求过来,从而大量的请求打到数据库。

解决方案

加互斥,在并发的多个请求中,只有第一个请求线程能拿到锁并执行数据库查询操作,其他的线程拿不到锁就阻塞等着,等到第一个线程将数据写入缓存后,直接走缓存。

3  缓存雪崩

问题描述

缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。

解决方案

可以给缓存的过期时间时加上一个随机值时间,使得每个 key 的过期时间分布开来,不会集中在同一时刻失效。

4  缓存服务器宕机

问题描述

并发太高,缓存服务器连接被打满,最后挂了

解决方案

  • 限流:Nginxspring cloud gateway、sentinel等都支持限流
  • 增加本地缓存(JVM内存缓存),减轻Redis一部分压力

5  Redis实现分布式

问题描述

如果用redis做分布式锁的话,有可能会存在这样一个问题:key丢失。比如,master节点写成功了还没来得及将它复制给slave就挂了,于是slave成为新的master,于是key丢失了,后果就是没锁住,多个线程持有同一把互斥锁。

解决方案

必须等redis把这个key复制给所有的slave并且都持久化完成后,才能返回加锁成功。但是这样的话,对其加锁的性能就会有影响。

ZooKeeper同样也可以实现分布式锁。在分布式锁的的实现上,zookeeper的重点是CP,redis的重点是AP。因此,要求强一致性就用zookeeper,对性能要求比较高的话就用redis

5  示例代码

pom.xml


 xmlns="Http://Maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.6.7
         
    
    com.example
    demo426
    0.0.1-SNAPSHOT
    demo426
    
        1.8
    
    
        
            org.springframework.boot
            spring-boot-starter-WEB
        
        
            org.springframework.boot
            spring-boot-starter-data-redis
        
        
            org.redisson
            redisson
            3.17.1
        
        
            com.GitHub.ben-manes.caffeine
            caffeine
            2.9.2
        
        
            com.alibaba
            fastJSON
            2.0.1
        
        
            org.apache.commons
            commons-lang3
            3.12.0
        
        
            org.projectlombok
            lombok
            true
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
    
    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
                
                    
                        
                            org.projectlombok
                            lombok
                        
                    
                
            
        
    

Product.java

package com.example.demo426.domain;

import lombok.Data;

import java.io.Serializable;
import java.time.LocalDateTime;


@Data
public class Product implements Serializable {

    private Long productId;

    private String productName;

    private Integer stock;

    private LocalDateTime createTime;

    private LocalDateTime updateTime;

    private Integer isDeleted;

    private Integer version;
}

ProductController.java

package com.example.demo426.controller;

import com.alibaba.fastjson.JSON;
import com.example.demo426.domain.Product;
import com.example.demo426.service.ProductService;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.time.Duration;
import java.util.Random;
import java.util.concurrent.TimeUnit;


@RestController
@RequestMapping("/product")
public class ProductController {

    @Autowired
    private RedissonClient redissonClient;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private ProductService productService;

    private final Cache PRODUCT_LOCAL_CACHE = Caffeine.newBuilder()
            .maximumSize(100)
            .expireAfterWrite(Duration.ofMinutes(60))
            .build();

    private final String PRODUCT_CACHE_PREFIX = "cache:product:";
    private final String PRODUCT_LOCK_PREFIX = "lock:product:";
    private final String PRODUCT_RW_LOCK_PREFIX = "lock:rw:product:";

    
    @PostMapping("/update")
    public void update(@RequestBody Product productDTO) {
        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(PRODUCT_RW_LOCK_PREFIX + productDTO.getProductId());
        RLock wLock = readWriteLock.writeLock();
        wLock.lock();
        try {
            //  写数据库
            //  update product set name=xxx,...,version=version+1 where id=xx and version=xxx
            Product product = productService.update(productDTO);
            //  放入缓存
            PRODUCT_LOCAL_CACHE.put(product.getProductId(), product);
            stringRedisTemplate.opsForValue().set(PRODUCT_CACHE_PREFIX + product.getProductId(), JSON.toJSONString(product), getProductTimeout(60), TimeUnit.MINUTES);
        } finally {
            wLock.unlock();
        }
    }

    
    @GetMapping("/query")
    public Product query(@RequestParam("productId") Long productId) {
        //  1. 尝试从缓存读取
        Product product = getProductFromCache(productId);
        if (null != product) {
            return product;
        }
        //  2. 准备从数据库中加载
        //  互斥锁
        RLock lock = redissonClient.getLock(PRODUCT_LOCK_PREFIX + productId);
        lock.lock();
        try {
            //  再次先查缓存
            product = getProductFromCache(productId);
            if (null != product) {
                return product;
            }

            //  为了避免缓存与数据库双写不一致
            //  读写锁
            RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(PRODUCT_RW_LOCK_PREFIX + productId);
            RLock rLock = readWriteLock.readLock();
            rLock.lock();
            try {
                //  查数据库
                product = productService.getById(productId);
                if (null == product) {
                    //  如果数据库中没有,则放置一个空对象,这样做是为了避免”缓存穿透“问题
                    product = new Product();
                } else {
                    PRODUCT_LOCAL_CACHE.put(productId, product);
                }
                //  放入缓存
                stringRedisTemplate.opsForValue().set(PRODUCT_CACHE_PREFIX + productId, JSON.toJSONString(product), getProductTimeout(60), TimeUnit.MINUTES);
            } finally {
                rLock.unlock();
            }
        } finally {
            lock.unlock();
        }

        return null;
    }

    
    private Product getProductFromCache(Long productId) {
        //  1. 尝试从本地缓存读取
        Product product = PRODUCT_LOCAL_CACHE.getIfPresent(productId);
        if (null != product) {
            return product;
        }
        //  2. 尝试从Redis中读取
        String key = PRODUCT_CACHE_PREFIX + productId;
        String value = stringRedisTemplate.opsForValue().get(key);
        if (StringUtils.isNotBlank(value)) {
            product = JSON.parseObject(value, Product.class);
            return product;
        }
        return null;
    }

    
    private int getProductTimeout(int initVal) {
        Random random = new Random(10);
        return initVal + random.nextInt();
    }
}

原文地址:https://www.cnblogs.com/cjsblog/arcHive/2022/04/26/16196302.html

您可能感兴趣的文档:

--结束END--

本文标题: Redis缓存相关的几个问题

本文链接: https://www.lsjlt.com/news/9297.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • oracle怎么查询当前用户所有的表
    要查询当前用户拥有的所有表,可以使用以下 sql 命令:select * from user_tables; 如何查询当前用户拥有的所有表 要查询当前用户拥有的所有表,可以使...
    99+
    2024-05-15
    oracle
  • oracle怎么备份表中数据
    oracle 表数据备份的方法包括:导出数据 (exp):将表数据导出到外部文件。导入数据 (imp):将导出文件中的数据导入表中。用户管理的备份 (umr):允许用户控制备份和恢复过程...
    99+
    2024-05-15
    oracle
  • oracle怎么做到数据实时备份
    oracle 实时备份通过持续保持数据库和事务日志的副本来实现数据保护,提供快速恢复。实现机制主要包括归档重做日志和 asm 卷管理系统。它最小化数据丢失、加快恢复时间、消除手动备份任务...
    99+
    2024-05-15
    oracle 数据丢失
  • oracle怎么查询所有的表空间
    要查询 oracle 中的所有表空间,可以使用 sql 语句 "select tablespace_name from dba_tablespaces",其中 dba_tabl...
    99+
    2024-05-15
    oracle
  • oracle怎么创建新用户并赋予权限设置
    答案:要创建 oracle 新用户,请执行以下步骤:以具有 create user 权限的用户身份登录;在 sql*plus 窗口中输入 create user identified ...
    99+
    2024-05-15
    oracle
  • oracle怎么建立新用户
    在 oracle 数据库中创建用户的方法:使用 sql*plus 连接数据库;使用 create user 语法创建新用户;根据用户需要授予权限;注销并重新登录以使更改生效。 如何在 ...
    99+
    2024-05-15
    oracle
  • oracle怎么创建新用户并赋予权限密码
    本教程详细介绍了如何使用 oracle 创建一个新用户并授予其权限:创建新用户并设置密码。授予对特定表的读写权限。授予创建序列的权限。根据需要授予其他权限。 如何使用 Oracle 创...
    99+
    2024-05-15
    oracle
  • oracle怎么查询时间段内的数据记录表
    在 oracle 数据库中查询指定时间段内的数据记录表,可以使用 between 操作符,用于比较日期或时间的范围。语法:select * from table_name wh...
    99+
    2024-05-15
    oracle
  • oracle怎么查看表的分区
    问题:如何查看 oracle 表的分区?步骤:查询数据字典视图 all_tab_partitions,指定表名。结果显示分区名称、上边界值和下边界值。 如何查看 Oracle 表的分区...
    99+
    2024-05-15
    oracle
  • oracle怎么导入dump文件
    要导入 dump 文件,请先停止 oracle 服务,然后使用 impdp 命令。步骤包括:停止 oracle 数据库服务。导航到 oracle 数据泵工具目录。使用 impdp 命令导...
    99+
    2024-05-15
    oracle
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作