Redis 内存优化与性能调优:编码转换、内存碎片与慢查询的定位链路

Redis 内存优化与性能调优:编码转换、内存碎片与慢查询的定位链路

1. 从一次线上告警说起

假设你维护一个订单系统,某天凌晨收到告警:Redis 实例内存使用率 92%,同时接口平均响应时间从 8ms 涨到了 60ms。值班同事的第一反应通常是下面几种:

  • 把 maxmemory 调大,让实例先"撑过去";
  • 重启实例,重置 used_memory;
  • 怀疑是流量上涨,直接扩机器。

这些动作可能在短时间内让监控恢复绿线,但一两周后同样的问题会再次出现。原因在于,它们都跳过了最关键的判断:内存到底被谁占用了,是真实业务数据增长,还是编码方式不合理、碎片率过高、过期 Key 没被及时清理?慢又是哪一种慢,是单条命令本身耗时,还是排队、网络、主从同步造成的?

本文不打算把 Redis 调优讲成一份参数清单,而是先给出一条可以复述的定位链路。你可以把它理解成一次体检:先看整体内存形状,再判断数据结构是否用了低效编码,然后检查内存碎片是否过高,最后用慢查询日志锁定具体命令。整条链路串起来,才是一个可复用的工程方法。

为了便于后续展开,先记住一个最小模型:

text 复制代码
客户端命令
   |
   v
命令执行线程 --> 读写 Redis 对象 --> 编码可能发生转换
   |                                  |
   |                                  v
   |                            内存分配 / 释放
   |                                  |
   v                                  v
慢查询日志 <-- 耗时超阈值        内存碎片率变化
   |                                  |
   v                                  v
定位具体命令                     触发淘汰策略

2. 先建立整体框架:一次写入会经过哪些环节

Redis 的内存问题,很少是单一原因导致的。它更像一个链条:一次写入命令进入后,先执行命令逻辑,然后决定用哪种内部结构保存数据;随着元素增多,编码可能从紧凑结构转换为通用结构,内存占用随之上升;元素删除或过期后,释放的内存不一定能立刻被复用,于是产生碎片;当内存达到上限,淘汰策略开始工作;如果某条命令因为数据量过大而变慢,它就会被 slowlog 记录下来。

把整体拆成四部分更容易理解:

  1. 对象与编码层:Redis 为了兼顾内存和性能,对同一种逻辑类型提供了多种底层编码,例如列表在小数据量时用紧凑列表,变大后转为快速链表。
  2. 内存分配与回收层:内存由分配器管理,频繁增删会造成碎片,碎片率过高会浪费物理内存。
  3. 淘汰与过期层 :达到 maxmemory 后触发淘汰,过期 Key 通过惰性删除和定期删除结合清理。
  4. 可观测层 :INFO、MEMORY USAGE、SLOWLOG、LATENCY 等命令帮助我们把现象对应到原因。

这四个部分并非独立存在。编码转换会直接影响内存占用和碎片产生,碎片率又会影响淘汰触发时机,而淘汰和过期清理如果执行不当,又可能造成延迟尖刺并进入慢查询日志。理解它们之间的连接,比单独记住某个参数更重要。

3. 编码转换:为什么同样 100 个元素,内存差好几倍

3.1 一句话模型

可以先把它理解成:Redis 对每种数据类型都准备了两套甚至多套"收纳盒",小数据用紧凑盒,省内存;大数据用性能盒,操作更快。超过阈值时,Redis 自动把数据从紧凑盒搬到性能盒,这个过程叫编码转换。

3.2 触发条件与常见编码

以常见的列表和哈希为例:

数据类型 小数据编码 大数据编码 常见转换触发条件
List listpack(早期为 ziplist) quicklist 元素个数或单个元素长度超过阈值
Hash listpack(早期为 ziplist) hashtable 字段数或字段值长度超过阈值
Set intset hashtable 元素非整数或数量超阈值
ZSet listpack(早期为 ziplist) skiplist 元素数或成员长度超阈值

阈值由配置项控制,例如 hash-max-listpack-entries、hash-max-listpack-value、list-max-listpack-size、set-max-intset-entries、zset-max-listpack-entries。不同 Redis 版本默认值和名称略有差异,生产环境必须以当前版本 redis.conf 为准。

3.3 一个可复现的实验

下面用 redis-cli 做一个最小实验,观察哈希从紧凑编码转为通用编码后内存的变化。前置条件是本地或测试环境有一个可执行 redis-cli 的 Redis 实例,版本为 7.x。

bash 复制代码
# 步骤一:写入少量字段,查看编码和内存
redis-cli DEL demo:hash
redis-cli HSET demo:hash f1 v1 f2 v2
redis-cli OBJECT ENCODING demo:hash
redis-cli MEMORY USAGE demo:hash

# 预期输出(示例)
# listpack
# (integer) 96

# 步骤二:写入大量字段,再次查看
for i in $(seq 1 500); do
  redis-cli HSET demo:hash "field_$i" "value_$i" > /dev/null
done
redis-cli OBJECT ENCODING demo:hash
redis-cli MEMORY USAGE demo:hash

# 预期输出(示例)
# hashtable
# (integer) 32768

关键步骤解释:OBJECT ENCODING 用来查看当前编码,MEMORY USAGE 用来查看单个 Key 占用内存。第一次输出 listpack,说明数据量小、仍然使用紧凑结构;第二次输出 hashtable,说明已经发生转换。内存从几十字节涨到几万字节,不完全是数据本身变多,还包括哈希表结构、指针和预分配带来的额外开销。

适用场景:这个实验适合在测试环境验证业务 Key 的编码是否符合预期,尤其适合字段数量接近阈值的场景。容易改错的地方是:直接用 MEMORY USAGE 比较不同版本可能得到不同结果,因为采样方式和版本实现会变化;另外,转换是单向的,一旦变成 hashtable,即使随后删掉大部分字段,编码通常也不会自动退回 listpack。

3.4 设计取舍

紧凑编码省内存,但每次修改都可能涉及整块内存搬移,时间复杂度更接近 O(N);通用编码操作快,但内存开销大。Redis 用阈值来平衡这两者。工程上真正要做的,是判断你的业务数据是否长期停留在"接近阈值但未超过"的位置。如果一个 Hash 有 400 个字段,恰好没触发 500 的默认阈值,但字段值较长,内存已经不小,这时可以考虑拆分成多个 Key,或者显式调整阈值并做压测。

4. 内存碎片:used_memory 和 RSS 为什么会差那么多

4.1 碎片是怎么产生的

内存碎片可以理解成一间仓库里堆满了大小不一的箱子,很多小空隙没法再放进新的大箱子。Redis 也类似:不断创建和删除不同大小的对象时,分配器释放的内存块可能无法被后续请求复用,于是操作系统看到的进程 RSS 远大于 Redis 自己统计的 used_memory。

判断碎片程度主要看两个指标:

  • used_memory:Redis 分配器统计的已用内存;
  • used_memory_rss:操作系统视角的常驻内存;
  • 碎片率近似为 used_memory_rss / used_memory。

通常碎片率在 1.0 到 1.5 之间可以接受;持续高于 1.5 且业务有明显增删波动时,就需要关注。

4.2 一次诊断流程示例

下面是一个完整的诊断流程,输入是一台内存持续高于预期的 Redis 实例,步骤是读取信息、计算碎片率、查看分配器状态,结果用于决定是否开启主动碎片整理。

bash 复制代码
# 步骤一:读取内存相关指标
redis-cli INFO memory | grep -E "used_memory:|used_memory_rss:|mem_fragmentation_ratio|mem_allocator"

# 预期输出(示例)
# used_memory:2147483648
# used_memory_rss:3758096384
# mem_fragmentation_ratio:1.75
# mem_allocator:jemalloc-5.2.1

# 步骤二:查看碎片整理是否可用及当前状态
redis-cli CONFIG GET activedefrag
redis-cli MEMORY STATS | grep -E "fragmentation|allocator"

# 步骤三:评估后开启主动碎片整理(生产需谨慎,建议在低峰期验证)
redis-cli CONFIG SET activedefrag yes
redis-cli CONFIG SET active-defrag-ignore-bytes 100mb
redis-cli CONFIG SET active-defrag-threshold-lower 10

关键步骤解释:第一步算出碎片率约 1.75,说明 RSS 比已用内存高出很多;第二步确认当前没有开启主动碎片整理;第三步调整参数让 Redis 在碎片超过一定比例时后台整理。active-defrag-ignore-bytes 表示碎片浪费低于该值时忽略,active-defrag-threshold-lower 表示碎片率超过该百分比才开始整理。

可观察结果:整理期间 used_memory_rss 会逐步下降,mem_fragmentation_ratio 回落。边界和常见错误:主动碎片整理会消耗 CPU,主线程和子线程都可能受影响,不建议在高负载实例上直接大范围开启;另外,重启实例也能消除碎片,但会造成缓存雪崩风险,需要配合预热方案。

4.3 碎片与淘汰的关系

这里最容易误解的是:碎片率高不等于淘汰会提前触发。淘汰判断基于 maxmemory 和 used_memory,而不是 RSS。也就是说,碎片可能让进程被操作系统 OOM Kill,却不触发 Redis 自身的淘汰。生产上如果只盯着碎片率却不看操作系统内存,会出现"Redis 自己觉得还有空间,系统已经把进程杀掉"的情况。因此内存告警应该同时覆盖 used_memory、used_memory_rss 和操作系统剩余内存。

5. 内存淘汰:达到上限后,谁先被牺牲

当 used_memory 超过 maxmemory,Redis 会按照 maxmemory-policy 的策略淘汰 Key。常见策略如下:

策略 淘汰范围 是否可能淘汰未过期 Key 适用场景
noeviction 不淘汰,写入报错 不淘汰 不允许丢数据的场景
allkeys-lru 所有 Key 会 纯缓存,允许按最近使用淘汰
allkeys-lfu 所有 Key 会 热点差异明显,希望按频率淘汰
volatile-lru 设置了过期时间的 Key 不会 混合存储,仅缓存部分可淘汰
volatile-lfu 设置了过期时间的 Key 不会 有过期时间的缓存,热点明显
allkeys-random 所有 Key 会 对淘汰顺序不敏感
volatile-random 设置了过期时间的 Key 不会 简单缓存场景
volatile-ttl 设置了过期时间的 Key 不会 希望优先淘汰快过期的 Key

5.1 淘汰不是精确算法

LRU 和 LFU 在 Redis 中都是近似实现。Redis 不会维护完整的访问链表,而是通过采样若干个 Key,从中选择淘汰对象。采样数量由 maxmemory-samples 控制,默认值通常是 5,调大更接近精确但更耗 CPU。

5.2 一次淘汰行为的观察

下面在测试环境复现一次淘汰,前置条件是把实例限制到很小的 maxmemory,并设置策略为 allkeys-lru。

bash 复制代码
# 步骤一:设置小内存和淘汰策略(仅测试环境)
redis-cli CONFIG SET maxmemory 20mb
redis-cli CONFIG SET maxmemory-policy allkeys-lru

# 步骤二:持续写入,直到触发淘汰
for i in $(seq 1 20000); do
  redis-cli SET "key:$i" "value-$i" > /dev/null
done

# 步骤三:查看淘汰统计和内存
redis-cli INFO stats | grep evicted_keys
redis-cli INFO memory | grep used_memory:

# 预期输出(示例)
# evicted_keys:5421
# used_memory:20961480

关键步骤解释:evicted_keys 持续增长说明淘汰已经发生,used_memory 稳定在 maxmemory 附近。适用场景:用于验证策略是否生效、观察淘汰速度。容易改错的地方:如果策略设为 volatile-*,但 Key 没有设置过期时间,那么淘汰集合为空,写入会直接报 OOM 错误,而不是淘汰数据。

5.3 淘汰与过期的关系

过期 Key 的清理有两种方式:惰性删除在访问时判断是否过期,定期删除由后台周期性抽样清理。淘汰则是在内存达到上限时被动触发。两者都可能造成延迟,但原因不同。如果大量 Key 设置了相同的过期时间,会在同一时刻集中过期,可能引起延迟尖刺;更稳妥的做法是在过期时间上加随机抖动。

6. 慢查询:把"变慢"拆成可定位的命令

6.1 slowlog 记录的是什么

Redis 的慢查询日志记录的是命令执行本身的耗时,不包括网络往返和排队时间。相关配置有两个:slowlog-log-slower-than 表示超过多少微秒记录,slowlog-max-len 表示最多保留多少条。查看命令为 SLOWLOG GET、SLOWLOG RESET、SLOWLOG LEN。

命令 作用 注意事项
SLOWLOG GET n 查看最近 n 条慢查询 返回包含命令参数,注意敏感信息
SLOWLOG LEN 查看当前慢查询条数 长期增长说明持续有慢命令
SLOWLOG RESET 清空慢查询日志 排障前先记录再清空
LATENCY RESET 清空延迟事件 用于配合延迟监控
LATENCY HISTORY 查看延迟历史 可定位延迟尖刺类型

6.2 一次完整的慢查询定位

下面的流程模拟从发现变慢到定位具体命令,输入是一个响应时间上涨的实例,步骤是先看慢查询,再分析命令涉及的数据规模。

bash 复制代码
# 步骤一:配置慢查询阈值,例如超过 10ms 记录(10000 微秒)
redis-cli CONFIG SET slowlog-log-slower-than 10000
redis-cli CONFIG SET slowlog-max-len 256

# 步骤二:查看慢查询
redis-cli SLOWLOG GET 5

# 预期输出(示例,节选)
# 1) 1) (integer) 14
#    2) (integer) 1712345678
#    3) (integer) 45231
#    4) 1) "HGETALL"
#       2) "big:hash:order"
#    5) "127.0.0.1:52341"
#    6) ""

# 步骤三:确认该 Key 的规模和编码
redis-cli HLEN big:hash:order
redis-cli OBJECT ENCODING big:hash:order
redis-cli MEMORY USAGE big:hash:order

# 预期输出(示例)
# (integer) 500000
# hashtable
# (integer) 48234496

关键步骤解释:第一条慢查询显示 HGETALL 耗时约 45ms,Key 名为 big:hash:order;第三步确认它有 50 万个字段、占用约 46MB,属于典型大 Key。HGETALL 会一次性返回所有字段,数据量大时不仅慢,还会占用网络带宽和客户端内存。

处理方向:业务上改用 HGET 按需读取、HSCAN 分批遍历,或者把大 Hash 拆分成多个小 Key。适用场景:所有出现响应时间尖刺的实例。边界:SLOWLOG 不记录网络延迟和执行前的排队时间,如果慢查询日志为空但接口仍然慢,需要排查连接池、网络、主从同步或客户端序列化。

6.3 大 Key 的排查代码

下面给出一个用 Java 客户端扫描大 Key 的完整示例。前置环境是 JDK 8 及以上、Maven 项目引入 Jedis 客户端、本地可连接 Redis。目标是按类型抽样估算 Key 规模,而不是在生产环境全量遍历。

java 复制代码
import redis.clients.jedis.Jedis;
import redis.clients.jedis.ScanParams;
import redis.clients.jedis.ScanResult;

import java.util.List;

public class BigKeyScanner {

    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        try {
            String cursor = ScanParams.SCAN_POINTER_START;
            ScanParams params = new ScanParams().count(100);
            long checked = 0;
            long bigKeyCount = 0;

            do {
                ScanResult<String> result = jedis.scan(cursor, params);
                List<String> keys = result.getResult();
                for (String key : keys) {
                    checked++;
                    String type = jedis.type(key);
                    long size = estimateSize(jedis, key, type);
                    if (size > 10_000) {
                        bigKeyCount++;
                        System.out.println("[BIG KEY] key=" + key + ", type=" + type + ", size=" + size);
                    }
                }
                cursor = result.getCursor();
            } while (!ScanParams.SCAN_POINTER_START.equals(cursor));

            System.out.println("checked=" + checked + ", bigKeyCount=" + bigKeyCount);
        } catch (Exception e) {
            System.err.println("scan failed: " + e.getMessage());
        } finally {
            jedis.close();
        }
    }

    private static long estimateSize(Jedis jedis, String key, String type) {
        switch (type) {
            case "string":
                return jedis.strlen(key);
            case "hash":
                return jedis.hlen(key);
            case "list":
                return jedis.llen(key);
            case "set":
                return jedis.scard(key);
            case "zset":
                return jedis.zcard(key);
            default:
                return 0;
        }
    }
}

关键步骤解释:使用 SCAN 而不是 KEYS,避免阻塞主线程;每次只取 100 个 Key,逐个按类型统计元素数量,超过 10000 就打印。预期输出会列出大 Key 及其类型和规模,最后打印扫描总数和大 Key 数量。适用场景:测试环境或低峰期对指定实例做抽样检查。容易改错的地方:SCAN 只保证遍历期间存在的元素最终被返回,不保证不重复,所以统计是近似值;生产环境不要用 KEYS * 替代它。

7. 性能调优:从参数到业务结构

7.1 调优的优先级

Redis 性能调优不是先改配置,而是先确认瓶颈位置。可以按照下面的顺序判断:

  1. 命令层面:是否存在 O(N) 大命令、大 Key、频繁全量遍历;
  2. 连接层面:客户端连接池是否过小或过大,是否存在连接泄漏;
  3. 内存层面:编码是否合理、碎片是否过高、淘汰是否频繁;
  4. 持久化层面:是否开启了 AOF 频繁刷盘或 RDB 频繁快照;
  5. 部署层面:主从、哨兵、Cluster 是否存在跨机房高延迟或频繁故障转移。

7.2 管道与批量操作

如果业务需要连续写入大量小 Key,逐条命令会产生大量网络往返。可以使用管道批量发送,但要控制每次批量的大小,避免单次响应过大。下面是一个完整示例,前置环境与上一节相同。

java 复制代码
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;

public class PipelineDemo {

    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        try {
            Pipeline pipeline = jedis.pipelined();
            for (int i = 0; i < 1000; i++) {
                pipeline.set("batch:key:" + i, "value-" + i);
            }
            pipeline.sync();
            System.out.println("pipeline done, sample=" + jedis.get("batch:key:999"));
        } catch (Exception e) {
            System.err.println("pipeline failed: " + e.getMessage());
        } finally {
            jedis.close();
        }
    }
}

关键步骤解释:pipelined() 返回管道对象,连续调用 set 只是把命令放入本地缓冲,sync() 才真正发送并读取响应。预期输出会打印 value-999。适用场景:批量写入、批量删除等不需要逐条读取结果的场景。边界:管道只节省网络往返,不减少服务端命令执行成本;单次管道过大可能导致客户端内存上涨或服务端输出缓冲超限。

7.3 用 Lua 减少往返的边界

有些场景需要在服务端原子执行多条命令,可以用 Lua 脚本。但脚本本身如果遍历大集合,同样会阻塞主线程。下面是一个简单示例,目标是原子地读取并更新计数。

java 复制代码
import redis.clients.jedis.Jedis;

public class LuaCounter {

    private static final String SCRIPT =
            "local current = redis.call('GET', KEYS[1]) " +
            "if current == false then current = 0 end " +
            "current = tonumber(current) + tonumber(ARGV[1]) " +
            "redis.call('SET', KEYS[1], current) " +
            "return current";

    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        try {
            Object result = jedis.eval(SCRIPT, 1, "counter:order", "5");
            System.out.println("counter=" + result);
        } catch (Exception e) {
            System.err.println("lua failed: " + e.getMessage());
        } finally {
            jedis.close();
        }
    }
}

关键步骤解释:脚本在服务端原子执行,避免客户端读取和写回之间被其他命令插入。预期输出为 counter=5,重复执行会递增。适用场景:计数、限流、简单的原子读改写。边界:脚本执行时间过长会阻塞其他命令,lua-time-limit 只控制脚本超时后的行为,不会自动杀死脚本;不要在脚本里做大范围遍历。

8. 常见误区

  • 误区一:used_memory 低就说明内存没问题。 操作系统看的是 RSS,碎片过高时可能被 OOM Kill。
  • 误区二:重启能解决内存问题。 重启只能清空碎片和过期数据,如果编码和 Key 结构不合理,问题会很快复发,还可能造成缓存雪崩。
  • 误区三:设置 volatile-lru 就一定能淘汰。 如果 Key 都没有过期时间,淘汰集合为空,写入会报错。
  • 误区四:slowlog 没记录就说明没有慢查询。 slowlog 只记录命令执行耗时,不覆盖网络、排队和客户端处理。
  • 误区五:编码转换后删除元素就能恢复内存。 大多数编码转换是单向的,删除元素不一定让编码退回紧凑结构。
  • 误区六:KEYS 用于排查大 Key。 KEYS 会阻塞主线程,生产环境应使用 SCAN。

9. 生产实践建议

  • 给所有缓存 Key 设置带随机抖动的过期时间,避免集中过期。
  • 对 Hash、List、Set、ZSet 的业务规模设上限,接近编码阈值时提前拆分或压测。
  • 监控同时覆盖 used_memory、used_memory_rss、evicted_keys、expired_keys 和 slowlog 长度。
  • 大 Key 和高频 Key 定期抽检,写入链路中避免一次性返回大集合。
  • 主动碎片整理只在评估 CPU 余量后开启,优先在低峰期验证。
  • 淘汰策略要与业务数据性质匹配:纯缓存用 allkeys-lru 或 allkeys-lfu,混合存储用 volatile-lru 并确保有过期时间。

10. 排障清单与面试复盘

10.1 排障清单

text 复制代码
收到 Redis 内存或延迟告警
  |
  v
1. INFO memory:看 used_memory、used_memory_rss、碎片率
  |
  v
2. INFO stats:看 evicted_keys、expired_keys 是否异常增长
  |
  v
3. SLOWLOG GET:确认是否有慢命令,记录参数和时间
  |
  v
4. 对慢命令涉及的 Key 执行 OBJECT ENCODING、MEMORY USAGE、HLEN/LLEN 等
  |
  v
5. 判断是编码不合理、大 Key、碎片过高还是淘汰策略不匹配
  |
  v
6. 制定改动:拆分 Key、调整阈值、开启整理、更换淘汰策略或优化业务访问
  |
  v
7. 低峰期验证并持续观察

10.2 面试与复盘问题

  • 为什么小 Hash 用 listpack 而大 Hash 用 hashtable,转换后为什么不能自动转回?
  • used_memory_rss 远大于 used_memory 时,可能有哪些原因,如何验证?
  • allkeys-lru 和 volatile-lru 在 Key 都没有过期时间时分别会发生什么?
  • slowlog 记录不到某个慢请求,可能是哪些环节造成的?
  • 大 Key 的常见识别方式和拆分思路有哪些?
  • 为什么管道不一定能提升服务端处理能力?

11. 总结

回到开头的告警场景,正确的处理顺序不是先调参数,而是先建立定位链路:用 INFO memory 看内存形状,用 OBJECT ENCODING 和 MEMORY USAGE 判断编码和大 Key,用碎片率判断内存是否被浪费,用 SLOWLOG 找到具体慢命令,最后再决定是拆分 Key、调整编码阈值、开启碎片整理还是更换淘汰策略。

现象 优先排查方向 典型动作
内存高但 used_memory 不高 碎片率 检查 RSS,评估主动碎片整理
内存高且 evicted_keys 增长 淘汰策略与数据量 调整策略或扩容,检查过期时间
延迟尖刺且 slowlog 有记录 大 Key 或 O(N) 命令 拆分 Key,改用分批命令
延迟尖刺但 slowlog 为空 网络、连接池、持久化 检查客户端、AOF、RDB、主从延迟
编码从紧凑变为通用 业务数据规模 控制元素数量,必要时拆分

把这张表当作日常排障的入口,比记住一堆参数更有用。

12. 参考资料

相关推荐
我不会起名字3224 小时前
Redis 入门(三):Spring Boot 整合 Redis,RedisTemplate 实战与序列化乱码
redis·spring·工具类·jedis·lettuce
神秘的猪头4 小时前
Redis 数据结构实战指南:从缓存、点赞、排行榜到消息队列
redis
新鲜势力呀7 小时前
PHP 消息队列实战:从同步接口阻塞到 RabbitMQ + Redis队列 + 异步任务架构完整优化方案
redis·rabbitmq·php
for_ever_love__7 小时前
Redis 持久化讲透:RDB、AOF 与混合持久化怎么选
java·数据库·redis·持久化·aof·区别·rdb
拾贰_C9 小时前
【English | conversation 】call | 抖音AI短句情景:打电话---come over
数据库·redis·缓存
ShineWinsu12 小时前
对于Redis:缓存的解析
java·redis·缓存·缓存穿透·缓存击穿·缓存雪崩·缓存预热
害人终害己12 小时前
redis修改密码的地方在哪里
数据库·redis·缓存
我不会起名字32213 小时前
Redis 入门(一):Redis 是什么,为什么后端都离不开它
linux·redis·docker·安装·cli
for_ever_love__13 小时前
缓存穿透、击穿、雪崩:三个经典问题与完整解决方案
java·数据库·redis·缓存·哈希算法·布隆过滤器·雪崩