作者:凯哥Java日期:2025年04月11日标签:Redis、批量删除、前缀匹配、性能优化

在 Redis 使用过程中,我们经常会遇到这样的场景:
需要对某一类数据进行清理,例如用户会话、缓存数据等,而这些数据通常以某种前缀命名(如 user:session:*、cache:data:*)。如果直接通过 redisTemplate.delete(key) 删除单个键,需要手动指定所有键,效率极低且不可维护。如果使用 redisTemplate.keys(prefix + "*") 查找并删除,虽然简单,但在键数量庞大的情况下(如百万级),会导致 Redis 服务阻塞,甚至引发宕机风险。因此,我们需要一种高效、安全的方法,能够根据指定前缀批量删除键,同时避免性能问题。
二、解决方案:三种方法详解以下是三种常见的实现方式,分别基于 keys、SCAN 和 Lua 脚本。
方法 1:使用 keys 查找并删除实现代码:
// 获取 RedisTemplate 实例RedisTemplate redisTemplate = ...;// 定义前缀String prefix = "CAR_TERMINAL_LOCATION:"; // 示例前缀// 查找所有匹配的键Set keys = redisTemplate.keys(prefix + "*");// 批量删除if (keys != null && !keys.isEmpty()) { redisTemplate.delete(keys);}原理:
通过 redisTemplate.keys(prefix + "*") 查找所有匹配的键。使用 redisTemplate.delete(keys) 批量删除。优点:
实现简单,代码清晰。适合键数量较少的场景。缺点:
keys 是阻塞操作,会一次性加载所有匹配的键到内存中。当键数量庞大时,可能导致 Redis 服务阻塞,甚至宕机。不适合生产环境的高并发场景。方法 2:使用 SCAN 增量式删除实现代码:
// 获取 RedisTemplate 实例RedisTemplate redisTemplate = ...;// 定义前缀String prefix = "CAR_TERMINAL_LOCATION:"; // 示例前缀// 使用 RedisConnection 执行 SCANredisTemplate.execute((RedisCallback) connection -> { Cursor<byte[]> cursor = connection.scan( ScanOptions.scanOptions().match(prefix + "*").count(1000).build() ); while (cursor.hasNext()) { connection.del(cursor.next()); } return null;});原理:
通过 SCAN 命令增量式遍历匹配的键,避免一次性加载所有键到内存中。使用 connection.del() 逐个删除键。优点:
非阻塞操作,适合处理大量键的场景。不会导致 Redis 服务阻塞,性能更优。适合生产环境的高并发场景。缺点:
实现相对复杂,需要手动管理游标。删除操作是逐个执行的,可能比批量删除稍慢。方法 3:使用 Lua 脚本实现代码:
// Lua 脚本:删除所有匹配指定前缀的键String luaScript = "local keys = redis.call('KEYS', ARGV[1]) " + "for i, key in ipairs(keys) do " + " redis.call('DEL', key) " + "end " + "return #keys";// 定义前缀String prefix = "CAR_TERMINAL_LOCATION:*";// 执行 Lua 脚本Long deletedCount = redisTemplate.execute( (RedisCallback) connection -> connection.eval( luaScript.getBytes(), ReturnType.INTEGER, 0, prefix.getBytes() ));System.out.println("Deleted keys: " + deletedCount);原理:
将删除逻辑封装到 Lua 脚本中,在 Redis 服务器端执行。通过 KEYS 查找所有匹配的键,并逐个删除。优点:
脚本在 Redis 服务器端执行,减少了网络开销。适合需要原子性操作的场景。缺点:
KEYS 命令是阻塞的,可能导致性能问题。Lua 脚本实现复杂,调试困难。不推荐在键数量庞大的场景中使用。三、方法对比:优缺点总结方法
优点
缺点
适用场景
keys
1. 实现简单2. 适合键数量较少的场景
1. 阻塞操作,可能导致 Redis 服务阻塞2. 不适合键数量庞大的场景
小规模数据清理、开发测试环境
SCAN
1. 非阻塞操作,性能更优2. 适合处理大量键的场景
1. 实现复杂,需要手动管理游标2. 删除操作逐个执行,可能稍慢
生产环境、高并发场景
Lua 脚本
1. 脚本在 Redis 服务器端执行,减少网络开销2. 支持原子性操作
1. KEYS 命令阻塞,可能导致性能问题2. Lua 脚本实现复杂,调试困难
需要原子性操作的场景(慎用)
四、总结与建议优先选择 SCAN:在生产环境中,SCAN 是最推荐的方式,因为它非阻塞、性能更优,适合处理大量键的场景。谨慎使用 keys:keys 适合键数量较少的场景,如开发测试环境,但不建议在生产环境中使用。Lua 脚本需谨慎:Lua 脚本适合需要原子性操作的场景,但由于 KEYS 命令的阻塞性,不建议在键数量庞大的场景中使用。优化建议:如果可能,尽量在设计 Redis 键时避免使用全局前缀匹配,而是通过其他方式(如哈希表)管理数据,减少删除操作的复杂性。希望本文能帮助你更好地理解 Redis 批量删除指定前缀键的实现方法,避免性能问题!欢迎在评论区留言讨论,点赞收藏支持!