Redis数据库——性能管理

本文详细探讨了Redis的内存管理,包括InfoMemory监控内存使用、内存碎片率与使用率、内存回收策略,以及如何处理缓存雪崩、穿透、击穿问题,提出解决方案如设置永不过期key、高可用集群和缓存预热策略。
摘要由CSDN通过智能技术生成

目录

一、Redis性能管理

1.Info Memory——查看Redis内存使用

2.内存碎片率

3.内存使用率

4.内存回收key

二、Redis缓存雪崩、穿透、击穿、预热

1.缓存雪崩

1.1什么是缓存雪崩

1.2产生原因

1.3实际应用场景

1.4解决方案

1.4.1方案一设置redis的某些key永不过期

1.4.2搭建高可用redis集群+持久化

2.缓存穿透

2.1什么是缓存穿透

2.2产生原因

2.3解决方案

2.3.1方案一使用布隆过滤器

2.3.2方案二缓存空对象或者默认值

3.缓存击穿

3.1什么是缓存击穿

3.2发生场景

3.3解决方案

3.3.1方案一差异失效时间

3.3.2方案二采用互斥锁,双检加锁策略更新

4.缓存预热

4.1缓存预热实现方案

4.1.1方案一批量预加载数据

4.1.2异步预热

4.1.3定时任务预热

4.1.4使用SpringBoot InitializingBean接口实现

5.总结


一、Redis性能管理

1.Info Memory——查看Redis内存使用

[root@localhost ~]#redis-cli 
127.0.0.1:6379> info memory
# Memory
used_memory:853304
used_memory_human:833.30K
used_memory_rss:10510336
used_memory_rss_human:10.02M
used_memory_peak:853304
used_memory_peak_human:833.30K
used_memory_peak_perc:100.01%
used_memory_overhead:841078
used_memory_startup:791384
used_memory_dataset:12226
used_memory_dataset_perc:19.74%
allocator_allocated:1661928
allocator_active:2068480
allocator_resident:9039872
total_system_memory:1911857152
total_system_memory_human:1.78G
used_memory_lua:37888
used_memory_lua_human:37.00K
used_memory_scripts:0
used_memory_scripts_human:0B
number_of_cached_scripts:0
maxmemory:0
maxmemory_human:0B
maxmemory_policy:noeviction
allocator_frag_ratio:1.24
allocator_frag_bytes:406552
allocator_rss_ratio:4.37
allocator_rss_bytes:6971392
rss_overhead_ratio:1.16
rss_overhead_bytes:1470464
mem_fragmentation_ratio:12.94
mem_fragmentation_bytes:9698056
mem_not_counted_for_evict:0
mem_replication_backlog:0
mem_clients_slaves:0
mem_clients_normal:49694
mem_aof_buffer:0
mem_allocator:jemalloc-5.1.0
active_defrag_running:0
lazyfree_pending_objects:0

2.内存碎片率

Redis内部有自己的内存管理器,为了提高内存使用的效率,来对内存的申请和释放进行管理。
Redis中的值删除的时候,并没有把内存直接释放,交还给操作系统,而是交给了Redis内部有内存管理器。

Redis中申请内存的时候,也是先看自己的内存管理器中是否有足够的内存可用。

Redis的这种机制,提高了内存的使用率,但是会使Redis中有部分自己没在用,却不释放的内存,导致了内存碎片的发生。

内存碎片是由操作系统低效的分配/回收物理内存导致的(不连续的物理内存分配)。 

操作系统分配的内存值 used_memory_rss 除以 Redis 使用的内存总量值 used_memory 计算得出。内存值 used_memory_rss 表示该进程所占物理内存的大小,即为操作系统分配给 Redis 实例的内存大小。

除了用户定义的数据和内部开销以外,used_memory_rss 指标还包含了内存碎片的开销, 内存碎片是由操作系统低效的分配/回收物理内存导致的(不连续的物理内存分配)。

举例来说:Redis 需要分配连续内存块来存储 1G 的数据集。如果物理内存上没有超过 1G 的连续内存块, 那操作系统就不得不使用多个不连续的小内存块来分配并存储这 1G 数据,该操作就会导致内存碎片的产生。

跟踪内存碎片率对理解Redis实例的资源性能是非常重要的

  • 内存碎片率稍大于1是合理的,这个值表示内存碎片率比较低,也说明 Redis 没有发生内存交换。
  • 内存碎片率超过1.5,说明Redis消耗了实际需要物理内存的150%,其中50%是内存碎片率。需要在redis-cli工具上输入shutdown save 命令,让 Redis 数据库执行保存操作并关闭 Redis 服务,再重启服务器。
  • 内存碎片率低于1的,说明Redis内存分配超出了物理内存,操作系统正在进行内存交换。需要增加可用物理内存或减少 Redis 内存占用。

3.内存使用率

redis实例的内存使用率超过可用最大内存,操作系统将开始进行内存与swap空间交换。

避免内存交换发生的方法

  • 针对缓存数据大小选择安装 Redis 实例
  • 尽可能的使用Hash数据结构存储
  • 设置key的过期时间

4.内存回收key

内存清理策略,保证合理分配redis有限的内存资源。

当达到设置的最大阀值时,需选择一种key的回收策略,默认情况下回收策略是禁止删除。
配置文件中修改 maxmemory-policy 属性值:

vim /etc/redis/6379.conf

sed -n '598p' /etc/redis/6379.conf
maxmemory-policy noenviction
  • volatile-lru:使用LRU算法从已设置过期时间的数据集合中淘汰数据(移除最近最少使用的key,针对设置了TTL的key)
  • volatile-ttl:从已设置过期时间的数据集合中挑选即将过期的数据淘汰(移除最近过期的key)
  • volatile-random:从已设置过期时间的数据集合中随机挑选数据淘汰(在设置了TTL的key里随机移除)
  • allkeys-lru:使用LRU算法从所有数据集合中淘汰数据(移除最少使用的key,针对所有的key)
  • allkeys-random:从数据集合中任意选择数据淘汰(随机移除key)
  • noenviction:禁止淘汰数据(不删除直到写满时报错)

二、Redis缓存雪崩、穿透、击穿、预热

在实际工程中,Redis缓存问题常伴随高并发场景出现。例如,电商大促、活动报名、突发新闻时,由于缓存失效导致大量请求访问数据库,导致雪崩、击穿、穿透等问题。因此,新系统上线前需预热缓存,以应对高并发,减轻数据库压力。

1.缓存雪崩

1.1什么是缓存雪崩

指在一个高并发的系统中,由于大量的缓存数据在同一时间段内过期或失效,导致大量请求无法从缓存中获取数据,因此大量并发请求可能导致数据库服务器过载,甚至宕机,从而引发整个系统崩溃。

1.2产生原因

  • 硬件故障。 比如:机房着火、停电等导致redis挂机。
  • 软件设计。redis中有大量key同时过期或者大面积失效。导致缓存中查不到对应key。此时访问压力给到数据库。

1.3实际应用场景

  • 金融系统股票价格、汇率等数据实时变动;
  • 热点新闻缓存过期时,大量用户访问新闻详情页的请求;
  • 电商平台在大促期间,如“618”、“双十一”等。

如果这些数据在缓存中的过期时间设置得过于集中,或者缓存服务器突然宕机,那么在数据过期或缓存失效的瞬间,大量请求会直接访问数据库,导致数据库负载剧增,可能引发雪崩。

1.4解决方案

1.4.1方案一设置redis的某些key永不过期
#简单地使用 SET 命令来添加这个key,不需要调用任何设置过期时间的命令
SET product:500100 "{\"name\":\"ProductName\", \"price\":99.99, \"status\":\"上架中\"}"

值得注意的是,可能因为Redis的内存限制或其他原因(如使用了Redis的LRU淘汰策略)而被删除。因此,在设计系统时,除了考虑key的过期时间外,还需要考虑Redis的内存管理和淘汰策略。

1.4.2搭建高可用redis集群+持久化

集群模式

  • 主从模式(Master-Slave)
  • 哨兵模式 (Sentinel)
  • 分片模式 (Cluster)

2.缓存穿透

2.1什么是缓存穿透

查询一条根本没有的记录。它既不存在于redis中,也不存在数据库中。一般我们的查询顺序是先查redis、再查数据库。每次请求最终都会访问到数据库。造成数据库访问压力。这种现象叫缓存穿透。

2.2产生原因

  • 查询记录在redis和数据库均不存在
  • 恶意攻击

2.3解决方案

2.3.1方案一使用布隆过滤器

流程说明

  • Web端发起请求到App服务应用
  • 经过布隆过滤器判断key是否存在
  • 若不存在,直接返回null
  • 若key存在或者被误判(下文详述),查询redis
  • 若redis中有结果,直接返回
  • 未查到,则查询数据库返回结果
  • 数据库查到结果,返回结果。并将对应key回写redis
  • 未查到,则返回null
2.3.2方案二缓存空对象或者默认值

针对要查询的数据,需求层面沟通,可以在Redis里存一个缺省值(比如,零、负数、defaultNull等)。

流程说明:

  • 先去redis查键user:xxxxxx没有,再去mysql查没有获得 ,这就发生了一次穿透现象;
  • 第一次来查询user:xxxxxx,redis和mysql都没有,返回null给调用者;
  • 将 user:xxxxxx,defaultNull 回写redis;
  • 第二次查user:xxxxxx,此时redis就有值了。

可以直接从Redis中读取default缺省值返回给业务应用程序,避免了把大量请求发送给mysql处理,增加mysql压力。

但是,此方法只能解决key相同的情况,但是无法应对黑客恶意攻击。

3.缓存击穿

3.1什么是缓存击穿

热点key突然失效了,大量请求暴击数据库。关键词:大量请求、同一个热点key、正好失效。

3.2发生场景

  • 某个key以到过期时间,但是还是被访问到
  • key被删除,但是被访问到

3.3解决方案

3.3.1方案一差异失效时间

差异失效时间,对于访问频繁的热点key,不设置过期时间。

3.3.2方案二采用互斥锁,双检加锁策略更新

双检锁通常用于解决懒加载中的并发问题,即只有当数据在缓存中不存在时,才进行数据库查询,并且确保只有一个线程进行数据库查询。

  1. 当大量请求进来,多个线程同时去查询数据库获取同一条数据;
  2. 在第一个查询数据的请求上使用一个互斥锁来锁定;
  3. 其他的线程走到这一步拿不到锁排队等着;
  4. 第一个线程查询到了数据,然后做缓存;
  5. 后面的线程进来发现已经有缓存了,就直接走缓存。

注意: 在真实的生产环境中,考虑到缓存一致性和数据一致性的问题,有些情况下可能还需要使用更复杂的并发控制机制。

4.缓存预热

缓存预热是一种缓存优化技术,其核心思想是在系统上线或服务重启之前,提前将相关的缓存数据加载到缓存系统中。这样做的目的是为了避免在实际请求到达时进行缓存项的加载,从而减少了响应时间,提升了系统的性能。

4.1缓存预热实现方案

4.1.1方案一批量预加载数据

缓存预热是一种缓存优化技术,其核心思想是在系统上线或服务重启之前,提前将相关的缓存数据加载到缓存系统中。这样做的目的是为了避免在实际请求到达时进行缓存项的加载,从而减少了响应时间,提升了系统的性能。

public void preloadCache() {
    List<String> keys = ... // 获取需要预热的缓存键列表
    for (String key : keys) {
        Object value = dao.get(key); // 从数据库中获取数据
        if (value != null) {
            redis.set(key, serialize(value), cacheExpiration); // 将数据存入缓存
        }
    }
}
4.1.2异步预热

如果预热的数据量很大,或者预热过程比较耗时,可以考虑使用异步任务来执行预热操作。这样可以避免阻塞系统启动,并且可以利用系统的空闲时间来预热缓存。

@PostConstruct
public void startPreloadCacheAsync() {
    CompletableFuture.runAsync(() -> preloadCache());
}
4.1.3定时任务预热

对于某些周期性变化的数据,可以用定时任务来定期预热缓存。例如,对于每天更新的数据,可以在每天的某个固定时间执行预热操作。

@Scheduled(fixedRate = 24 * 60 * 60 * 1000) // 每天执行一次
public void schedulePreloadCache() {
    preloadCache();
}
4.1.4使用SpringBoot InitializingBean接口实现
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

@Component
public class CachePreloader implements InitializingBean {

    @Override
    public void afterPropertiesSet() throws Exception {
        // 获取需要预热的缓存键列表
        List<String> keysToPreload = ...; 
        for (String key : keysToPreload) {
           // 从数据源获取数据
            Object value = dbService.get(key); 
            if (value != null) {
                // 将数据放入缓存
                cacheService.put(key, value);
            }
        }
    }
}

5.总结

缓存问题产生原因解决方案
缓存雪崩大量缓存失效,导致数据库过载1. 分散缓存失效时间
2. 多级缓存
3. 缓存高可用
4. 服务降级限流
缓存穿透查询不存在的数据,导致数据库过载1. 布隆过滤器
2. 空值缓存
缓存击穿热点数据失效,导致数据库过载1. 热点数据永不过期
2. 使用互斥锁
  • 26
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值