一、数据量不大情况下,用keys
Redis的单线程服务模式,命令keys会阻塞正常的业务请求。
如果你一次keys匹配的数量过多或者在del的时候遇到大key,都会直接导致业务的不可用,甚至造成redis宕机的风险。
删除
//删除所有以U:INFO:开头的key
Set<String> keys1 = redisTemplate.keys("U:INFO:" + "*");
if (StringUtil.isNotNull(keys)) {
redisTemplate.delete(keys);
}
查询
// *号 必须要加,否则无法模糊查询
//String prefix = "ofc-pincode-"+ pincode + "-*";
// 批量获取所有的key
Set<String> keys = redisTemplate.keys(prefix);
List<MyObject> myObjectListRedis = redisTemplate.opsForValue().multiGet(keys);
List<MyObject> myObjectList = JSON.parseArray(myObjectListRedis.toString(), MyObject.class);
log.info("myObjectList:{}",myObjectList.toString());
二、数据量大情况下,用scan
类似的SCAN命令,对于Redis不同的数据类型还有另外几个SSCAN、HSCAN和ZSCAN,使用方法类似。
案例1
/**
* redis扩展工具
*/
public abstract class RedisHelper {
private static Logger logger = LoggerFactory.getLogger(RedisHelper.class);
/**
* scan 实现
* @param redisTemplate redisTemplate
* @param pattern 表达式,如:abc*,找出所有以abc开始的键
*/
public static Set<String> scan(RedisTemplate<String, Object> redisTemplate, String pattern) {
return redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
Set<String> keysTmp = new HashSet<>();
try (Cursor<byte[]> cursor = connection.scan(new ScanOptions.ScanOptionsBuilder()
.match(pattern)
.count(10000).build())) {
while (cursor.hasNext()) {
keysTmp.add(new String(cursor.next(), "Utf-8"));
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
throw new RuntimeException(e);
}
return keysTmp;
});
}
}
SCAN 命令及其相关的 SSCAN 命令、 HSCAN 命令和 ZSCAN 命令都用于增量地迭代 ,它们每次执行都只会返回少量元素,不会阻塞服务器, 所以这些命令可以用于生产环境, 而不会出现像 KEYS 命令、 SMEMBERS 命令带来的问题。
SCAN一样有它自己的问题:
1.因为是分段获取key,所以它会多次请求redis服务器,这样势必取同样的key,scan耗时更长。
2.在对键进行增量式迭代的过程中, 键可能会被修改, 所以增量式迭代命令只能对被返回的元素提供有限的保证。
案例2
/**
* 模糊匹配前缀删除缓存
*
* @param key
* @return
*/
@Override
public Response<Void> fuzzyMatchDel(String key) throws CommonException {
cacheAsyncTask.fuzzyMatchDel(key);
return ResponseUtils.success();
}
/**
* @description: 缓存服务service实现
*/
@Service
@Slf4j
@EnableAsync
public class CacheAsyncTask {
@Value("${component.cache.scanCount:100}")
private Integer scanCount;
@Value("${constant.cache.cacheInEquity:true}")
private Boolean cacheInEquity;
@Value("${component.cache.prefixCacheName:equity}")
private String prefixCacheName;
@Autowired
private StringRedisTemplate stringRedisTemplate;
public static final String SPACER = "::";
public static final String WILDCARD = "*";
@Async("asyncTaskExecutor")
public void fuzzyMatchDel(String key) throws CommonException {
log.info("fuzzyMatchDel删除开始,key前缀:{}", key);
long beginTime = System.currentTimeMillis();
// 校验传入的prefix
this.regularCheck(key);
// 通过spring-redis关于redis:scan命令相关api非阻塞查询缓存结果
Set<String> keySet = stringRedisTemplate.execute((RedisCallback<Set<String>>) connection -> {
Set<String> binaryKeys = new HashSet<>();
ScanOptions scanOptions = new ScanOptions.ScanOptionsBuilder().match(key).count(scanCount).build();
Cursor<byte[]> cursor = connection.scan(scanOptions);
while (cursor.hasNext()) {
binaryKeys.add(RedisSerializer.string().deserialize(cursor.next()));
}
cursor.close();
return binaryKeys;
});
List<String> delFailKeys = new ArrayList<>();
Iterator<String> it = keySet.iterator();
while (it.hasNext()) {
// 当前开发测试环境的redis版本不支持非阻塞删除unlink命令
String keyNext = it.next();
Boolean success = stringRedisTemplate.delete(keyNext);
if (!success) {
delFailKeys.add(key);
}
}
long seconds = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis() - beginTime);
if (delFailKeys.isEmpty()) {
log.info("fuzzyMatchDel删除的keys:{},共{}个key,耗时{}秒", keySet, keySet.size(), seconds);
return;
}
log.info("fuzzyMatchDel删除失败,扫描到的keys:{},删除失败的keys:{},耗时{}秒", keySet, delFailKeys, seconds);
}
/**
* 对传入的key前缀进行校验
*
* @param prefix
* @throws CommonException
*/
private void regularCheck(String prefix) throws CommonException {
if (cacheInEquity && !prefix.startsWith(prefixCacheName + SPACER)) {
// 开启了只能删除权益宝工程创建的缓存
throw ExceptionUtils.create(EquityException.EQUITY_CACHE_PREFIX_ILLEGAL);
}
if (!prefix.endsWith(WILDCARD)) {
// 接口只做模糊匹配删除一类前缀的key
throw ExceptionUtils.create(EquityException.EQUITY_CACHE_PREFIX_ILLEGAL);
}
}
}
参考文章
https://www.cnblogs.com/37Y37/p/11037923.html
https://blog.youkuaiyun.com/qq_42643690/article/details/106234530
https://javazhiyin.blog.youkuaiyun.com/article/details/104890232
https://www.cnblogs.com/thiaoqueen/p/10919538.html