【Redis】Redis性能优化Day14(2026年)

写在前面

Redis以高性能著称,但在实际生产环境中,不当的使用方式可能导致性能急剧下降。今天我们来系统学习Redis性能优化的方法,包括慢查询分析、内存优化、网络优化和大key治理。

文章目录

    • 写在前面
    • 一、慢查询分析
      • [1.1 什么是慢查询](#1.1 什么是慢查询)
      • [1.2 SLOWLOG配置](#1.2 SLOWLOG配置)
      • [1.3 查看慢查询日志](#1.3 查看慢查询日志)
      • [1.4 常见慢查询命令](#1.4 常见慢查询命令)
      • [1.5 慢查询优化案例](#1.5 慢查询优化案例)
    • 二、内存优化
      • [2.1 内存使用分析](#2.1 内存使用分析)
      • [2.2 内存碎片问题](#2.2 内存碎片问题)
      • [2.3 对象编码优化](#2.3 对象编码优化)
      • [2.4 内存优化技巧](#2.4 内存优化技巧)
    • 三、网络优化
      • [3.1 Pipeline管道](#3.1 Pipeline管道)
      • [3.2 Lua脚本优化](#3.2 Lua脚本优化)
      • [3.3 连接池优化](#3.3 连接池优化)
      • [3.4 网络配置优化](#3.4 网络配置优化)
    • 四、大key治理
      • [4.1 什么是大key](#4.1 什么是大key)
      • [4.2 大key的危害](#4.2 大key的危害)
      • [4.3 大key发现](#4.3 大key发现)
      • [4.4 大key删除](#4.4 大key删除)
      • [4.5 大key预防](#4.5 大key预防)
    • 五、踩坑提醒
      • [5.1 KEYS命令在生产环境禁用](#5.1 KEYS命令在生产环境禁用)
      • [5.2 避免使用SELECT切换数据库](#5.2 避免使用SELECT切换数据库)
      • [5.3 避免大量key同时过期](#5.3 避免大量key同时过期)
      • [5.4 避免使用阻塞命令](#5.4 避免使用阻塞命令)
      • [5.5 常见性能问题汇总](#5.5 常见性能问题汇总)
    • 六、性能监控
      • [6.1 关键监控指标](#6.1 关键监控指标)
      • [6.2 监控命令](#6.2 监控命令)
      • [6.3 监控工具](#6.3 监控工具)
    • 七、面试高频考点
      • [7.1 如何排查Redis性能问题?](#7.1 如何排查Redis性能问题?)
      • [7.2 Redis内存优化有哪些方法?](#7.2 Redis内存优化有哪些方法?)
      • [7.3 为什么KEYS命令在生产环境禁用?](#7.3 为什么KEYS命令在生产环境禁用?)
      • [7.4 如何处理大key?](#7.4 如何处理大key?)
    • 八、参考资料
    • 九、互动话题

一、慢查询分析

1.1 什么是慢查询

实际场景:某天运维收到告警,Redis响应时间飙升,需要排查是哪些命令导致的。

慢查询定义:执行时间超过指定阈值的命令。

1.2 SLOWLOG配置

redis 复制代码
# 查看慢查询配置
CONFIG GET slowlog-*

# 设置慢查询阈值(微秒)
CONFIG SET slowlog-log-slower-than 10000

# 设置慢查询日志最大条数
CONFIG SET slowlog-max-len 128

配置说明

配置项 说明 默认值
slowlog-log-slower-than 慢查询阈值(微秒) 10000
slowlog-max-len 慢查询日志最大条数 128

1.3 查看慢查询日志

redis 复制代码
# 查看慢查询日志
SLOWLOG GET

# 查看最近10条慢查询
SLOWLOG GET 10

# 查看慢查询日志条数
SLOWLOG LEN

# 清空慢查询日志
SLOWLOG RESET

慢查询日志格式

复制代码
1) 1) (integer) 1           # 日志ID
   2) (integer) 1703123456  # 执行时间戳
   3) (integer) 15000       # 执行耗时(微秒)
   4) 1) "KEYS"             # 执行的命令
      2) "user:*"
   5) "127.0.0.1:54321"     # 客户端地址
   6) "client-name"         # 客户端名称

1.4 常见慢查询命令

踩坑提醒:以下命令在生产环境慎用或禁用。

命令 时间复杂度 说明 替代方案
KEYS O(N) 遍历所有key SCAN
HGETALL O(N) 获取所有字段 HSCAN
SMEMBERS O(N) 获取所有成员 SSCAN
LRANGE O(N) 获取列表范围 分批获取
DEL O(N) 删除大key UNLINK
FLUSHALL O(N) 清空所有数据 慎用

1.5 慢查询优化案例

实际场景:KEYS命令导致Redis阻塞。

问题代码

redis 复制代码
# 危险:遍历所有key
KEYS user:*

优化方案

redis 复制代码
# 使用SCAN命令分批遍历
SCAN 0 MATCH user:* COUNT 100
# 返回:下一个游标和匹配的key列表

Java代码示例

java 复制代码
public List<String> scanKeys(String pattern) {
    List<String> keys = new ArrayList<>();
    ScanOptions options = ScanOptions.scanOptions()
        .match(pattern)
        .count(100)
        .build();
    
    Cursor<String> cursor = redisTemplate.scan(options);
    while (cursor.hasNext()) {
        keys.add(cursor.next());
    }
    return keys;
}

二、内存优化

2.1 内存使用分析

经验之谈:定期分析Redis内存使用情况,及时发现内存问题。

redis 复制代码
# 查看内存使用情况
INFO memory

# 关键指标:
# used_memory: 已使用内存
# used_memory_rss: 系统分配内存
# used_memory_peak: 内存使用峰值
# mem_fragmentation_ratio: 内存碎片率

内存相关指标

指标 说明 理想值
used_memory Redis分配的内存总量 -
used_memory_rss 操作系统分配给Redis的内存 -
mem_fragmentation_ratio 内存碎片率(rss/used) 1.0-1.5
used_memory_peak 内存使用峰值 -

2.2 内存碎片问题

踩坑提醒:内存碎片率过高会浪费内存,过低可能导致OOM。

内存碎片产生原因

  1. 频繁的内存分配和释放
  2. 数据大小不一致
  3. Redis的内存分配器(jemalloc)

内存碎片率分析

碎片率 说明 建议
< 1.0 Redis使用了swap 检查系统内存
1.0-1.5 正常 无需处理
> 1.5 碎片较多 考虑整理

内存碎片整理

redis 复制代码
# 开启自动碎片整理
CONFIG SET activedefrag yes

# 设置碎片整理阈值
CONFIG SET active-defrag-ignore-bytes 100mb
CONFIG SET active-defrag-threshold-lower 10

2.3 对象编码优化

面试高频考点:Redis会根据数据类型和大小自动选择编码方式,了解编码有助于优化内存。

数据类型编码表

数据类型 编码方式 条件 内存效率
String int 整数且范围在long内 最高
String embstr 字符串长度≤44字节
String raw 字符串长度>44字节
Hash ziplist 元素数≤512,值≤64字节
Hash hashtable 不满足ziplist条件
List quicklist 3.2+版本默认
Set intset 全是整数,元素数≤512
Set hashtable 不满足intset条件
ZSet skiplist 元素数≤128,值≤64字节
ZSet skiplist+dict 不满足条件

查看编码方式

redis 复制代码
# 查看key的编码方式
OBJECT ENCODING mykey

2.4 内存优化技巧

实际场景:存储100万个用户信息,如何优化内存占用?

优化方案对比

方案 内存占用 说明
String存储JSON 每个key都有元数据开销
Hash存储 多个字段共享一个key
Hash + ziplist 小对象使用压缩编码

优化示例

redis 复制代码
# 方案1:String存储(不推荐)
SET user:1 '{"id":1,"name":"张三","age":25}'
SET user:2 '{"id":2,"name":"李四","age":30}'

# 方案2:Hash存储(推荐)
HSET user:1 id 1 name "张三" age 25
HSET user:2 id 2 name "李四" age 30

# 方案3:压缩Hash(最优)
# 控制字段数量和大小,使用ziplist编码
HSET user:1 id 1 name "张三" age 25
# 确保元素数≤512,值长度≤64字节

内存优化建议

优化项 建议
Key长度 尽量短,使用缩写
数据结构 选择合适的编码方式
过期策略 设置合理的过期时间
数据压缩 使用压缩编码
大key拆分 避免存储大对象

三、网络优化

3.1 Pipeline管道

实际场景:需要批量插入10000条数据,逐条执行太慢,如何优化?

Pipeline原理

复制代码
传统方式:
Client → Redis → Client → Redis → Client → Redis
(每次请求都需要网络往返)

Pipeline方式:
Client → Redis → Redis → Redis → Client
(一次网络往返执行多个命令)

Pipeline性能对比

方式 10000次SET耗时
逐条执行 约10秒
Pipeline 约0.1秒
提升 100倍

Java实现Pipeline

java 复制代码
public void batchInsert(Map<String, String> data) {
    redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
        data.forEach((key, value) -> {
            connection.set(key.getBytes(), value.getBytes());
        });
        return null;
    });
}

Pipeline注意事项

注意点 说明
批量大小 控制每次Pipeline的命令数量(建议100-1000)
原子性 Pipeline中的命令不是原子的
返回值 注意返回值的顺序

3.2 Lua脚本优化

经验之谈:将多个命令封装成Lua脚本,减少网络往返。

lua 复制代码
-- 扣减库存Lua脚本
local stock = redis.call('GET', KEYS[1])
if tonumber(stock) > 0 then
    redis.call('DECR', KEYS[1])
    return 1
else
    return 0
end

Java调用Lua脚本

java 复制代码
String script = 
    "local stock = redis.call('GET', KEYS[1]) " +
    "if tonumber(stock) > 0 then " +
    "    redis.call('DECR', KEYS[1]) " +
    "    return 1 " +
    "else " +
    "    return 0 " +
    "end";

RedisScript<Long> redisScript = RedisScript.of(script, Long.class);
Long result = redisTemplate.execute(redisScript, 
    Collections.singletonList("stock:product:123"));

3.3 连接池优化

踩坑提醒:连接池配置不当可能导致连接泄漏或性能下降。

Jedis连接池配置

java 复制代码
JedisPoolConfig config = new JedisPoolConfig();
// 最大连接数
config.setMaxTotal(200);
// 最大空闲连接数
config.setMaxIdle(50);
// 最小空闲连接数
config.setMinIdle(10);
// 获取连接最大等待时间(毫秒)
config.setMaxWaitMillis(3000);
// 连接有效性检查
config.setTestOnBorrow(true);
config.setTestOnReturn(false);
config.setTestWhileIdle(true);

JedisPool pool = new JedisPool(config, "127.0.0.1", 6379);

Lettuce连接池配置

java 复制代码
RedisClient client = RedisClient.create("redis://127.0.0.1:6379");
StatefulRedisConnection<String, String> connection = client.connect();

// Lettuce使用Netty,单个连接即可支持高并发
// 如需连接池,使用ConnectionPoolSupport
GenericObjectPool<StatefulRedisConnection<String, String>> pool = 
    ConnectionPoolSupport.createGenericObjectPool(
        () -> client.connect(),
        new GenericObjectPoolConfig()
    );

连接池配置建议

参数 建议值 说明
maxTotal 200 最大连接数
maxIdle 50 最大空闲连接
minIdle 10 最小空闲连接
maxWaitMillis 3000 获取连接超时
testOnBorrow true 获取时检查连接

3.4 网络配置优化

Redis配置优化

conf 复制代码
# 绑定IP
bind 0.0.0.0

# 保护模式
protected-mode no

# TCP端口
port 6379

# TCP连接队列
tcp-backlog 511

# TCP保活
tcp-keepalive 300

# 最大客户端连接数
maxclients 10000

# 输出缓冲区限制
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60

四、大key治理

4.1 什么是大key

踩坑提醒:大key是Redis性能杀手,必须及时发现和处理。

大key定义

类型 大key标准
String value > 10KB
Hash 元素数 > 5000
List 元素数 > 5000
Set 元素数 > 5000
ZSet 元素数 > 5000

4.2 大key的危害

危害 说明
内存占用大 单个key占用过多内存
网络阻塞 传输大key阻塞网络
过期阻塞 删除大key阻塞主线程
迁移阻塞 大key迁移影响性能

4.3 大key发现

方法1:使用redis-cli --bigkeys

shell 复制代码
redis-cli --bigkeys

# 输出示例:
# -------- summary -------
# Sampled 100000 keys in the keyspace!
# Biggest string found 'user:10000' has 10240 bytes
# Biggest hash found 'session:5000' has 10000 fields

方法2:使用MEMORY USAGE命令

redis 复制代码
# 查看key占用内存
MEMORY USAGE mykey
# 返回字节数

# 分析多个key
MEMORY USAGE user:1
MEMORY USAGE user:2

方法3:使用RDB分析工具

shell 复制代码
# 使用rdb-tools分析
rdb --command json /var/lib/redis/dump.rdb > dump.json

4.4 大key删除

踩坑提醒:直接使用DEL删除大key会阻塞Redis主线程。

安全删除大key

redis 复制代码
# String类型:使用UNLINK(异步删除)
UNLINK bigkey

# Hash类型:分批删除
HSCAN bigkey 0 COUNT 100
HDEL bigkey field1 field2 ...

# List类型:分批删除
LTRIM bigkey 0 -101
LTRIM bigkey 0 -101
# 直到删除完毕

# Set类型:分批删除
SSCAN bigkey 0 COUNT 100
SREM bigkey member1 member2 ...

# ZSet类型:分批删除
ZREMRANGEBYRANK bigkey 0 99

Java代码示例

java 复制代码
// 安全删除Hash大key
public void deleteBigHash(String key) {
    ScanOptions options = ScanOptions.scanOptions().count(100).build();
    Cursor<Map.Entry<Object, Object>> cursor = redisTemplate.opsForHash().scan(key, options);
    
    List<Object> fields = new ArrayList<>();
    while (cursor.hasNext()) {
        fields.add(cursor.next().getKey());
        if (fields.size() >= 100) {
            redisTemplate.opsForHash().delete(key, fields.toArray());
            fields.clear();
        }
    }
    if (!fields.isEmpty()) {
        redisTemplate.opsForHash().delete(key, fields.toArray());
    }
}

4.5 大key预防

预防措施 说明
拆分大key 将大key拆分成多个小key
控制元素数量 Hash/Set/List元素数控制在5000以内
控制value大小 String类型value控制在10KB以内
监控告警 定期扫描大key并告警
代码审查 代码层面禁止写入大key

大key拆分示例

redis 复制代码
# 拆分前:一个大Hash
HSET user:1000:profile name "张三" age 25 city "北京" ...

# 拆分后:多个小Hash
HSET user:1000:basic name "张三" age 25
HSET user:1000:address city "北京" province "北京"
HSET user:1000:contact phone "13800138000"

五、踩坑提醒

5.1 KEYS命令在生产环境禁用

踩坑提醒:KEYS命令会阻塞Redis,生产环境绝对禁止使用!

替代方案

redis 复制代码
# 使用SCAN替代KEYS
SCAN 0 MATCH user:* COUNT 100

5.2 避免使用SELECT切换数据库

redis 复制代码
# 不推荐:使用多个数据库
SELECT 0
SELECT 1
SELECT 2

# 推荐:使用key前缀区分
SET user:1:name "张三"
SET order:1:id "12345"

5.3 避免大量key同时过期

java 复制代码
// 不推荐:相同过期时间
redisTemplate.expire("key1", 3600, TimeUnit.SECONDS);
redisTemplate.expire("key2", 3600, TimeUnit.SECONDS);

// 推荐:随机过期时间
Random random = new Random();
int baseExpire = 3600;
int randomExpire = random.nextInt(600);
redisTemplate.expire("key1", baseExpire + randomExpire, TimeUnit.SECONDS);

5.4 避免使用阻塞命令

命令 说明 替代方案
KEYS 遍历所有key SCAN
HGETALL 获取所有字段 HSCAN
SMEMBERS 获取所有成员 SSCAN
BLPOP 阻塞弹出 LPOP + 轮询

5.5 常见性能问题汇总

问题 原因 解决方案
响应慢 慢查询 分析SLOWLOG
内存高 内存碎片 开启碎片整理
CPU高 大key操作 拆分大key
网络阻塞 大value 压缩或拆分
连接超时 连接池配置不当 优化连接池

六、性能监控

6.1 关键监控指标

指标 说明 告警阈值
used_memory 内存使用量 > 80% maxmemory
mem_fragmentation_ratio 内存碎片率 > 1.5
connected_clients 客户端连接数 > maxclients * 0.8
blocked_clients 阻塞客户端数 > 10
instantaneous_ops_per_sec 当前QPS 监控趋势
slowlog_len 慢查询数量 > 0 告警
expired_keys 过期key数量 监控趋势
evicted_keys 驱逐key数量 > 0 告警

6.2 监控命令

redis 复制代码
# 查看服务器信息
INFO

# 查看内存信息
INFO memory

# 查看统计信息
INFO stats

# 查看客户端连接
CLIENT LIST

# 查看慢查询
SLOWLOG GET 10

# 实时监控命令
MONITOR

6.3 监控工具

工具 说明
Redis-cli 官方命令行工具
RedisInsight 官方可视化工具
Prometheus + Grafana 监控告警平台
Redis Exporter Redis指标导出器
Redis-stat 实时监控工具

七、面试高频考点

7.1 如何排查Redis性能问题?

答案

  1. 查看慢查询日志
redis 复制代码
SLOWLOG GET 10
  1. 检查内存使用
redis 复制代码
INFO memory
  1. 检查大key
shell 复制代码
redis-cli --bigkeys
  1. 检查客户端连接
redis 复制代码
CLIENT LIST
  1. 检查命令执行情况
redis 复制代码
INFO stats
  1. 使用MONITOR实时监控
redis 复制代码
MONITOR

7.2 Redis内存优化有哪些方法?

答案

  1. 选择合适的数据结构:使用编码效率高的数据类型

  2. 控制key长度:使用简短的key名

  3. 使用压缩编码:ziplist、intset等

  4. 设置过期时间:及时清理无用数据

  5. 开启内存碎片整理:activedefrag

  6. 避免大key:拆分大key

  7. 使用Hash替代String:多个字段存储在一起

7.3 为什么KEYS命令在生产环境禁用?

答案

  1. 时间复杂度O(N):需要遍历所有key

  2. 阻塞主线程:Redis单线程模型,KEYS执行期间无法处理其他请求

  3. 影响服务可用性:大量key时可能导致服务不可用

替代方案:使用SCAN命令分批遍历

7.4 如何处理大key?

答案

  1. 发现大key

    • redis-cli --bigkeys
    • MEMORY USAGE命令
    • RDB分析工具
  2. 删除大key

    • 使用UNLINK异步删除
    • 分批删除(HSCAN + HDEL)
  3. 预防大key

    • 拆分大key
    • 控制元素数量
    • 代码审查

八、参考资料

  1. Redis官方文档 - 内存优化
  2. Redis官方文档 - 慢查询日志
  3. Redis性能优化最佳实践

九、互动话题

  1. 你在生产环境遇到过Redis性能问题吗?是如何排查和解决的?
  2. 你们团队是如何监控Redis的?使用了哪些工具?
  3. 对于大key治理,你有什么好的经验和建议?

欢迎在评论区分享你的实战经验!


下期预告:Day15我们将进行Redis面试高频考点汇总,全面回顾Redis核心知识点,为面试做好准备。

相关推荐
程序员老油条1 小时前
用 AI 生成复杂 SQL:LangChain4j + 本地模型实践
数据库·人工智能·sql
IT邦德1 小时前
Oracle 26ai RAC 通过gold image RU打补丁
数据库·oracle
smith成长之旅1 小时前
08 | Mem0 框架分析: BM25 的 Sigmoid 归一化
数据库·python·算法
C137的本贾尼1 小时前
MySQL 整体架构与存储引擎对比
数据库·mysql·架构
禅思院1 小时前
大列表性能优化 · 工程实战·四
开发语言·前端·性能优化·前端框架·php·异步加载
C137的本贾尼2 小时前
【实战】分析一张真实业务表的 InnoDB 存储结构
java·大数据·数据库
超梦dasgg2 小时前
亿级数据 不停服务平滑迁移(生产环境实战方案)
java·数据库
ct9782 小时前
ES6 新特性
前端·vue.js·性能优化
j_xxx404_2 小时前
MySQL数据库基础硬核解析:从 C/S 网络服务到磁盘文件与存储引擎
linux·运维·服务器·开发语言·数据库·mysql·ai