最近接到一个需求,后台管理页面中,提供一个简单的redis操作界面(其实rdm本身很不错,https://github.com/uglide/RedisDesktopManager/releases/ 为啥还要搞这个,rdm应该只是运维人员使用呢,涉及账号密码等权限的事情),不管,看看基本功能如何实现。
第一个需求,列出redis的key,以及能按照pattern查询出key列表。
redis本身提供了info,dbsize,keys *
info可以看到所有库的key数量
dbsize则是当前库key的数量
keys *这种数据量小还可以,大的时候可以直接搞死生产环境。
dbsize和keys *统计的key数可能是不一样的,如果没记错的话,keys *统计的是当前db有效的key,而dbsize统计的是所有未被销毁的key(有效和未被销毁是不一样的,具体可以了解redis的过期策略)
跟现在需求实现还是有点距离。
想要取指定pattern的key的数量?这个功能Redis没有,以后也不会有。
有人在github上向antirez提过这个要求,结果被否决了,理由是:
Hi, KEYS is only intended for debugging since it is O(N) and performs a full keyspace scan. COUNT has the same problem but without the excuse of being useful for debugging... (since you can simply use redis-cli keys ... | grep ...). So feature not accepted. Thanks for your interest.
崩溃ing。。。。
翻阅文档,看到有一个scan命令(增量式迭代命令)。好像能实现功能,先走一把。
public List findKeysForPage(String patternKey, int pageNum, int pageSize) {
ScanOptions options = ScanOptions.scanOptions().match(patternKey).build();
RedisConnectionFactory factory = stringRedisTemplate.getConnectionFactory();
RedisConnection rc = factory.getConnection();
Cursor cursor = rc.scan(options);
List result = new ArrayList(pageSize);
int tmpIndex = 0;
int startIndex = (pageNum - 1) * pageSize;
int end = pageNum * pageSize;
while (cursor.hasNext()) {
if (tmpIndex >= startIndex && tmpIndex < end) {
result.add(new String(cursor.next()));
tmpIndex++;
continue;
}
// 获取到满足条件的数据后,就可以退出了
if(tmpIndex >=end) {
break;
}
tmpIndex++;
cursor.next();
}
try {
// cursor.close();
} catch (Exception e) {
e.printStackTrace();
}
try {
RedisConnectionUtils.releaseConnection(rc, factory);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
1.能分页获取key的列表
2.如果需要获取总数,去掉上面方法中,
if(tmpIndex >=end) {
break;
}
那么最终tmpIndex的值就是总数。
另外:
在spring-redis-data的1.6.0版本之前可能会出现,
java.util.NoSuchElementException] with root cause
java.util.NoSuchElementException
at java.util.Collections$EmptyIterator.next(Collections.java:4189)
at org.springframework.data.redis.core.ScanCursor.moveNext(ScanCursor.java:215)
at org.springframework.data.redis.core.ScanCursor.next(ScanCursor.java:202)
这里有说明:
https://jira.spring.io/browse/DATAREDIS-417
1.6.1之后解决了。
但是,ohmygod,总是有但是:
1.如何输入的patternKey内容返回的结果很多,而且需要得到总数,那么就会遍历几乎所有的key,一般我们线上key的数量在千万级别,那么跑起来的速度很吓人,占用线上资源
2.scan命令本身,因为增量式命令仅仅使用游标来记录迭代状态,同一个元素可能会被返回多次。 处理重复元素的工作交由应用程序负责。比如说, 可以考虑将迭代返回的元素仅仅用于可以安全地重复执行多次的操作上。如果一个元素是在迭代过程中被添加到数据集的, 又或者是在迭代过程中从数据集中被删除的, 那么这个元素可能会被返回, 也可能不会, 这是未定义的(undefined)。
结论:
1.在生产环境使用,要谨慎,最好只是开发人员权限的功能
2.最好不要使用总数,这样相对来说分页获取值,一般不会获取很多页后面的内容