在Redis KEYS命令的文档 中,有如下一句话:
consider KEYS as a command that should only be used in production environments with extreme care. It may ruin performance when it is executed against large databases.
简而言之,KEYS命令性能很差,尽量不要在生产环境中使用。
不过官方文档也提到了,可以使用SCAN命令
那么,KEYS命令为什么性能差呢?
从server.c 中可以看到,KEYS命令被keysCommand函数 处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 void keysCommand (client *c) { dictIterator *di; dictEntry *de; sds pattern = c->argv[1 ]->ptr; int plen = sdslen(pattern), allkeys; unsigned long numkeys = 0 ; void *replylen = addDeferredMultiBulkLength(c); di = dictGetSafeIterator(c->db->dict); allkeys = (pattern[0 ] == '*' && pattern[1 ] == '0' ); while ((de = dictNext(di)) != NULL ) { sds key = dictGetKey(de); robj *keyobj; if (allkeys || stringmatchlen(pattern,plen,key,sdslen(key),0 )) { keyobj = createStringObject(key,sdslen(key)); if (expireIfNeeded(c->db,keyobj) == 0 ) { addReplyBulk(c,keyobj); numkeys++; } decrRefCount(keyobj); } } dictReleaseIterator(di); setDeferredMultiBulkLength(c,replylen,numkeys); }
db本来就是一个dict,所以直接拿到一个dictIterator di,然后依次遍历,整个KEYS操作的复杂度是O(n),n为redis中key的数量。
这个操作,会阻塞到redis进程的。
可以看到,redis对于KEYS *这种情况做了优化。如果pattern是*,那么直接遍历,而不用判断是否match