redis集群上找出无ttl的key
在Redis集群模式下处理近1亿个KEY并筛选出无TTL(过期时间)的KEY,核心挑战是分布式遍历和性能影响控制。由于集群中KEY分散在多个节点,且数据量极大,需结合集群特性选择高效方案。以下是两种可行方案,分别适用于实时分析和离线处理场景:
方案一:在线扫描(实时获取,适合非生产高峰)
通过SCAN命令渐进式遍历各节点的KEY,结合TTL命令检查过期时间,适合需要实时数据且能接受轻微性能影响的场景。
核心原理
- 集群节点遍历:Redis集群的KEY按槽位分布在主节点上,需逐个连接主节点(跳过从节点,避免重复处理)。
- 渐进式扫描:用
SCAN替代KEYS(KEYS会阻塞节点),SCAN通过游标分批返回KEY,每次迭代不阻塞服务器。 - TTL筛选:对
SCAN返回的每个KEY,用TTL命令检查:TTL = -1:KEY存在且无过期时间(目标KEY)。TTL = -2:KEY不存在(忽略)。TTL > 0:有过期时间(忽略)。
具体步骤
1. 获取集群主节点列表
通过CLUSTER NODES命令解析所有主节点的IP和端口(从节点无需处理,因数据与主节点一致)。
# 连接集群任意节点,获取节点信息
redis-cli -h 集群任意节点IP -p 端口 -a 密码 cluster nodes# 输出示例(筛选出role为master的节点)
# 123456... 192.168.1.100:6379@16379 master - 0 1620000000000 1 connected 0-5460
# 789012... 192.168.1.101:6379@16379 master - 0 1620000000000 2 connected 5461-10922
2. 逐个节点扫描并筛选无TTL的KEY
编写脚本(如Python),连接每个主节点,用SCAN遍历KEY,批量检查TTL。
示例脚本(Python):
import redis
import timedef get_master_nodes(cluster_host, cluster_port, password):"""获取集群所有主节点(IP, 端口)"""r = redis.StrictRedis(host=cluster_host, port=cluster_port, password=password, decode_responses=True)nodes = r.cluster_nodes().split('\n')masters = []for node in nodes:parts = node.split()if len(parts) < 4 or not parts[2].startswith('master'):continue # 跳过从节点和其他角色host, port = parts[1].split(':')masters.append((host, int(port.split('@')[0]))) # 提取端口(忽略集群总线端口)return mastersdef scan_no_ttl_keys(node_host, node_port, password, output_file):"""扫描单个节点,筛选无TTL的KEY并写入文件"""r = redis.StrictRedis(host=node_host, port=node_port, password=password, decode_responses=True)cursor = 0batch_size = 1000 # 每次扫描的KEY数量(可根据节点负载调整)with open(output_file, 'a', encoding='utf-8') as f:while True:# 渐进式扫描cursor, keys = r.scan(cursor, count=batch_size)if not keys:if cursor == 0:break # 扫描结束continue# 批量获取TTL(用pipeline减少网络往返)pipe = r.pipeline()for key in keys:pipe.ttl(key)ttls = pipe.execute()# 筛选TTL=-1的KEYfor key, ttl in zip(keys, ttls):if ttl == -1:f.write(f"{key}\n")# 控制扫描速度,避免压垮节点time.sleep(0.05) # 每批休眠50ms,可根据负载调整if __name__ == "__main__":# 集群信息(替换为实际值)CLUSTER_HOST = "192.168.1.100"CLUSTER_PORT = 6379PASSWORD = "your_redis_password"# 获取主节点masters = get_master_nodes(CLUSTER_HOST, CLUSTER_PORT, PASSWORD)print(f"发现{len(masters)}个主节点,开始扫描...")# 逐个节点处理for i, (host, port) in enumerate(masters):output = f"no_ttl_node_{i}_{host}_{port}.txt"print(f"处理节点 {host}:{port},结果写入 {output}")scan_no_ttl_keys(host, port, PASSWORD, output)
关键优化
- 批量处理:用
pipeline批量执行TTL命令(一次网络请求处理1000个KEY),减少延迟。 - 速率控制:通过
sleep控制扫描频率,避免单个节点CPU/内存过高(根据节点负载调整batch_size和休眠时间)。 - 分文件存储:每个节点的结果写入单独文件,避免单文件过大(1亿KEY可能生成数十GB文件)。
方案二:离线解析RDB文件(无性能影响,适合生产环境)
通过解析各节点的RDB持久化文件获取KEY的TTL信息,完全不影响在线集群,适合对性能敏感的生产环境(数据为RDB生成时的快照,非实时)。
核心原理
Redis的RDB文件记录了所有KEY的元数据(包括过期时间),通过工具解析RDB可直接获取“无TTL的KEY”,无需访问在线集群。
具体步骤
1. 获取各节点的RDB文件
- 对每个主节点执行
BGSAVE生成最新RDB(后台执行,不阻塞服务):redis-cli -h 节点IP -p 端口 -a 密码 BGSAVE - 等待命令返回
OK(表示后台保存开始),通过LASTSAVE确认完成时间:redis-cli -h 节点IP -p 端口 -a 密码 LASTSAVE # 返回Unix时间戳,确认保存完成 - 从节点数据目录(如
/var/lib/redis)复制dump.rdb到本地(注意权限)。
2. 解析RDB文件筛选无TTL的KEY
使用rdbtools工具(Python编写)解析RDB,提取KEY和过期时间。
操作步骤:
-
安装工具:
pip install rdbtools python-lzf # python-lzf用于解压RDB(若启用压缩) -
解析RDB并生成包含过期时间的CSV:
rdb -c memory /path/to/dump.rdb --keys "*" > node_keys.csv- CSV格式说明(关键列):
key:KEY名称。expiry:过期时间(Unix时间戳,无TTL则为空)。
- CSV格式说明(关键列):
-
筛选无TTL的KEY(
expiry为空的行):# 用awk筛选expiry为空的行(根据实际CSV列索引调整,假设第3列是expiry) awk -F ',' '$3 == "" {print $1}' node_keys.csv > no_ttl_keys_node.csv
优势与局限
- 优势:完全不影响在线集群,适合生产环境;解析速度快(1亿KEY的RDB约需几分钟)。
- 局限:数据非实时(取决于RDB生成时间);需获取RDB文件(可能涉及权限和传输)。
总结与选择建议
| 方案 | 实时性 | 对集群影响 | 适用场景 |
|---|---|---|---|
| 在线扫描 | 实时 | 轻微(可控) | 需实时数据,非生产高峰 |
| 离线解析RDB | 快照 | 无 | 生产环境,性能敏感,可接受非实时 |
建议:优先使用离线解析RDB方案,尤其对生产集群;若必须实时数据,选择在线扫描并在低峰期执行,同时严格控制扫描速率。
