redis的key操作命令包括exists,del,type,keys,randomkey,rename和renamenx命令.这一组命令均是操作key,因此命令参数是key.
redis exists命令
redis exists命令的格式为exists key
, 判断包含指定key的哈希节点是否存在,如果存在,返回1,否则,返回0. 需要注意的是,如果包含该key的哈希节点存在,但是其value也可能是空的.telnet的模拟操作为:
telnet 10.7.7.132 6379
Trying 10.7.7.132...
Connected to 10.7.7.132.
Escape character is '^]'.
set mykey 7
myvalue
+OK
exists mykey
1
redis exists命令对应的处理函数为existsCommand
, 其实现为(redis.c):
1372 static void existsCommand(redisClient *c) {
1373 dictEntry *de;
1374
1375 de = dictFind(c->dict,c->argv[1]);
1376 if (de == NULL)
1377 addReply(c,shared.zero);
1378 else
1379 addReply(c,shared.one);
1380 }
redis type命令
redis type命令的格式为type key
, 该命令返回key对应的value类型, value类型包括string, list和set.如果包含key的哈希节点不存在,则返回none. telnet的模拟操作为:
telnet 10.7.7.132 6379
Trying 10.7.7.132...
Connected to 10.7.7.132.
Escape character is '^]'.
set mykey 7
myvalue
+OK
type mykey
string
redis type命令对应的处理函数为typeCommand
,其实现为(redis.c):
1485 static void typeCommand(redisClient *c) {
1486 dictEntry *de;
1487 char *type;
1488
1489 de = dictFind(c->dict,c->argv[1]);
1490 if (de == NULL) {
1491 type = "none";
1492 } else {
1493 robj *o = dictGetEntryVal(de);
1494
1495 switch(o->type) {
1496 case REDIS_STRING: type = "string"; break;
1497 case REDIS_LIST: type = "list"; break;
1498 case REDIS_SET: type = "set"; break;
1499 default: type = "unknown"; break;
1500 }
1501 }
1502 addReplySds(c,sdsnew(type));
1503 addReply(c,shared.crlf);
1504 }
Line1489在数据库中查找包含指定key的哈希节点.如果没有找到该节点,返回none字符串,否则,根据value的类型,返回相应的字符串.
redis randomkey命令
redis randomkey命令的格式为randomkey
, 该命令从数据库中返回一个随机key.注意是从该客户端已选择的数据库中.telnet的模拟操作为:
telnet 10.7.7.132 6379
Trying 10.7.7.132...
Connected to 10.7.7.132.
Escape character is '^]'.
set mykey 7
myvalue
+OK
type mykey
string
randomkey
mykey
redis randomkey命令对应的处理函数为randomkeyCommand
,其实现为(redis.c):
1437 static void randomkeyCommand(redisClient *c) {
1438 dictEntry *de;
1439
1440 de = dictGetRandomKey(c->dict);
1441 if (de == NULL) {
1442 addReply(c,shared.crlf);
1443 } else {
1444 addReplySds(c,sdsdup(dictGetEntryKey(de)));
1445 addReply(c,shared.crlf);
1446 }
1447 }
Line1440调用函数dictGetRandomKey
从该客户端已选择的数据库中返回一个随机哈希节点.该函数的实现为(dict.c):
342 /* Return a random entry from the hash table. Useful to
343 * implement randomized algorithms */
344 dictEntry *dictGetRandomKey(dict *ht)
345 {
346 dictEntry *he;
347 unsigned int h;
348 int listlen, listele;
349
350 if (ht->size == 0) return NULL;
351 do {
352 h = random() & ht->sizemask;
353 he = ht->table[h];
354 } while(he == NULL);
355
356 /* Now we found a non empty bucket, but it is a linked
357 * list and we need to get a random element from the list.
358 * The only sane way to do so is to count the element and
359 * select a random index. */
360 listlen = 0;
361 while(he) {
362 he = he->next;
363 listlen++;
364 }
365 listele = random() % listlen;
366 he = ht->table[h];
367 while(listele--) he = he->next;
368 return he;
369 }
Line350如果哈希表为空,返回NULL.Line351:354随机选择哈希表中的一个BUCKET索引,要求其链表非空.Line360:364计算该BUCKET对应的链表中的节点个数.Line365:368在该链表中随机选取一个节点并返回.
回到函数randomkeyCommand
,Line1441:1446如果返回的随机哈希节点为空,则向客户端发送空提示字符串,否则,发送该哈希节点包含的key.
redis rename命令
redis rename命令的格式为rename oldkey newkey
,该命令的含义是将包含oldkey的哈希节点,以newkey重新记录于数据库中,因为key发生了改变,需要基于newkey做映射,并释放oldkey的内存.telnet的模拟操作为:
telnet 10.7.7.132 6379
Trying 10.7.7.132...
Connected to 10.7.7.132.
Escape character is '^]'.
set mykey1 7
myvalue
+OK
rename mykey1 mykey2
+OK
redis rename命令对应的处理函数为renameCommand
,其实现为(redis.c):
1569 static void renameCommand(redisClient *c) {
1570 renameGenericCommand(c,0);
1571 }
函数renameGenericCommand
的实现为(redis.c):
1537 static void renameGenericCommand(redisClient *c, int nx) {
1538 dictEntry *de;
1539 robj *o;
1540
1541 /* To use the same key as src and dst is probably an error */
1542 if (sdscmp(c->argv[1],c->argv[2]) == 0) {
1543 addReplySds(c,sdsnew("-ERR src and dest key are the same\r\n"));
1544 return;
1545 }
1546
1547 de = dictFind(c->dict,c->argv[1]);
1548 if (de == NULL) {
1549 addReplySds(c,sdsnew("-ERR no such key\r\n"));
1550 return;
1551 }
1552 o = dictGetEntryVal(de);
1553 incrRefCount(o);
1554 if (dictAdd(c->dict,c->argv[2],o) == DICT_ERR) {
1555 if (nx) {
1556 decrRefCount(o);
1557 addReplySds(c,sdsnew("-ERR destination key exists\r\n"));
1558 return;
1559 }
1560 dictReplace(c->dict,c->argv[2],o);
1561 } else {
1562 c->argv[2] = NULL;
1563 }
1564 dictDelete(c->dict,c->argv[1]);
1565 server.dirty++;
1566 addReply(c,shared.ok);
1567 }
Line1541:1545如果oldkey和newkey相同,则向客户端发送错误提示字符串.Line1547:1551获得oldkey对应的哈希节点,如果该节点不存在,则向客户端返回错误提示字符串.Line1552:1553获得哈希节点中的robj
对象,并增加其应用计数.Line1554:1563将newkey/value对应的哈希节点存入数据库.Line1560如果newkey已经存在,进行value替换.Line1564调用函数dictDelete
删除oldkey对应的哈希节点,该函数的实现为(dict.c):
248 int dictDelete(dict *ht, const void *key) {
249 return dictGenericDelete(ht,key,0);
250 }
由于传入函数dictGenericDelete
的第三个参数为0, 所以该函数会释放哈希节点的key和value,value的释放函数是基于引用计数,如果robj
对象的引用计数为0,才真正释放robj
对象封装的value.因此可以看到函数renameGenericCommand
中Line15533中增加robj
对象引用计数的含义,因为rename操作只需要释放oldkey而不需要释放value.
回到函数renameGenericCommand
, Line1565:1566更新数据库更新计数,并向客户端返回操作成功提示字符串.
redis renamenx命令
redis renamenx命令的格式为renamenx oldkey newkey
,该命令的含义是如果包含newkey的哈希节点不存在,将包含oldkey的哈希节点,以newkey重新记录于数据库中.telnet的模拟操作为:
telnet 10.7.7.132 6379
Trying 10.7.7.132...
Connected to 10.7.7.132.
Escape character is '^]'.
set mykey1 7
myvalue
+OK
renamenx mykey1 mykey2
+OK
redis renamenx命令对应的处理函数为renamenxCommand
,其实现为(redis.c):
1573 static void renamenxCommand(redisClient *c) {
1574 renameGenericCommand(c,1);
1575 }
和rename命令的区别在于函数renameGenericCommand
Line1555:1559,如果newkey对应的哈希节点已经存在,则向客户端返回错误提示字符串.
redis keys命令
redis keys命令的格式为keys pattern
,该命令从数据库中返回匹配glob格式的key.telnet的模拟操作为:
telnet 10.7.7.132 6379
Trying 10.7.7.132...
Connected to 10.7.7.132.
Escape character is '^]'.
set mykey1 8
myvalue1
+OK
set mykey2 8
myvalue2
+OK
keys mykey*
13
mykey1 mykey2
redis keys命令对应的处理函数为keysCommand
,其实现为(redis.c):
1449 static void keysCommand(redisClient *c) {
1450 dictIterator *di;
1451 dictEntry *de;
1452 sds keys, reply;
1453 sds pattern = c->argv[1];
1454 int plen = sdslen(pattern);
1455
1456 di = dictGetIterator(c->dict);
1457 keys = sdsempty();
1458 while((de = dictNext(di)) != NULL) {
1459 sds key = dictGetEntryKey(de);
1460 if ((pattern[0] == '*' && pattern[1] == '\0') ||
1461 stringmatchlen(pattern,plen,key,sdslen(key),0)) {
1462 keys = sdscatlen(keys,key,sdslen(key));
1463 keys = sdscatlen(keys," ",1);
1464 }
1465 }
1466 dictReleaseIterator(di);
1467 keys = sdstrim(keys," ");
1468 reply = sdscatprintf(sdsempty(),"%lu\r\n",sdslen(keys));
1469 reply = sdscatlen(reply,keys,sdslen(keys));
1470 reply = sdscatlen(reply,"\r\n",2);
1471 sdsfree(keys);
1472 addReplySds(c,reply);
1473 }
Line1453:1454获得模式字符串机器长度.Line1456动态创建一个哈希表遍历器.Line1457创建一个sds
对象,用来容纳匹配的key.Line1458:1465依次遍历哈希表中的节点,如果其key和模式字符串匹配,记录该key.Line1466释放动态创建的哈希表遍历器.Line1467:1472构造响应内容,并发送给客户端.