You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Redis生产环境全Key过期时间检查及无过期Key处理的实现方案咨询

检查Redis生产环境Key过期时间的可行方案与实现

首先得给你提个醒:你一开始想的get *完全不对路!get是用来取单个Key的值的,根本不是遍历Key的命令,就算换成KEYS *,生产环境也绝对不能用——Redis是单线程模型,数百万个Key的情况下,KEYS *会一次性遍历所有Key,直接阻塞Redis进程好几分钟,导致服务彻底不可用,这是生产环境的大忌!

正确的方案是用SCAN命令,它是增量式遍历Key的,每次只返回一小批Key(数量可配置),不会长时间阻塞Redis,完全适合生产环境大规模Key的场景。整体流程是:用SCAN遍历所有Key → 对每个Key用TTL(秒精度)或PTTL(毫秒精度)检查过期时间 → 若返回-1说明该Key没有设置过期时间,再用EXPIRE或PEXPIRE给它设置过期时间。

接下来分别说Unix环境和C#借助SSH Nuget包的实现方式:

Unix环境下的Shell脚本实现

你可以写一个bash脚本,结合redis-cli来完成这个操作,既简单又高效:

#!/bin/bash
# 配置参数
REDIS_CLI="/usr/bin/redis-cli"
# 若Redis有密码/指定端口,修改为:REDIS_CLI="/usr/bin/redis-cli -a your_password -p 6379"
EXPIRE_DAYS=30  # 给无过期的Key设置30天过期,可自行调整
EXPIRE_SECONDS=$((EXPIRE_DAYS * 86400))
SCAN_COUNT=1000 # 每次SCAN返回的Key数量,可根据Redis性能调整

# 初始化游标为0
cursor="0"

while true; do
    # 执行SCAN命令
    result=$($REDIS_CLI SCAN $cursor COUNT $SCAN_COUNT)
    # 解析结果:第一部分是新游标,第二部分是Key列表
    cursor=$(echo "$result" | head -n1)
    keys=$(echo "$result" | tail -n1)

    # 处理当前批次的Key
    for key in $keys; do
        # 获取Key的过期时间
        ttl=$($REDIS_CLI TTL "$key")
        # TTL返回-1表示无过期时间,返回-2表示Key不存在
        if [ "$ttl" -eq -1 ]; then
            echo "设置过期时间:$key"
            $REDIS_CLI EXPIRE "$key" $EXPIRE_SECONDS
        fi
    done

    # 游标回到0时,遍历完成
    if [ "$cursor" = "0" ]; then
        break
    fi
done

脚本注意事项:

  • 先在测试环境验证脚本逻辑,确认没问题再放到生产环境执行
  • SCAN_COUNT可以根据你的Redis性能调整,比如5000或10000,数值越大遍历越快,但对Redis的瞬时负载也会略高
  • 如果是Redis集群,需要针对每个节点单独执行这个脚本,因为SCAN只能遍历单个节点的Key

C#借助SSH Nuget包的实现

如果你需要用C#远程操作Unix服务器上的Redis,可以用SSH.NET这个Nuget包来建立SSH连接,然后执行redis-cli命令。

步骤1:安装SSH.NET包

在Nuget包管理器中搜索SSH.NET并安装,或者用命令行:

Install-Package SSH.NET

步骤2:C#代码实现

using Renci.SshNet;
using System;
using System.Linq;

namespace RedisExpirationTool
{
    class Program
    {
        static void Main(string[] args)
        {
            // SSH连接配置
            var sshConfig = new ConnectionInfo(
                host: "你的服务器IP",
                port: 22,
                username: "SSH用户名",
                new PasswordAuthenticationMethod("SSH用户名", "SSH密码")
                // 若用密钥认证,替换为:new PrivateKeyAuthenticationMethod("用户名", new PrivateKeyFile("密钥路径"))
            );

            // Redis配置
            string redisCliPath = "/usr/bin/redis-cli";
            int expireSeconds = 30 * 86400; // 30天过期
            int scanCount = 1000;

            using var sshClient = new SshClient(sshConfig);
            try
            {
                sshClient.Connect();
                if (!sshClient.IsConnected)
                {
                    Console.WriteLine("SSH连接失败");
                    return;
                }

                string cursor = "0";
                do
                {
                    // 执行SCAN命令
                    var scanCmd = sshClient.RunCommand($"{redisCliPath} SCAN {cursor} COUNT {scanCount}");
                    if (scanCmd.ExitStatus != 0)
                    {
                        Console.WriteLine($"SCAN命令执行失败:{scanCmd.Error}");
                        break;
                    }

                    // 解析SCAN结果
                    var outputLines = scanCmd.Result.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
                    if (outputLines.Length < 1) break;

                    cursor = outputLines[0].Trim();
                    var keys = outputLines.Length > 1 ? outputLines[1].Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries) : Array.Empty<string>();

                    // 处理每个Key
                    foreach (var key in keys)
                    {
                        var ttlCmd = sshClient.RunCommand($"{redisCliPath} TTL \"{key}\"");
                        if (ttlCmd.ExitStatus != 0)
                        {
                            Console.WriteLine($"获取Key [{key}]的TTL失败:{ttlCmd.Error}");
                            continue;
                        }

                        if (int.TryParse(ttlCmd.Result.Trim(), out int ttl) && ttl == -1)
                        {
                            // 设置过期时间
                            var expireCmd = sshClient.RunCommand($"{redisCliPath} EXPIRE \"{key}\" {expireSeconds}");
                            if (expireCmd.ExitStatus == 0)
                            {
                                Console.WriteLine($"成功给Key [{key}]设置过期时间");
                            }
                            else
                            {
                                Console.WriteLine($"给Key [{key}]设置过期时间失败:{expireCmd.Error}");
                            }
                        }
                    }

                } while (cursor != "0");

                Console.WriteLine("所有Key处理完成");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"执行出错:{ex.Message}");
            }
            finally
            {
                if (sshClient.IsConnected)
                {
                    sshClient.Disconnect();
                }
            }
        }
    }
}

C#代码注意事项:

  • 如果Redis需要密码,修改redisCliPath为"/usr/bin/redis-cli -a your_redis_password"
  • 若你的C#程序可以直接访问Redis(不需要通过SSH),更推荐用StackExchange.Redis客户端,直接调用IDatabase.ScanAsync方法,效率会更高
  • 同样,生产环境执行前一定要在测试环境验证逻辑

内容的提问来源于stack exchange,提问作者dqv70219

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 07:54:47