【Redis】一文带你彻底了解Redis!

目录

Redis在项目中的应用

做缓存(以SpringBoot项目中Redis做缓存为例)

RedisTemplate方法

使用Spring Cache注解来管理缓存的方式

缓存穿透

缓存雪崩

缓存和数据库中的数据一致性问题

Redis本身特性

Redis内存淘汰机制

allkeys-lru(least recently used):当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(这个是最常用的)

Redis中的持久化方式

快照(snapshotting)持久化(RDB)

AOF(append-only file)持久化


Redis在项目中的应用

做缓存(以SpringBoot项目中Redis做缓存为例)

RedisTemplate方法

  1. 添加依赖:在项目的构建文件(如pom.xml)中添加Spring Data Redis的依赖。

  2. 配置Redis连接信息:在配置文件(如application.propertiesapplication.yml)中配置Redis的连接信息,包括主机、端口、密码等。

  3. 创建配置类:创建一个Java配置类,用于配置RedisTemplate和相关的连接工厂、序列化器等。

@Configuration
public class RedisConfig {

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        // 配置Redis连接工厂
        RedisStandaloneConfiguration standaloneConfig = new RedisStandaloneConfiguration();
        standaloneConfig.setHostName("redisHost");
        standaloneConfig.setPort(6379);
        standaloneConfig.setPassword(RedisPassword.of("redisPassword"));

        JedisConnectionFactory connectionFactory = new JedisConnectionFactory(standaloneConfig);
        connectionFactory.afterPropertiesSet();

        return connectionFactory;
    }

    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        // 配置RedisTemplate
        RedisTemplate redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.setDefaultSerializer(new StringRedisSerializer());
        redisTemplate.setKeySerializer(redisTemplate.getStringSerializer());
        redisTemplate.setValueSerializer(redisTemplate.getDefaultSerializer());
        redisTemplate.setHashKeySerializer(redisTemplate.getStringSerializer());
        redisTemplate.setHashValueSerializer(redisTemplate.getDefaultSerializer());
        redisTemplate.afterPropertiesSet();

        return redisTemplate;
    }
}

4. 创建Service类:在Service类中注入RedisTemplate,并根据具体需求编写相应的缓存操作方法。 

@Service
public class ProductService {

    @Autowired
    private ProductRepository productRepository;
  
    @Autowired
    private RedisTemplate redisTemplate;
  
    public Product getProductById(Long id) {
        String cacheKey = "product:" + id;
        
        // 先尝试从缓存中获取产品
        Product product = (Product) redisTemplate.opsForValue().get(cacheKey);
        
        // 如果缓存中不存在,则从数据库中获取并放入缓存
        if (product == null) {
            product = productRepository.findById(id);
            redisTemplate.opsForValue().set(cacheKey, product);
        }
        
        return product;
    }

    public Product saveProduct(Product product) {
        Product savedProduct = productRepository.save(product);
        String cacheKey = "product:" + savedProduct.getId();
        
        // 更新或新增缓存数据
        redisTemplate.opsForValue().set(cacheKey, savedProduct);
        
        return savedProduct;
    }

    public void deleteProduct(Long id) {
        productRepository.deleteById(id);
        String cacheKey = "product:" + id;
        
        // 删除对应的缓存数据
        redisTemplate.delete(cacheKey);
    }
    
    public Product updateProduct(Product product) {
        Product updatedProduct = productRepository.save(product);
        String cacheKey = "product:" + updatedProduct.getId();
        
        // 更新缓存中的数据
        redisTemplate.opsForValue().set(cacheKey, updatedProduct);
        
        return updatedProduct;
    }
}

使用Spring Cache注解来管理缓存的方式

  1. 添加Redis依赖:在pom.xml文件中添加Redis相关依赖,例如spring-boot-starter-data-redis

  2. 配置Redis连接信息:在application.properties(或application.yml)文件中配置Redis连接信息,包括主机、端口、密码等。

  3. 创建Redis配置类:创建一个Java配置类,用于配置Redis连接工厂、缓存管理器等Redis相关的配置项。这个类通常使用@Configuration注解标记

  4. 启用缓存功能:在主类中添加@EnableCaching注解启用Spring Cache,这样可以使用缓存注解来管理缓存

  5. 定义缓存操作方法:在需要缓存的方法上添加缓存注解,例如@Cacheable@CachePut@CacheEvict等,定义缓存的读取、更新和清除规则。

下面代码是一个简单的流程:

@Configuration
@EnableCaching
public class RedisCacheConfig extends CachingConfigurerSupport {
    
    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        // 创建 Redis 缓存配置实例
        RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofMinutes(10)); // 设置缓存的过期时间为 10 分钟

        // 创建 Redis 缓存管理器,并关联 Redis 连接工厂和默认缓存配置
        return RedisCacheManager.builder(redisConnectionFactory)
                .cacheDefaults(cacheConfiguration)
                .build();
    }
}
@Service
public class ProductService {

    @Autowired
    private ProductRepository productRepository;
  
    @Cacheable(value="products", key="#id")
    public Product getProductById(Long id) {
        // 从缓存中获取指定 id 的 Product 对象,如果缓存中不存在,则通过调用 productRepository.findById(id) 方法获取数据,并将数据放入缓存中
        return productRepository.findById(id);
    }

    @CachePut(value="products", key="#product.id")
    public Product saveProduct(Product product) {
        // 将传入的 Product 对象保存到数据库中,并更新缓存中的数据或新增缓存数据(根据 key 的存在与否判断)
        return productRepository.save(product);
    }

    @CacheEvict(value="products", key="#id")
    public void deleteProduct(Long id) {
        // 删除指定 id 的 Product 对象,并删除缓存中对应的数据
        productRepository.deleteById(id);
    }
    
    @CachePut(value="products", key="#product.id")
    public Product updateProduct(Product product) {
        // 直接更新数据库中的 Product 对象,并更新缓存中对应的数据
        return productRepository.save(product);
    }
}

 

缓存穿透

  • 缓存击穿是指在高并发场景下,当查询或操作一条缓存中没有的数据时,请求直接打到数据库中,导致对数据库造成压力。
  • 这通常发生在某个热点数据过期后,恶意用户发送大量查询该数据的请求,导致缓存无法命中,而每个请求都要访问数据库。
  • 解决缓存击穿可以使用布隆过滤器。布隆过滤器是一种空间效率很高的概率算法,通过快速判断一个元素是否存在于集合中,可以帮助快速过滤掉不存在的数据,减轻对数据库的访问压力。在缓存查询前,可以先使用布隆过滤器快速判断该数据是否可能存在于缓存中,如果不存在,则直接返回缓存未命中,避免对数据库的不必要访问。

缓存雪崩

  • 缓存雪崩是指在某一时刻,大量缓存项同时失效或过期,导致大量请求直接访问数据库,造成数据库压力剧增。
  • 这通常发生在缓存中的数据设置了相同的过期时间,而这些数据在同一时间段失效,导致大量请求无法命中缓存。
  • 解决缓存雪崩可以采取多种方法。一种常见的方式是设置不同的过期时间,使得缓存的失效时间分散开来,避免同时失效。另一种方式是为热点数据设置永不过期,保证热点数据始终可用。还可以使用互斥锁或限流措施,避免大量请求同时涌入数据库。

缓存和数据库中的数据一致性问题

做验证码功能

做session共享,分布式Session存储,相对普通session的优势

Redis本身特性

数据结构:

  • String——SDS简单动态字符串 O(1)获得长度
  • List——压缩列表,类似于LinkList,支持队列的特性
  • Set——HashSet
  • Hash——类似于HashTable,渐进式扩容的特点
  • ZSet——跳表,数据结构

地理位置:

  • 布隆过滤器
  • 数据过期机制:
  • 设置过期时间
  • 定期删除——从所有key中随机选取一部分访问,将过期的删除
  • 惰性删除(在访问这个key的时候才检查这个key是否过期,如果发现过期就在返回当前结果后立即删除)

Redis内存淘汰机制

Redis提供了6种内存淘汰机制:

  1. volatile-lru(least recently used):从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰

  2. volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰

  3. volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰

  4. allkeys-lru(least recently used):当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(这个是最常用的
  5. allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰

  6. no-eviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。这个应该没人使用吧!

Redis4.0 版本后增加了以下两种:

  1. volatile-lfu(least frequently used):从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰

  2. allkeys-lfu(least frequently used):当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的 key

Redis中的持久化方式

快照(snapshotting)持久化(RDB)

        Redis可以通过创建快照来获得存储在内存里面的数据在某个时间点上的副本。Redis创建快照之后,可以对快照进行备份,可以将快照复制到其他服务器副本(Redis主从结构,主要用来提高Redis性能),还可以将快照留在原地以便重启服务器的时候使用。

  • 快照持久化是将数据库在某个时间点的数据集合完整地保存到磁盘上的文件中。它通过将数据库的数据状态转储到一个二进制文件中,实现了数据的快照
  • 在快照持久化过程中,Redis会单独创建一个子进程来处理数据的写操作,并将数据写入到一个新的RDB文件中。该RDB文件包含了数据库的数据快照,包括键值对、数据类型和相关的元数据等。
  • 快照持久化是一种非常高效的持久化方式,适用于大规模的数据集合或需要定期备份数据库的场景。它可以通过配置定期执行或者手动执行命令来生成快照。

        快照持久化是Redis默认采用的持久化方式,在redis.conf配置文件中默认有此下配置:

save 900 1              #在900秒(15分钟)之后,如果至少有1个key发生变化,Redis就会自动触发BGSAVE命令创建快照。

save 300 10            #在300秒(5分钟)之后,如果至少有10个key发生变化,Redis就会自动触发BGSAVE命令创建快照。

save 60 10000        #在60秒(1分钟)之后,如果至少有10000个key发生变化,Redis就会自动触发BGSAVE命令创建快照。

        根据配置,快照将被写入dbfilename选项指定的文件里面,并存储在dir选项指定的路径上面。如果在新的快照文件创建完毕之前,Redis、系统或者硬件这三者中的任意一个崩溃了,那么Redis将丢失最近一次创建快照写入的所有数据。

        举个例子:假设Redis的上一个快照是2:35开始创建的,并且已经创建成功。下午3:06时,Redis又开始创建新的快照,并且在下午3:08快照创建完毕之前,有35个键进行了更新。如果在下午3:06到3:08期间,系统发生了崩溃,导致Redis无法完成新快照的创建工作,那么Redis将丢失下午2:35之后写入的所有数据。另一方面,如果系统恰好在新的快照文件创建完毕之后崩溃,那么Redis将丢失35个键的更新数据。

AOF(append-only file)持久化

  • AOF持久化采用追加日志(append-only log)的方式,将Redis服务器接收到的每个写命令记录下来。这些写命令按顺序追加到AOF文件的末尾,形成一个有序的命令日志
  • AOF文件通过记录每个写命令的方式,可以完全恢复整个数据库的状态。当Redis重启时,它会通过重新执行AOF文件中的写命令来重建数据库并还原数据。
  • AOF持久化有不同的同步选项,可以在性能和数据安全之间进行权衡。常见的同步选项包括无同步、每秒同步和每个写命令同步
  • 相对于快照持久化,AOF持久化具有较低的延迟和更高的数据安全性,因为AOF文件记录了每个写命令,可以从命令级别的日志中恢复数据。

与快照持久化相比,AOF持久化 的实时性更好,因此已成为主流的持久化方案。默认情况下Redis没有开启AOF(append only file)方式的持久化,可以通过appendonly参数开启:

appendonly yes

        开启AOF持久化后每执行一条会更改Redis中的数据的命令,Redis就会将该命令写入硬盘中的AOF文件。AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的,默认的文件名是appendonly.aof。

        举个例子,比如当你向Redis执行以下写指令时

SET key1 value1
SET key2 value2

 这些命令将会被追加到AOF文件的末尾,使得AOF文件变为:

*3
$3
SET
$4
key1
$6
value1
*3
$3
SET
$4
key2
$6
value2

        AOF(append-only file)始终都是一个文件,在这个文件中持续地追加所执行过的指令。

        AOF持久化机制通过记录每个写命令的方式来保存数据库的状态。当Redis服务器接收到写命令时,它会将命令追加到AOF文件的末尾,形成一个有序的命令日志。

        

在Redis的配置文件中存在三种同步方式,它们分别是:

appendfsync always     #每次有数据修改发生时都会写入AOF文件,这样会严重降低Redis的速度
appendfsync everysec  #每秒钟同步一次,显示地将多个写命令同步到硬盘
appendfsync no      #让操作系统决定何时进行同步

        appendfsync always 可以实现将数据丢失减到最少,不过这种方式需要对硬盘进行大量的写入而且每次只写入一个命令,十分影响Redis的速度。另外使用固态硬盘的用户谨慎使用appendfsync always选项,因为这会明显降低固态硬盘的使用寿命。

        为了兼顾数据和写入性能,用户可以考虑 appendfsync everysec选项 ,让Redis每秒同步一次AOF文件,Redis性能几乎没受到任何影响。而且这样即使出现系统崩溃,用户最多只会丢失一秒之内产生的数据。当硬盘忙于执行写入操作的时候,Redis还会优雅的放慢自己的速度以便适应硬盘的最大写入速度。

        appendfsync no 选项一般不推荐,这种方案会使Redis丢失不定量的数据而且如果用户的硬盘处理写入操作的速度不够的话,那么当缓冲区被等待写入的数据填满时,Redis的写入操作将被阻塞,这会导致Redis的请求速度变慢。

        虽然AOF持久化非常灵活地提供了多种不同的选项来满足不同应用程序对数据安全的不同要求,但AOF持久化也有缺陷——AOF文件的体积太大。

 

你可能感兴趣的:(redis,数据库,缓存,spring,java)