redis的数据库操作命令包括select命令和move命令.
redis select命令
redis select命令的格式为select index
, 其含义是为当前客户端选择使用哪一个全局数据库.redis服务器默认有16个全局数据库,当新客户端连接到redis服务器时,使用的是第一个(索引为0)全局数据库,更新数据和查询数据都是在其选择的数据库中进行.select命令可以让客户端在全局数据库中切换.telnet的模拟操作为:
telnet 10.7.7.132 6379
Trying 10.7.7.132...
Connected to 10.7.7.132.
Escape character is '^]'.
select 1
+OK
redis select命令对应的处理函数为selectCommand
,其实现为(redis.c):
1427 static void selectCommand(redisClient *c) {
1428 int id = atoi(c->argv[1]);
1429
1430 if (selectDb(c,id) == REDIS_ERR) {
1431 addReplySds(c,"-ERR invalid DB index\r\n");
1432 } else {
1433 addReply(c,shared.ok);
1434 }
1435 }
首先将数据库索引转换为整数,调用函数selectDb
为该客户端选择索引指定的全局数据库,如果索引值无效,向客户端发送错误提示字符串,否则,发送操作成功字符串.函数selectDb
的实现为(redis.c):
926 static int selectDb(redisClient *c, int id) {
927 if (id < 0 || id >= server.dbnum)
928 return REDIS_ERR;
929 c->dict = server.dict[id];
930 return REDIS_OK;
931 }
需要注意的是select命令是和客户端对象绑定的,它只是切换当前客户端的数据库,对于其他已经连接到redis服务器的客户端并没有影响,对于新连接到redis服务器的客户端,默认仍然选择的是第一个全局数据库.
redis move命令
redis move的格式为move key index
,其含义是将当前数据库中包含指定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
move mykey 1
+OK
get mykey
nil
redis move命令对应的处理函数为moveCommand
, 其实现为(redis.c):
1577 static void moveCommand(redisClient *c) {
1578 dictEntry *de;
1579 sds *key;
1580 robj *o;
1581 dict *src, *dst;
1582
1583 /* Obtain source and target DB pointers */
1584 src = c->dict;
1585 if (selectDb(c,atoi(c->argv[2])) == REDIS_ERR) {
1586 addReplySds(c,sdsnew("-ERR target DB out of range\r\n"));
1587 return;
1588 }
1589 dst = c->dict;
1590 c->dict = src;
1591
1592 /* If the user is moving using as target the same
1593 * DB as the source DB it is probably an error. */
1594 if (src == dst) {
1595 addReplySds(c,sdsnew("-ERR source DB is the same as target DB\r\n"));
1596 return;
1597 }
1598
1599 /* Check if the element exists and get a reference */
1600 de = dictFind(c->dict,c->argv[1]);
1601 if (!de) {
1602 addReplySds(c,sdsnew("-ERR no such key\r\n"));
1603 return;
1604 }
1605
1606 /* Try to add the element to the target DB */
1607 key = dictGetEntryKey(de);
1608 o = dictGetEntryVal(de);
1609 if (dictAdd(dst,key,o) == DICT_ERR) {
1610 addReplySds(c,sdsnew("-ERR target DB already contains the moved key\r\n"));
1611 return;
1612 }
1613
1614 /* OK! key moved, free the entry in the source DB */
1615 dictDeleteNoFree(src,c->argv[1]);
1616 server.dirty++;
1617 addReply(c,shared.ok);
1618 }
Line1583:1590获得客户端当前连接的数据库和目标索引对应的数据库.Line1592:1597如果目标数据库和客户端当前连接数据库相同,则向客户端返回错误提示字符串.Line1599:1604检查包含指定key的哈希节点在当前数据库中是否存在,如果不存在,向客户端返回错误提示字符串.Line1606:1612将该哈希节点存储在目标数据库中,这里只是把节点的key进行哈希映射,获得在目标数据库中的BUCKET索引,然后把该节点插入到目标数据库的BUCKET索引的链表中,并不涉及到内存拷贝,全是指针操作.Line1614:1615调用函数dictDeleteNoFree
在当前数据库中(节点被移除的数据库)删除该节点,该函数的实现为(dict.c):
252 int dictDeleteNoFree(dict *ht, const void *key) {
253 return dictGenericDelete(ht,key,1);
254 }
调用函数dictGenericDelete
在数据库中删除该哈希节点,注意传入的第三个参数为1,即不释放该哈希节点中key和value,因该key和value仍然存储于另外一个数据库中.
回到函数moveCommand
, Line1616更新数据库更新次数.最后向客户端发送操作成功提示字符串.