Redis 7.0在2022年4月正式发布,带来了很多新特性和性能优化。

我们公司的Redis一直用的是6.2版本,随着业务增长,Redis的压力越来越大,高峰期响应时间能到10ms以上,偶尔还会超时。老板让我研究一下Redis 7.0,看看能不能通过升级和优化提升性能。

我花了一周时间,研究了Redis 7.0的新特性,在测试环境验证后,把生产环境的Redis从6.2升级到了7.0,同时做了全面的性能优化。最终效果不错:平均响应时间从5ms降到了1ms,高峰期也能稳定在2ms以内。

本文分享Redis 7.0的新特性、升级过程、性能优化实战、踩坑记录,帮你用好Redis 7.0。

一、Redis 7.0的重要新特性

先说说Redis 7.0有哪些重要的新特性。

1. Redis Functions

Redis 7.0引入了Functions(函数),这是一个重要的新特性。

Functions和Lua脚本类似,但有几个优势:

  • Functions是持久化的,存在数据库里,重启不丢失
  • Functions有版本管理,可以升级
  • Functions支持AOF和复制,更可靠
  • Functions的执行效率更高

Lua脚本的问题是:每次都要传脚本内容,或者用EVALSHA但要管理脚本缓存,主从切换时脚本可能丢失。Functions解决了这些问题。

# 创建函数
redis-cli -x FUNCTION LOAD REPLACE <<EOF
#!lua name=mylib
redis.register_function('myincr', function(keys, args)
  return redis.call('INCR', keys[1])
end)
EOF

# 调用函数
redis-cli FCALL myincr 1 mykey

2. 多部分AOF(Multi-part AOF)

Redis 7.0改进了AOF(Append Only File)持久化机制。

以前的AOF是一个文件,重写的时候会创建一个临时文件,重写完成后替换。Redis 7.0把AOF分成了多个文件:

  • 基础文件(base file):重写生成的快照
  • 增量文件(incr file):重写之后的增量命令
  • 清单文件(manifest file):记录文件列表和历史

这种方式的好处:

  • AOF重写更快,不需要复制整个文件
  • 崩溃恢复更快,只需要加载基础文件+增量文件
  • 支持AOF的原子操作,更安全

3. ACL改进

Redis 7.0改进了ACL(访问控制列表):

  • 支持按key前缀授权(~prefix:*
  • 支持按命令的子命令授权
  • 支持select命令的权限控制
  • ACL可以持久化到外部文件

这些改进,让Redis的权限管理更细粒度,更安全。

4. 客户端Eviction

Redis 7.0新增了客户端Eviction机制。

当内存不足时,Redis可以自动断开一些客户端连接,释放内存。可以配置:

  • 最大客户端内存使用量
  • 断开哪些类型的客户端(普通客户端、从节点、订阅客户端等)

这对防止客户端内存泄漏很有用。

5. 性能优化

Redis 7.0做了很多底层的性能优化:

  • 列表(List)的底层实现优化,内存占用更少
  • 哈希(Hash)的编码优化,小哈希更省内存
  • 网络IO优化,高并发下性能更好
  • 过期key的删除优化,减少阻塞
  • RDB加载速度提升

根据官方测试,Redis 7.0比6.0性能提升了约20-30%。

6. 其他新特性

  • SHUTDOWN命令增加了NOWFORCE选项
  • CLIENT NO-EVICT命令,保护特定客户端不被evict
  • COMMAND DOCS命令,查看命令的文档
  • 支持EXPIRETIMEPEXPIRETIME命令,查看过期时间戳
  • 集群模式的改进,支持更灵活的槽位迁移

二、升级过程

说说我们从Redis 6.2升级到7.0的过程。

1. 升级前的准备

升级前,做了充分的准备:

  • 阅读Redis 7.0的release notes,了解不兼容的变化
  • 检查应用代码,确认没有使用被废弃的命令
  • 备份数据,确保可以回滚
  • 在测试环境验证,确认应用兼容

不兼容的变化:

  • 一些旧的配置项被移除或改名
  • INFO命令的输出格式有变化
  • Lua脚本的一些行为有变化
  • 一些旧的RDB格式不再支持

2. 升级步骤

我们用的是主从架构,升级步骤:

  1. 先升级从节点:停掉从节点,替换二进制文件,启动,等待同步完成
  2. 验证从节点:确认数据同步正常,应用能正常连接
  3. 主从切换:把主节点切换为从节点,原从节点变为主节点
  4. 升级原主节点:停掉原主节点,替换二进制文件,启动,作为从节点同步
  5. 验证:确认主从同步正常,应用无异常

整个过程,应用没有停机,只是在主从切换时有几秒钟的延迟。

3. 回滚方案

准备了回滚方案:

  • 如果升级后出问题,立即切回6.2版本的主节点
  • 数据用升级前的备份恢复
  • 应用的连接配置改回原地址

因为Redis 7.0的RDB格式和6.2不完全兼容,回滚的时候要注意数据格式。

三、性能优化实战

升级之后,做了全面的性能优化。

1. 内存优化

Redis是内存数据库,内存优化是重点。

优化一:使用合适的数据结构

  • 小数据量用压缩编码:Hash、List、ZSet在元素少的时候用压缩列表(ziplist/listpack),省内存
  • 避免大key:单个key不要存太多数据,否则会阻塞Redis
  • 用Bitmap、HyperLogLog等概率数据结构,省内存

我们有一个用户标签的场景,原来用Set存每个用户的标签,内存占用很大。改成用Bitmap后,内存占用减少了80%。

优化二:设置过期时间

很多key没有设置过期时间,一直存在内存里。

我们梳理了所有的key,给不需要永久存储的key都加了过期时间。比如:

  • 缓存数据:1小时到1天
  • 会话数据:7天
  • 临时数据:1小时

加了过期时间后,内存占用减少了30%。

优化三:内存淘汰策略

配置了合理的内存淘汰策略:

maxmemory 10gb
maxmemory-policy allkeys-lru

allkeys-lru表示所有key都按LRU淘汰。我们的场景是缓存为主,用这个策略比较合适。

如果是有持久化需求的场景,可以用volatile-lru(只淘汰设置了过期时间的key)。

优化四:Redis 7.0的listpack

Redis 7.0用listpack替代了ziplist,listpack更省内存,访问更快。

升级到7.0后,小Hash、小List、小ZSet自动用listpack编码,内存占用减少了约15%。

2. 网络优化

优化一:连接池

应用端用连接池,避免频繁创建和销毁连接。

我们的Java应用用的是Lettuce,配置了连接池:

GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
poolConfig.setMaxTotal(50);
poolConfig.setMaxIdle(20);
poolConfig.setMinIdle(5);

连接池的大小要合理,不是越大越好。一般来说,连接数 = CPU核心数 * 2 + 磁盘数。

优化二:Pipeline

对于批量操作,用Pipeline减少网络往返。

比如,一次更新100个key,不用循环执行100次命令,而是用Pipeline一次发送。

List<Object> results = redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
    for (String key : keys) {
        connection.get(key.getBytes());
    }
    return null;
});

用Pipeline后,批量操作的性能提升了5-10倍。

优化三:避免热key

热key是指访问量特别大的key,会导致单个Redis节点压力过大。

我们的解决方案:

  • 本地缓存:热点数据在应用端做本地缓存(Caffeine),减少Redis访问
  • key拆分:把一个热key拆成多个key,分散压力
  • 读写分离:读请求走从节点,写请求走主节点

3. 持久化优化

优化一:AOF配置

appendonly yes
appendfsync everysec
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
  • appendfsync everysec:每秒刷盘一次,性能和安全的平衡
  • 自动重写:AOF文件增长100%且超过64MB时自动重写

Redis 7.0的多部分AOF,重写速度更快,对性能影响更小。

优化二:RDB配置

save 900 1
save 300 10
save 60 10000

RDB快照的频率要合理,太频繁会影响性能,太少会丢失更多数据。

我们的场景,用AOF为主,RDB为辅。RDB主要用于备份和快速重启。

优化三:禁用危险命令

在生产环境,禁用一些危险命令:

rename-command FLUSHDB ""
rename-command FLUSHALL ""
rename-command CONFIG ""
rename-command KEYS ""

防止误操作导致数据丢失或性能问题。

4. 慢查询优化

SLOWLOG命令查看慢查询:

SLOWLOG GET 10

我们发现了几个慢查询:

  • KEYS *:全量遍历,禁用,改用SCAN
  • 大key的HGETALL:拆分成多个小key,或者用HSCAN
  • 复杂的Lua脚本:优化脚本逻辑,或者拆分成多个命令

优化后,慢查询从每天几十条降到了几乎没有。

四、具体的优化案例

说说几个具体的优化案例。

案例一:排行榜优化

我们有一个排行榜功能,用ZSet实现。原来的实现是每次都全量计算排名,性能很差。

优化方案:

  • 用ZSet的ZREVRANK命令直接获取排名,O(log N)复杂度
  • ZINCRBY增量更新分数,不需要全量重算
  • 排行榜前100名用缓存,减少Redis访问

优化后,排行榜接口的响应时间从50ms降到了2ms。

案例二:计数器优化

我们有一个文章阅读量计数器,每篇文章一个key,用INCR更新。

问题是,文章很多,key数量大,内存占用高。而且每次阅读都写Redis,压力大。

优化方案:

  • 用Hash把多篇文章的计数存在一个key里,减少key数量
  • 应用端先累加,每隔一段时间批量写入Redis
  • 用Redis 7.0的Functions,把计数逻辑封装成函数,减少网络往返

优化后,内存占用减少了50%,写入QPS提升了3倍。

案例三:缓存穿透优化

我们遇到了缓存穿透的问题:大量请求查询不存在的key,直接打到数据库。

优化方案:

  • 缓存空值:查询不存在的key,也缓存一个空值,过期时间短一些(比如5分钟)
  • 布隆过滤器:用Redis的Bloom Filter(RedisBloom模块),过滤不存在的key
  • 接口限流:对异常流量限流

优化后,数据库的压力减少了80%。

五、监控和运维

优化之后,监控和运维也很重要。

1. 关键监控指标

用Prometheus + Grafana监控Redis:

  • 性能指标:QPS、响应时间、慢查询数
  • 内存指标:内存使用率、内存碎片率、淘汰key数
  • 网络指标:连接数、网络流量
  • 持久化指标:AOF大小、RDB执行时间
  • 主从指标:主从延迟、同步状态

2. 告警配置

配置了告警:

  • 内存使用率超过80%告警
  • 响应时间超过5ms告警
  • 主从延迟超过1秒告警
  • 慢查询超过10条/分钟告警
  • 连接数超过最大值的80%告警

3. 定期运维

  • 每天检查慢查询
  • 每周检查内存使用情况,清理无用key
  • 每月做一次主从切换演练
  • 定期备份,验证备份可用性

六、踩坑记录

说说升级和优化过程中踩的坑。

坑一:Lua脚本不兼容

升级到7.0后,有一个Lua脚本报错了。

原因是Redis 7.0的Lua脚本中,redis.call的返回值类型有变化。原来返回的是number,现在返回的是string(取决于命令)。

解决方法:修改Lua脚本,用tonumber()做类型转换。或者用Redis 7.0的Functions替代Lua脚本。

坑二:AOF文件变大

升级后,AOF文件突然变大了。

原因是Redis 7.0的多部分AOF,基础文件和增量文件分开了。看起来文件多了,但总大小其实差不多。而且重写更快了。

解决方法:理解新的AOF机制,不需要担心。可以用INFO persistence查看详细信息。

坑三:客户端连接被断开

升级后,偶尔有客户端连接被断开。

原因是Redis 7.0的客户端Eviction机制,默认会在内存不足时断开客户端。我们的内存配置有点紧张,高峰期触发了Eviction。

解决方法:调大maxmemory,或者配置client-eviction no关闭这个功能。我们选择了调大内存。

坑四:CONFIG命令被禁用

我们的应用用了CONFIG GET命令获取配置,升级后发现报错了。

原因是我们在配置文件里rename了CONFIG命令,但应用代码里还在用。

解决方法:修改应用代码,不用CONFIG命令。或者在测试环境用,生产环境禁用。

七、优化效果

说说最终的优化效果。

1. 性能提升

  • 平均响应时间:从5ms降到1ms
  • P99响应时间:从20ms降到5ms
  • QPS:从5万提升到15万
  • 高峰期无超时

2. 内存优化

  • 内存使用率:从85%降到55%
  • 内存碎片率:从1.5降到1.1
  • key数量:从2000万降到1200万(清理了无用key)

3. 稳定性提升

  • 慢查询:从每天几十条降到几乎没有
  • 主从延迟:从偶尔1秒以上降到稳定在10ms以内
  • 故障次数:从每月2-3次降到0

八、写在最后

Redis 7.0是一个值得升级的版本。新特性(Functions、多部分AOF、ACL改进)很实用,性能也有明显提升。

但升级只是第一步,真正的性能提升,来自于全面的优化:内存优化、网络优化、持久化优化、慢查询优化、业务层面的优化。

Redis是一个简单但强大的工具。用好了,它能成为你系统的性能利器;用不好,它也可能成为系统的瓶颈。

2022年了,Redis已经成为很多系统的标配。但很多人只是把它当缓存用,没有发挥它的全部能力。了解新特性,做好性能优化,能让Redis更好地为业务服务。

最后,用一句话总结:"Redis 7.0的升级,不只是版本的升级,更是性能和架构的升级。理解新特性,做好优化,才能从慢到快。"

愿大家的Redis,都能又快又稳。