Skip to content

Redis 内存管理

Redis 是一个内存数据库,内存管理是 Redis 运维的核心任务之一。有效的内存管理可以提高 Redis 性能、降低成本、避免内存溢出等问题。本文档将详细介绍 Redis 内存管理的各个方面,包括内存结构、内存使用统计、内存配置优化、内存淘汰策略、内存碎片管理和大键管理等。

Redis 内存结构

1. 内存使用分类

Redis 内存主要分为以下几个部分:

  • 数据内存:存储 Redis 数据的内存,包括键值对、数据结构等
  • 进程内存:Redis 进程本身占用的内存,包括代码段、数据段、堆内存等
  • 缓冲区内存:包括客户端缓冲区、复制缓冲区、AOF 缓冲区等
  • 内存碎片:由于内存分配和释放导致的内存碎片

2. 数据内存

数据内存是 Redis 内存的主要组成部分,用于存储 Redis 数据。Redis 支持多种数据结构,每种数据结构的内存使用方式不同:

  • 字符串(String):简单的键值对存储
  • 哈希(Hash):字段值对的集合
  • 列表(List):有序的字符串列表
  • 集合(Set):无序的字符串集合
  • 有序集合(Sorted Set):有序的字符串集合,每个元素带有分数
  • 流(Stream):日志型数据结构
  • 位图(Bitmap):二进制位操作
  • 基数统计(HyperLogLog):基数估算
  • 地理空间(Geo):地理位置数据

3. 缓冲区内存

缓冲区内存主要包括以下几种:

  • 客户端缓冲区:每个客户端连接都有输入缓冲区和输出缓冲区
  • 复制缓冲区:主从复制时使用的缓冲区,包括复制积压缓冲区和复制客户端缓冲区
  • AOF 缓冲区:AOF 持久化时使用的缓冲区,包括 AOF 写入缓冲区和 AOF 重写缓冲区

4. 内存碎片

内存碎片是由于内存分配和释放导致的内存不连续现象。Redis 会产生内存碎片的原因包括:

  • 频繁的键值对增删:导致内存分配和释放频繁
  • 不同大小的键值对:导致内存分配的块大小不一致
  • 内存分配器的特性:不同的内存分配器(如 jemalloc、glibc)产生的内存碎片情况不同

内存使用统计

1. INFO memory 命令

使用 INFO memory 命令可以获取 Redis 内存使用的详细统计信息:

bash
redis-cli info memory

主要输出项包括:

  • used_memory:Redis 分配器分配的内存总量
  • used_memory_rss:Redis 进程占用的物理内存总量
  • used_memory_peak:Redis 内存使用的峰值
  • used_memory_lua:Lua 脚本引擎占用的内存
  • mem_fragmentation_ratio:内存碎片率,used_memory_rss / used_memory
  • mem_allocator:Redis 使用的内存分配器

2. 内存使用分析

2.1 内存碎片率分析

bash
# 查看内存碎片率
redis-cli info memory | grep mem_fragmentation_ratio

# 理想碎片率:1.0-1.5
# 高碎片率:>1.5
# 低碎片率:<1.0(可能存在内存交换)

2.2 数据内存分析

bash
# 查看数据内存使用情况
redis-cli info memory | grep used_memory_dataset

2.3 客户端缓冲区分析

bash
# 查看客户端连接数
redis-cli info clients | grep connected_clients

# 查看客户端输入缓冲区最大使用量
redis-cli info clients | grep instantaneous_input_kbps

# 查看客户端输出缓冲区最大使用量
redis-cli info clients | grep instantaneous_output_kbps

3. 内存使用监控

3.1 实时监控

bash
# 每秒监控一次内存使用情况
watch -n 1 'redis-cli info memory | grep -E "used_memory|used_memory_rss|mem_fragmentation_ratio"'

3.2 持久化监控

使用 Prometheus + Grafana 等监控工具进行持久化监控,设置内存使用告警阈值。

内存配置优化

1. 最大内存配置

txt
# redis.conf
# 设置 Redis 最大使用内存(字节)
maxmemory 1gb

# 或使用单位表示
maxmemory 1024mb
maxmemory 2gb

2. 内存分配器配置

txt
# redis.conf
# 设置内存分配器(默认 jemalloc)
# 可选值:jemalloc, glibc, tcmalloc
maxmemory-allocator jemalloc

3. 客户端缓冲区配置

txt
# redis.conf
# 设置客户端输出缓冲区硬限制
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60

# 设置客户端输入缓冲区限制
proto-max-bulk-len 512mb

# 设置客户端连接超时时间
timeout 300

4. 复制缓冲区配置

txt
# redis.conf
# 设置复制积压缓冲区大小
repl-backlog-size 1mb

# 设置复制积压缓冲区过期时间
repl-backlog-ttl 3600

5. AOF 缓冲区配置

txt
# redis.conf
# 设置 AOF 重写缓冲区大小(由系统自动管理)

# 设置 AOF 写入策略
appendfsync everysec

内存淘汰策略

1. 内存淘汰策略类型

Redis 支持多种内存淘汰策略,用于在内存达到上限时选择要删除的键:

  • volatile-lru:从设置了过期时间的键中,使用 LRU(最近最少使用)算法删除
  • allkeys-lru:从所有键中,使用 LRU 算法删除
  • volatile-lfu:从设置了过期时间的键中,使用 LFU(最不经常使用)算法删除
  • allkeys-lfu:从所有键中,使用 LFU 算法删除
  • volatile-random:从设置了过期时间的键中,随机删除
  • allkeys-random:从所有键中,随机删除
  • volatile-ttl:从设置了过期时间的键中,优先删除剩余 TTL 最小的键
  • noeviction:不删除键,返回错误(默认策略)

2. 淘汰策略配置

txt
# redis.conf
# 设置内存淘汰策略
maxmemory-policy allkeys-lru

3. 淘汰策略选择

  • volatile-lru:适合存在热点数据且大部分键设置了过期时间的场景
  • allkeys-lru:适合存在明显热点数据的场景
  • volatile-lfu:适合键访问频率变化较大的场景
  • allkeys-lfu:适合键访问频率差异较大的场景
  • volatile-random:适合没有明显热点数据的场景
  • allkeys-random:适合数据访问均匀的场景
  • volatile-ttl:适合需要根据 TTL 优先级删除键的场景
  • noeviction:适合需要确保数据不丢失的场景

4. 淘汰策略监控

bash
# 查看淘汰键的数量
redis-cli info stats | grep evicted_keys

# 查看淘汰策略
redis-cli config get maxmemory-policy

内存碎片管理

1. 内存碎片原因

  • 频繁的键值对增删:导致内存分配和释放频繁
  • 不同大小的键值对:导致内存分配的块大小不一致
  • 内存分配器的特性:不同的内存分配器产生的内存碎片情况不同

2. 内存碎片率计算

bash
# 计算内存碎片率
mem_fragmentation_ratio = used_memory_rss / used_memory
  • 理想碎片率:1.0-1.5
  • 高碎片率:>1.5
  • 低碎片率:<1.0(可能存在内存交换)

3. 内存碎片处理

3.1 重启 Redis 实例

重启 Redis 实例是最直接的内存碎片处理方法,但会导致服务中断。

3.2 内存重分配

Redis 4.0+ 支持自动内存碎片整理:

txt
# redis.conf
# 启用自动内存碎片整理
activedefrag yes

# 设置内存碎片整理的触发条件
active-defrag-ignore-bytes 100mb
active-defrag-threshold-lower 10
active-defrag-threshold-upper 100
active-defrag-cycle-min 25
active-defrag-cycle-max 75

参数说明:

  • activedefrag:是否启用自动内存碎片整理
  • active-defrag-ignore-bytes:内存碎片超过该值时才进行整理
  • active-defrag-threshold-lower:内存碎片率超过该值时开始整理
  • active-defrag-threshold-upper:内存碎片率超过该值时,尽最大努力整理
  • active-defrag-cycle-min:内存碎片整理的最小 CPU 占用百分比
  • active-defrag-cycle-max:内存碎片整理的最大 CPU 占用百分比

3.3 手动内存碎片整理

Redis 4.0+ 支持手动触发内存碎片整理:

bash
redis-cli memory defrag

4. 内存碎片预防

  • 合理规划键的生命周期:避免频繁的键值对增删
  • 使用合适的数据结构:选择内存效率高的数据结构
  • 合理设置内存分配器:不同的内存分配器产生的内存碎片情况不同
  • 定期监控内存碎片率:及时发现和处理内存碎片问题

大键管理

1. 大键定义

大键是指占用内存较大的键,具体定义因业务场景而异,一般包括:

  • 大字符串:单个字符串超过 10MB
  • 大哈希:哈希表中的字段数量超过 10,000
  • 大列表:列表中的元素数量超过 10,000
  • 大集合:集合中的元素数量超过 10,000
  • 大有序集合:有序集合中的元素数量超过 10,000

2. 大键危害

  • 内存使用不均:导致部分内存块过大,影响内存分配
  • 性能问题:大键的读写操作会阻塞 Redis 主线程
  • 持久化问题:大键会导致 RDB 和 AOF 持久化时间过长
  • 复制问题:大键会导致主从复制延迟增加
  • 删除问题:删除大键会阻塞 Redis 主线程

3. 大键发现

3.1 使用 redis-cli --bigkeys 命令

bash
redis-cli --bigkeys

该命令会扫描所有键,按数据类型统计最大的键。

3.2 使用 SCAN 命令结合 DEBUG OBJECT 命令

bash
#!/bin/bash

# 扫描所有键,找出大键
redis-cli scan 0 MATCH * COUNT 1000 | while read KEY; do
    # 获取键的内存使用情况
    MEMORY_USAGE=$(redis-cli memory usage $KEY)
    if [ $MEMORY_USAGE -gt 10485760 ]; then  # 10MB
        echo "Big key found: $KEY, Memory usage: $MEMORY_USAGE bytes"
    fi
done

3.3 使用第三方工具

  • redis-rdb-tools:分析 RDB 文件,找出大键
  • redis-memory-analyzer:实时分析 Redis 内存使用情况

4. 大键处理

4.1 拆分大键

  • 大哈希拆分:将大哈希拆分为多个小哈希
  • 大列表拆分:将大列表拆分为多个小列表
  • 大集合拆分:将大集合拆分为多个小集合
  • 大有序集合拆分:将大有序集合拆分为多个小有序集合

4.2 使用异步删除

Redis 4.0+ 支持异步删除大键:

bash
# 使用 UNLINK 命令异步删除键
redis-cli unlink big_key

# 或使用配置参数启用异步删除
redis-cli config set lazyfree-lazy-server-del yes
redis-cli del big_key  # 此时 DEL 命令会异步执行

4.3 合理设置过期时间

为大键设置合理的过期时间,让 Redis 自动删除过期的大键:

bash
# 设置键的过期时间为 1 天
redis-cli expire big_key 86400

4.4 定期清理大键

定期清理不再使用的大键,避免占用过多内存。

内存监控和告警

1. 监控指标

  • 内存使用量used_memoryused_memory_rssused_memory_peak
  • 内存碎片率mem_fragmentation_ratio
  • 淘汰键数量evicted_keys
  • 客户端连接数connected_clients
  • 复制积压缓冲区使用情况repl_backlog_activerepl_backlog_sizerepl_backlog_first_byte_offsetrepl_backlog_histlen

2. 告警阈值设置

  • 内存使用率:超过 80% 时告警
  • 内存碎片率:超过 1.5 或低于 1.0 时告警
  • 淘汰键数量:短时间内淘汰键数量急剧增加时告警
  • 客户端连接数:接近 maxclients 时告警

3. 监控工具

  • Prometheus + Grafana:常用的监控组合,支持自定义仪表盘和告警
  • Redis Exporter:用于采集 Redis 监控指标
  • Datadog:提供 Redis 监控集成
  • New Relic:提供 Redis 监控集成
  • Zabbix:支持 Redis 监控模板

4. 告警方式

  • 邮件告警:将告警发送到指定邮箱
  • 短信告警:将告警发送到指定手机号
  • 即时通讯工具告警:将告警发送到 Slack、Discord、企业微信、钉钉等即时通讯工具
  • 监控平台告警:在监控平台上显示告警

常见问题(FAQ)

Q1: 如何选择合适的内存淘汰策略?

A1: 选择内存淘汰策略需要考虑业务场景:

  1. 存在明显热点数据:使用 allkeys-lruallkeys-lfu
  2. 大部分键设置了过期时间:使用 volatile-lruvolatile-lfu
  3. 数据访问均匀:使用 allkeys-random
  4. 需要根据 TTL 优先级删除:使用 volatile-ttl
  5. 不允许数据丢失:使用 noeviction

Q2: 如何处理高内存碎片率问题?

A2: 处理高内存碎片率问题的方法:

  1. 启用自动内存碎片整理:Redis 4.0+ 支持自动内存碎片整理
  2. 手动触发内存碎片整理:使用 memory defrag 命令
  3. 重启 Redis 实例:最直接的方法,但会导致服务中断
  4. 优化内存使用:减少频繁的键值对增删,使用合适的数据结构

Q3: 如何发现和处理大键?

A3: 发现和处理大键的步骤:

  1. 发现大键:使用 redis-cli --bigkeys、SCAN 命令结合 DEBUG OBJECT 命令或第三方工具
  2. 分析大键:确定大键的类型、大小和使用频率
  3. 处理大键:拆分大键、使用异步删除、合理设置过期时间或定期清理

Q4: 如何优化 Redis 内存使用?

A4: 优化 Redis 内存使用的方法:

  1. 使用合适的数据结构:选择内存效率高的数据结构
  2. 合理设置过期时间:让 Redis 自动删除过期数据
  3. 压缩数据:对于字符串类型数据,可以考虑压缩存储
  4. 拆分大键:将大键拆分为多个小键
  5. 合理配置内存淘汰策略:根据业务场景选择合适的淘汰策略
  6. 定期清理不再使用的数据:避免占用过多内存

Q5: 如何监控 Redis 内存使用?

A5: 监控 Redis 内存使用的方法:

  1. 使用 INFO memory 命令:获取内存使用的详细统计信息
  2. 使用 redis-cli --bigkeys 命令:发现大键
  3. 使用监控工具:如 Prometheus + Grafana、Datadog、New Relic 等
  4. 设置告警:当内存使用率超过阈值时触发告警

Q6: 如何处理 Redis 内存溢出问题?

A6: 处理 Redis 内存溢出问题的方法:

  1. 增加 Redis 内存:调整 maxmemory 参数,增加 Redis 可用内存
  2. 优化内存使用:使用合适的数据结构,合理设置过期时间,拆分大键等
  3. 调整淘汰策略:选择合适的内存淘汰策略,及时删除不常用的数据
  4. 垂直扩展:升级 Redis 服务器的硬件配置
  5. 水平扩展:使用 Redis Cluster 或分片技术扩展 Redis 集群