Redis 7.0发布后,我在项目中用了很多新特性,也踩了不少坑。

本文分享Redis 7.0新特性实战中的踩坑经验,包括Redis Functions、ACL、多部分AOF、集群、新命令等方面的问题和解决方案。

一、Redis Functions踩坑

Redis Functions是7.0最重要的新特性,我用得最多,踩的坑也最多。

1. 坑一:Function加载失败

问题: 第一次用Functions的时候,加载总是失败。

FUNCTION LOAD "#!lua name=mylib\nredis.register_function('hello', function() return 'hello' end)"

报错:"Function library already exists"

原因: 之前加载过同名的library,没有加replace参数。

解决方案: 加REPLACE参数,覆盖已有的library。

FUNCTION LOAD REPLACE "#!lua name=mylib\nredis.register_function('hello', function() return 'hello' end)"

经验: 开发环境用REPLACE,生产环境要谨慎,确认版本后再覆盖。

2. 坑二:Function里不能用某些命令

问题: 在Function里调用了一些命令,报错。

redis.register_function('test', function()
    return redis.call('KEYS', '*')  -- 报错
end)

原因: Functions里有命令白名单,不是所有命令都能用。比如KEYS、FLUSHALL等危险命令被禁止了。

解决方案: 用SCAN代替KEYS,或者用其他安全的方式。

redis.register_function('test', function()
    local keys = {}
    local cursor = '0'
    repeat
        local result = redis.call('SCAN', cursor)
        cursor = result[1]
        for _, key in ipairs(result[2]) do
            table.insert(keys, key)
        end
    until cursor == '0'
    return keys
end)

经验: 用Functions之前,先看文档里的命令白名单,不要想当然。

3. 坑三:Function的持久化问题

问题: Functions加载后,重启Redis发现Function还在,以为是持久化的。但换了一台机器,Function就没了。

原因: Functions是存在数据库里的,会随RDB/AOF持久化。但如果是新机器,没有数据文件,Function就没了。

解决方案:

  • 把Function代码存在代码仓库里
  • 应用启动时检查并加载Function
  • 不要依赖Redis里的Function持久化
def ensure_functions(r):
    try:
        r.function_list()
    except:
        # Function不存在,加载
        with open('functions.lua', 'r') as f:
            code = f.read()
        r.function_load(code, replace=True)

经验: Functions虽然持久化了,但应用启动时还是要检查和加载,保证可移植性。

4. 坑四:Function的调试困难

问题: Function里的Lua代码出错了,很难调试。

  • 没有print输出
  • 错误信息不详细
  • 不能断点调试

解决方案:

  • 用return返回调试信息
  • 先在本地用Lua解释器测试
  • 写单元测试
  • 错误处理要完善
redis.register_function('test', function(keys, args)
    local ok, result = pcall(function()
        -- 你的逻辑
        return 'success'
    end)
    if not ok then
        return 'error: ' .. tostring(result)
    end
    return result
end)

经验: Functions的调试确实不方便,要写好错误处理和日志。

二、ACL踩坑

Redis 6.0引入了ACL,7.0做了改进,但用的时候还是踩了坑。

1. 坑一:权限配置太严

问题: 配置了ACL用户,结果应用连不上,报权限错误。

ACL SETUSER appuser on >password ~app:* +@all

应用用了一些不在app:*前缀下的key,就报错了。

原因: key前缀限制太严,应用实际用的key不只是app:*

解决方案:

  • 先梳理应用用到的所有key
  • 合理配置key前缀
  • 不要一开始就限制太严
  • 可以先用~*(所有key),逐步收紧
# 先宽松
ACL SETUSER appuser on >password ~* +@all

# 逐步收紧
ACL SETUSER appuser on >password ~app:* ~cache:* +@all

经验: ACL配置要循序渐进,先宽后严,不要一步到位。

2. 坑二:命令类别授权不完整

问题: 给用户授权了+@read,但有些读命令用不了。

原因: 有些命令的类别和你想的不一样。比如INFO属于@admin,不是@read

解决方案:

  • 查看命令的类别:COMMAND INFO INFO
  • 按需添加命令类别
  • 或者直接授权具体命令
# 授权具体命令
ACL SETUSER appuser on >password ~* +GET +SET +INFO

经验: 不要想当然地认为命令属于某个类别,要用COMMAND INFO查一下。

3. 坑三:ACL不生效

问题: 配置了ACL,但客户端还是能用所有命令。

原因:

  • 默认用户没有禁用
  • 客户端用了默认用户连接
  • ACL文件没有加载

解决方案:

  • 禁用默认用户:ACL SETUSER default off
  • 确认客户端用的是配置的用户
  • 检查ACL配置是否加载:ACL LIST

经验: 配置ACL后,一定要验证是否生效,不要以为配置了就完事了。

三、多部分AOF踩坑

Redis 7.0的AOF改成了多部分,踩了一些坑。

1. 坑一:AOF文件变大了

问题: 升级到7.0后,AOF文件变大了。

原因: 多部分AOF有base文件和incr文件,base文件是RDB格式,可能比之前的AOF大。

解决方案:

  • 这是正常现象,不用太担心
  • 配置AOF重写频率
  • 监控磁盘空间

经验: 升级前要了解AOF格式的变化,预留磁盘空间。

2. 坑二:AOF恢复变慢

问题: 重启Redis,AOF恢复时间变长了。

原因: 多部分AOF恢复时,先加载base文件,再重放incr文件。如果incr文件很大,恢复就慢。

解决方案:

  • 配置AOF重写,控制incr文件大小
  • auto-aof-rewrite-min-size
  • auto-aof-rewrite-percentage
auto-aof-rewrite-min-size 64mb
auto-aof-rewrite-percentage 100

经验: 合理配置AOF重写,避免incr文件过大。

四、集群踩坑

Redis 7.0对集群做了改进,但还是有坑。

1. 坑一:新命令不支持集群

问题: 在集群模式下用了一些新命令,报错"Command can't be executed in cluster mode"。

原因: 不是所有命令都支持集群模式,尤其是跨slot的命令。

解决方案:

  • 查文档,确认命令是否支持集群
  • 跨slot的操作要自己处理
  • 用hash tag确保key在同一个slot
# 用hash tag,确保在同一个slot
{user:1}:name
{user:1}:age

经验: 集群模式下,用新命令前先确认是否支持。

2. 坑二:集群扩容数据迁移慢

问题: 集群扩容,加了新节点,数据迁移很慢。

原因: 7.0的集群迁移虽然有改进,但数据量大的时候还是慢。

解决方案:

  • 低峰期扩容
  • 控制迁移速度
  • 监控迁移进度
  • 准备回滚方案

经验: 集群扩容要在低峰期做,要有耐心,不要急。

五、新命令踩坑

1. 坑一:GETDEL的误用

问题: 用了GETDEL,结果数据被删了,以为是bug。

value = r.getdel('key')  # 获取并删除

后来又用r.get('key'),发现是None,以为数据丢了。

原因: GETDEL就是获取并删除,调用后key就没了。这是正常行为,不是bug。

解决方案:

  • 理解命令的语义
  • 不要想当然地认为GETDEL只是GET
  • 用之前看文档

经验: 新命令用之前,一定要看文档,理解清楚语义。

2. 坑二:ZMPOP的返回值

问题: 用了ZMPOP,返回值格式和预期不一样。

result = r.zmpop(1, ['zset1', 'zset2'])
# 返回的是 [zset_name, [member, score]]

我以为直接返回member和score,结果多了一层zset_name。

解决方案:

  • 看文档,了解返回值格式
  • 写代码时处理好返回值
  • 写测试验证
result = r.zmpop(1, ['zset1', 'zset2'])
if result:
    zset_name, member_score = result
    member, score = member_score

经验: 新命令的返回值格式要仔细看,不要想当然。

六、升级踩坑

1. 坑一:直接升级不兼容

问题: 直接从Redis 6.x升级到7.0,启动失败。

原因:

  • 配置文件有不兼容的参数
  • 数据文件格式不兼容
  • 客户端版本太老

解决方案:

  • 升级前看升级指南
  • 先在测试环境验证
  • 备份数据
  • 逐步升级,不要一次性全升

经验: 大版本升级要谨慎,先测试,再灰度,最后全量。

2. 坑二:客户端不兼容

问题: 升级Redis 7.0后,一些老客户端报错。

原因:

  • 老客户端不支持新的RESP协议特性
  • 新命令老客户端不支持
  • 一些行为变化

解决方案:

  • 升级客户端库到最新版本
  • 测试所有功能
  • 不要急着用新命令

经验: 升级Redis的同时,也要升级客户端库。

七、性能踩坑

1. 坑一:Functions比Lua脚本慢

问题: 以为Functions比Lua脚本快,结果实际测试反而慢了。

原因: Functions有额外的开销(加载、版本管理等),简单的场景下可能比EVAL慢。

解决方案:

  • 简单场景用EVAL就够了
  • 复杂、复用多的场景用Functions
  • 实际测试,不要想当然

经验: 新技术不一定更快,要实际测试。

2. 坑二:ACL影响性能

问题: 开了ACL后,性能下降了。

原因: ACL需要每次命令都检查权限,有一定开销。

解决方案:

  • 开销不大,一般可以接受
  • 如果对性能要求极高,可以优化ACL规则
  • 实际测试,看影响有多大

经验: 安全和性能要平衡,不要为了安全牺牲太多性能,也不要为了性能不要安全。

八、我的建议

基于这些踩坑经验,我的建议:

1. 升级前做好准备

  • 读升级指南
  • 测试环境验证
  • 备份数据
  • 准备回滚方案

2. 新特性逐步使用

  • 不要一上来就用所有新特性
  • 先在非核心功能试用
  • 稳定后再推广
  • 积累经验

3. 充分测试

  • 单元测试
  • 集成测试
  • 性能测试
  • 异常测试

4. 监控和日志

  • 监控Redis状态
  • 记录错误日志
  • 设置告警
  • 及时发现问题

5. 多看文档

  • 官方文档是最好的资料
  • 新特性一定要看文档
  • 不要想当然
  • 社区博客和经验也值得参考

九、写在最后

Redis 7.0带来了很多新特性,也带来了一些坑。

踩坑不可怕,可怕的是踩了坑不总结。每个坑都是一次学习的机会,踩过了,记住了,以后就不会再踩。

新技术的使用,总是要交学费的。但只要做好准备,充分测试,逐步推广,就能把风险降到最低。

2022年了,Redis已经发展到7.0,功能越来越丰富。用好新特性,能提升开发效率和系统性能。但也要注意,新特性可能有坑,要谨慎使用。

最后,用一句话总结:"Redis 7.0新特性很好用,但坑也不少。做好准备,充分测试,逐步推广,才能少踩坑。"

愿你用Redis 7.0,少踩坑,多受益。