BLPOP:

BLPOP 是阻塞式列表的弹出原语。 它是命令 LPOP 的阻塞版本,这是因为当给定列表内没有任何元素可供弹出的时候,

连接将被 BLPOP 命令阻塞。 当给定多个 key 参数时,按参数 key 的先后顺序依次检查各个列表,弹出第一个非空列表的头元素。

BRPOP:

BRPOP 是一个阻塞的列表弹出原语。 它是 RPOP 的阻塞版本,因为这个命令会在给定list无法弹出任何元素的时候阻塞连接。 该命令会按照给出的 key 顺序查看 list,并在找到的第一个非空 list 的尾部弹出一个元素。

请在 BLPOP 文档 中查看该命令的准确语义,因为 BRPOP 和 BLPOP 基本是完全一样的,除了它们一个是从尾部弹出元素,而另一个是从头部弹出元素。

时间复杂度 :O(1)

  1. 127.0.0.1:6379> rpush order 10001
  2. (integer) 1
  3. 127.0.0.1:6379> rpush order 10002
  4. (integer) 2
  5. 127.0.0.1:6379> rpush order 10003
  6. (integer) 3
  7. 127.0.0.1:6379> rpush order 10004
  8. (integer) 4
  9. 127.0.0.1:6379> rpush order 10005
  10. (integer) 5
  11. 127.0.0.1:6379> rpush order 10006
  12. (integer) 1
  13. 127.0.0.1:6379>
  1. 127.0.0.1:6379> brpop order 0
  2. 1) "order"
  3. 2) "10005"
  4. 127.0.0.1:6379> brpop order 0
  5. 1) "order"
  6. 2) "10004"
  7. 127.0.0.1:6379> brpop order 0
  8. 1) "order"
  9. 2) "10003"
  10. 127.0.0.1:6379> brpop order 0
  11. 1) "order"
  12. 2) "10002"
  13. 127.0.0.1:6379> brpop order 0
  14. 1) "order"
  15. 2) "10001"
  16. 127.0.0.1:6379> brpop order 0
  17. 1) "order"
  18. 2) "10006"
  19. (2765.54s)
  20. 127.0.0.1:6379>

 

 

 

 源码解析

  1. void blpopCommand(client *c) {
  2. blockingPopGenericCommand(c,LIST_HEAD);
  3. }
  4. void brpopCommand(client *c) {
  5. blockingPopGenericCommand(c,LIST_TAIL);
  6. }
  1. blockingPopGenericCommand
  1. /* Blocking RPOP/LPOP */
  2. void blockingPopGenericCommand(client *c, int where) {
  3. robj *o;
  4. mstime_t timeout;
  5. int j;
  6. if (getTimeoutFromObjectOrReply(c,c->argv[c->argc-1],&timeout,UNIT_SECONDS)
  7. != C_OK) return;
  8. for (j = 1; j < c->argc-1; j++) {
  9. o = lookupKeyWrite(c->db,c->argv[j]);
  10. if (o != NULL) {
  11. if (o->type != OBJ_LIST) {
  12. addReply(c,shared.wrongtypeerr);
  13. return;
  14. } else {
  15. if (listTypeLength(o) != 0) {
  16. /* Non empty list, this is like a non normal [LR]POP. */
  17. char *event = (where == LIST_HEAD) ? "lpop" : "rpop";
  18. robj *value = listTypePop(o,where);
  19. serverAssert(value != NULL);
  20. addReplyArrayLen(c,2);
  21. addReplyBulk(c,c->argv[j]);
  22. addReplyBulk(c,value);
  23. decrRefCount(value);
  24. notifyKeyspaceEvent(NOTIFY_LIST,event,
  25. c->argv[j],c->db->id);
  26. if (listTypeLength(o) == 0) {
  27. dbDelete(c->db,c->argv[j]);
  28. notifyKeyspaceEvent(NOTIFY_GENERIC,"del",
  29. c->argv[j],c->db->id);
  30. }
  31. signalModifiedKey(c,c->db,c->argv[j]);
  32. server.dirty++;
  33. /* Replicate it as an [LR]POP instead of B[LR]POP. */
  34. rewriteClientCommandVector(c,2,
  35. (where == LIST_HEAD) ? shared.lpop : shared.rpop,
  36. c->argv[j]);
  37. return;
  38. }
  39. }
  40. }
  41. }
  42. /* If we are inside a MULTI/EXEC and the list is empty the only thing
  43. * we can do is treating it as a timeout (even with timeout 0). */
  44. if (c->flags & CLIENT_MULTI) {
  45. addReplyNullArray(c);
  46. return;
  47. }
  48. /* If the list is empty or the key does not exists we must block */
  49. blockForKeys(c,BLOCKED_LIST,c->argv + 1,c->argc - 2,timeout,NULL,NULL);
  50. }
  1. blockForKeys
  1. void blockForKeys(client *c, int btype, robj **keys, int numkeys, mstime_t timeout, robj *target, streamID *ids) {
  2. dictEntry *de;
  3. list *l;
  4. int j;
  5. c->bpop.timeout = timeout;
  6. c->bpop.target = target;
  7. if (target != NULL) incrRefCount(target);
  8. for (j = 0; j < numkeys; j++) {
  9. /* Allocate our bkinfo structure, associated to each key the client
  10. * is blocked for. */
  11. bkinfo *bki = zmalloc(sizeof(*bki));
  12. if (btype == BLOCKED_STREAM)
  13. bki->stream_id = ids[j];
  14. /* If the key already exists in the dictionary ignore it. */
  15. if (dictAdd(c->bpop.keys,keys[j],bki) != DICT_OK) {
  16. zfree(bki);
  17. continue;
  18. }
  19. incrRefCount(keys[j]);
  20. /* And in the other "side", to map keys -> clients */
  21. de = dictFind(c->db->blocking_keys,keys[j]);
  22. if (de == NULL) {
  23. int retval;
  24. /* For every key we take a list of clients blocked for it */
  25. l = listCreate();
  26. retval = dictAdd(c->db->blocking_keys,keys[j],l);
  27. incrRefCount(keys[j]);
  28. serverAssertWithInfo(c,keys[j],retval == DICT_OK);
  29. } else {
  30. l = dictGetVal(de);
  31. }
  32. listAddNodeTail(l,c);
  33. bki->listnode = listLast(l);
  34. }
  35. blockClient(c,btype);
  36. }
  1. blockClient
  1. void blockClient(client *c, int btype) {
  2. c->flags |= CLIENT_BLOCKED;
  3. c->btype = btype;
  4. server.blocked_clients++;
  5. server.blocked_clients_by_type[btype]++;
  6. addClientToTimeoutTable(c);
  7. }
  1. addClientToTimeoutTable
  1. void addClientToTimeoutTable(client *c) {
  2. if (c->bpop.timeout == 0) return;
  3. uint64_t timeout = c->bpop.timeout;
  4. unsigned char buf[CLIENT_ST_KEYLEN];
  5. encodeTimeoutKey(buf,timeout,c);
  6. if (raxTryInsert(server.clients_timeout_table,buf,sizeof(buf),NULL,NULL))
  7. c->flags |= CLIENT_IN_TO_TABLE;
  8. }

返回列表里的元素的索引 index 存储在 key 里面。 下标是从0开始索引的,所以 0 是表示第一个元素,

1 表示第二个元素,并以此类推。 负数索引用于指定从列表尾部开始索引的元素。在这种方法下,-1 表示最后一个元素,-2 表示倒数第二个元素,并以此往前推。

当 key 位置的值不是一个列表的时候,会返回一个error。

时间复杂度:O(N)

  1. 127.0.0.1:6379> lpush mylist "World" "Hello"
  2. (integer) 2
  3. 127.0.0.1:6379> lindex mylist 0
  4. "Hello"
  5. 127.0.0.1:6379> lindex mylist 1
  6. "World"
  7. 127.0.0.1:6379>

源码解析

  1. void lindexCommand(client *c) {
  2. robj *o = lookupKeyReadOrReply(c,c->argv[1],shared.null[c->resp]);
  3. if (o == NULL || checkType(c,o,OBJ_LIST)) return;
  4. long index;
  5. robj *value = NULL;
  6. if ((getLongFromObjectOrReply(c, c->argv[2], &index, NULL) != C_OK))
  7. return;
  8. if (o->encoding == OBJ_ENCODING_QUICKLIST) {
  9. quicklistEntry entry;
  10. if (quicklistIndex(o->ptr, index, &entry)) {
  11. if (entry.value) {
  12. value = createStringObject((char*)entry.value,entry.sz);
  13. } else {
  14. value = createStringObjectFromLongLong(entry.longval);
  15. }
  16. addReplyBulk(c,value);
  17. decrRefCount(value);
  18. } else {
  19. addReplyNull(c);
  20. }
  21. } else {
  22. serverPanic("Unknown list encoding");
  23. }
  24. }

 

  1. /* Populate 'entry' with the element at the specified zero-based index
  2. * where 0 is the head, 1 is the element next to head
  3. * and so on. Negative integers are used in order to count
  4. * from the tail, -1 is the last element, -2 the penultimate
  5. * and so on. If the index is out of range 0 is returned.
  6. *
  7. * Returns 1 if element found
  8. * Returns 0 if element not found */
  9. int quicklistIndex(const quicklist *quicklist, const long long idx,
  10. quicklistEntry *entry) {
  11. quicklistNode *n;
  12. unsigned long long accum = 0;
  13. unsigned long long index;
  14. int forward = idx < 0 ? 0 : 1; /* < 0 -> reverse, 0+ -> forward */
  15. initEntry(entry);
  16. entry->quicklist = quicklist;
  17. if (!forward) {
  18. index = (-idx) - 1;
  19. n = quicklist->tail;
  20. } else {
  21. index = idx;
  22. n = quicklist->head;
  23. }
  24. if (index >= quicklist->count)
  25. return 0;
  26. while (likely(n)) {
  27. if ((accum + n->count) > index) {
  28. break;
  29. } else {
  30. D("Skipping over (%p) %u at accum %lld", (void *)n, n->count,
  31. accum);
  32. accum += n->count;
  33. n = forward ? n->next : n->prev;
  34. }
  35. }
  36. if (!n)
  37. return 0;
  38. D("Found node: %p at accum %llu, idx %llu, sub+ %llu, sub- %llu", (void *)n,
  39. accum, index, index - accum, (-index) - 1 + accum);
  40. entry->node = n;
  41. if (forward) {
  42. /* forward = normal head-to-tail offset. */
  43. entry->offset = index - accum;
  44. } else {
  45. /* reverse = need negative offset for tail-to-head, so undo
  46. * the result of the original if (index < 0) above. */
  47. entry->offset = (-index) - 1 + accum;
  48. }
  49. quicklistDecompressNodeForUse(entry->node);
  50. entry->zi = ziplistIndex(entry->node->zl, entry->offset);
  51. ziplistGet(entry->zi, &entry->value, &entry->sz, &entry->longval);
  52. /* The caller will use our result, so we don't re-compress here.
  53. * The caller can recompress or delete the node as needed. */
  54. return 1;
  55. }

把 value 插入存于 key 的列表中在基准值 pivot 的前面或后面。

当 key 不存在时,这个list会被看作是空list,任何操作都不会发生。

当 key 存在,但保存的不是一个list的时候,会返回error。

时间复杂度:O(N)

  1. 127.0.0.1:6379> linsert mylist before "World" "There"
  2. (integer) 3
  3. 127.0.0.1:6379> lrange mylist 0 -1
  4. 1) "Hello"
  5. 2) "There"
  6. 3) "World"
  7. 127.0.0.1:6379>

返回存储在 key 的列表里指定范围内的元素。 start 和 end 偏移量都是基于0的下标,即list的第一个元素下标是0(list的表头),第二个元素下标是1,以此类推。

偏移量也可以是负数,表示偏移量是从list尾部开始计数。 例如, -1 表示列表的最后一个元素,-2 是倒数第二个,以此类推。

时间复杂度:O(S+N)

  1. 127.0.0.1:6379> lrange mylist 0 -1
  2. 1) "Hello"
  3. 2) "There"
  4. 3) "World"
  5. 127.0.0.1:6379> lrange mylist 0 1
  6. 1) "Hello"
  7. 2) "There"
  8. 127.0.0.1:6379>

源码解析

  1. void lrangeCommand(client *c) {
  2. robj *o;
  3. long start, end, llen, rangelen;
  4. if ((getLongFromObjectOrReply(c, c->argv[2], &start, NULL) != C_OK) ||
  5. (getLongFromObjectOrReply(c, c->argv[3], &end, NULL) != C_OK)) return;
  6. if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptyarray)) == NULL
  7. || checkType(c,o,OBJ_LIST)) return;
  8. llen = listTypeLength(o);
  9. /* convert negative indexes */
  10. if (start < 0) start = llen+start;
  11. if (end < 0) end = llen+end;
  12. if (start < 0) start = 0;
  13. /* Invariant: start >= 0, so this test will be true when end < 0.
  14. * The range is empty when start > end or start >= length. */
  15. if (start > end || start >= llen) {
  16. addReply(c,shared.emptyarray);
  17. return;
  18. }
  19. if (end >= llen) end = llen-1;
  20. rangelen = (end-start)+1;
  21. /* Return the result in form of a multi-bulk reply */
  22. addReplyArrayLen(c,rangelen);
  23. if (o->encoding == OBJ_ENCODING_QUICKLIST) {
  24. listTypeIterator *iter = listTypeInitIterator(o, start, LIST_TAIL);
  25. while(rangelen--) {
  26. listTypeEntry entry;
  27. listTypeNext(iter, &entry);
  28. quicklistEntry *qe = &entry.entry;
  29. if (qe->value) {
  30. addReplyBulkCBuffer(c,qe->value,qe->sz);
  31. } else {
  32. addReplyBulkLongLong(c,qe->longval);
  33. }
  34. }
  35. listTypeReleaseIterator(iter);
  36. } else {
  37. serverPanic("List encoding is not QUICKLIST!");
  38. }
  39. }

 

版权声明:本文为vic-tory原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/vic-tory/p/13214064.html