在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