redis 删除指定前缀的key(建议收藏)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观

前言

在使用 Redis 时,我们经常需要对存储的键(Key)进行管理,尤其是当系统中存在大量以特定前缀命名的键时,如何高效、安全地删除这些键成为了一个关键问题。例如,在缓存系统中,可能需要定期清理以"cache:product:"为前缀的过期商品数据;在会话管理中,可能需要删除以"user:session:"开头的失效会话信息。

本文将从 Redis 的核心机制出发,结合实际场景,深入讲解如何删除指定前缀的 Key,并提供代码示例和优化建议。通过循序渐进的方式,帮助读者掌握这一实用技能,同时避免常见的性能陷阱。


Redis Key 的命名与管理

Key 的命名规范与作用

Redis 的 Key 是字符串类型,可以包含任意字符,但合理的命名规范能显著提升系统的可维护性。例如:

  • 命名空间分隔:使用冒号(:)或下划线(_)分隔层级,如 "user:id:1001" 或 "logs:error:2023"。
  • 前缀标识:通过前缀标识业务场景,如 "cache:" 表示缓存,"lock:" 表示分布式锁。

这种命名方式类似于图书馆的图书分类系统——通过前缀快速定位到某一类数据,便于批量操作。


Redis 删除 Key 的基础命令

Redis 提供了多个删除 Key 的命令,但直接删除指定前缀的 Key 并非其内置功能。以下是常用命令:
| 命令 | 描述 | 时间复杂度 |
|--------------|-------------------------------|------------|
| DEL key | 删除单个 Key | O(1) |
| KEYS pattern| 根据模式匹配 Key(不推荐生产环境使用) | O(N) |

注意KEYS 命令在生产环境中可能因遍历全量数据导致阻塞,需谨慎使用。


方法一:结合 SCANDEL 命令

SCAN 命令的原理与优势

SCAN 是 Redis 提供的非阻塞遍历 Key 的命令,通过分页方式逐步扫描数据库。其核心参数包括:

  • 游标(cursor):用于记录扫描进度,初始值为 0。
  • 匹配模式(match):支持通配符 *,例如 "cache:*" 匹配所有以 "cache:" 开头的 Key。

类比SCAN 相当于在图书馆中逐层翻阅书架,每次只查看一部分书籍,避免一次性搬空所有书架导致“图书馆瘫痪”。


具体实现步骤

以下是一个 Python 示例,使用 redis-py 库实现删除指定前缀的 Key:

import redis  

def delete_keys_by_prefix(prefix, redis_client):  
    cursor = '0'  
    while cursor != 0:  
        cursor, keys = redis_client.scan(cursor=cursor, match=f"{prefix}*", count=1000)  
        if keys:  
            redis_client.delete(*keys)  
            print(f"Deleted {len(keys)} keys")  

client = redis.Redis(host='localhost', port=6379, db=0)  
delete_keys_by_prefix("cache:", client)  

关键点解析

  1. 分页扫描:通过 count 参数控制每次扫描的 Key 数量(如 1000),避免内存压力过大。
  2. 游标循环:循环执行 SCAN 直到游标返回 0,表示扫描完成。
  3. 批量删除:使用 DEL 命令批量删除匹配的 Key,减少网络开销。

方法二:使用 Lua 脚本实现原子性删除

原子操作的重要性

在高并发场景下,直接通过 SCAN + DEL 可能因网络延迟或中断导致部分 Key 未被删除。此时,可以使用 Lua 脚本将扫描和删除操作封装为原子操作。

类比:原子操作如同在银行取款时,先冻结账户余额再扣款,避免中途被其他操作干扰。


Lua 脚本实现

以下是一个 Lua 脚本示例,通过 redis.call 实现原子删除:

local cursor = "0"  
repeat  
    cursor, keys = redis.call("SCAN", cursor, "MATCH", ARGV[1], "COUNT", 1000)  
    if #keys > 0 then  
        redis.call("DEL", unpack(keys))  
    end  
until tonumber(cursor) == 0  

调用方式(Python 示例):

script = """  
...(上述 Lua 代码)  
"""  
client.eval(script, 0, "cache:*")  # 参数1: 键数,参数2: 匹配模式  

优势

  • 原子性:整个过程在 Redis 服务端执行,避免网络延迟影响。
  • 性能优化:减少客户端与服务端的通信次数。

方法三:结合 KEYS 命令的快速方案(慎用)

KEYS 的适用场景与风险

在测试环境或 Key 数量极小的场景中,可以直接使用 KEYS 命令配合 DEL

keys_to_delete = client.keys("cache:*")  
if keys_to_delete:  
    client.delete(*keys_to_delete)  

风险提示

  • 阻塞问题:当 Key 数量超过万级时,KEYS 会阻塞 Redis 线程,导致服务不可用。
  • 内存消耗:一次性加载大量 Key 到内存可能触发 OOM(内存溢出)。

类比:这就像在繁忙的十字路口一次性暂停所有车辆,虽然能快速清空道路,但会导致整个交通系统瘫痪。


实际案例:清理过期缓存

场景描述

假设我们有一个电商系统,商品详情缓存的 Key 格式为 "product:detail:{id}",需要每天凌晨删除过期缓存。

实现方案

使用 SCAN + DEL 的方式编写定时任务:

from redis import StrictRedis  
import time  

def clean_product_cache():  
    r = StrictRedis(host='127.0.0.1', port=6379, db=0)  
    cursor = '0'  
    while True:  
        cursor, keys = r.scan(cursor=cursor, match='product:detail:*', count=1000)  
        if keys:  
            r.delete(*keys)  
        if cursor == '0':  
            break  
        time.sleep(0.01)  # 避免 CPU 过度占用  

if __name__ == "__main__":  
    clean_product_cache()  

优化点

  • 分批次处理:通过 sleep 控制扫描频率,避免占用过多 CPU 资源。
  • 灵活扩展:可调整 count 参数适应不同数据量。

注意事项与最佳实践

1. 测试环境验证

在生产环境执行删除操作前,务必在测试环境中验证脚本的正确性。例如:

def test_delete(prefix, redis_client):  
    cursor = '0'  
    while cursor != '0':  
        cursor, keys = redis_client.scan(cursor, match=f"{prefix}*", count=1000)  
        print(f"Found {len(keys)} keys: {keys}")  

2. 监控与性能调优

  • 监控扫描进度:通过日志或指标(如每秒删除 Key 数量)监控任务状态。
  • 调整 COUNT 参数:根据服务器性能,合理设置 SCANCOUNT 值。

3. 命名规范的长期价值

  • 避免前缀冲突:确保不同业务模块的 Key 前缀唯一。
  • 版本控制:如 "v1:users:*",便于未来迁移或清理旧版本数据。

结论

删除 Redis 中指定前缀的 Key 是一个需要综合考虑性能、安全性和可维护性的任务。通过 SCAN + DEL 的组合、Lua 脚本的原子操作,以及合理规避 KEYS 的风险,开发者可以高效完成这一操作。

在实际应用中,建议:

  1. 优先使用非阻塞的 SCAN 命令
  2. 在高并发场景中采用 Lua 脚本保证原子性
  3. 通过命名规范减少误删风险

掌握这些方法后,读者可以进一步探索 Redis 的高级功能(如过期策略、集群管理),持续提升分布式系统的优化能力。

最新发布