【redis源码学习】redis 中的,java面试笔试经典编程题

/* Adds a new item into the stream ‘s’ having the specified number of

  • field-value pairs as specified in ‘numfields’ and stored into ‘argv’.

  • Returns the new entry ID populating the ‘added_id’ structure.

  • If ‘use_id’ is not NULL, the ID is not auto-generated by the function,

  • but instead the passed ID is used to add the new entry. In this case

  • adding the entry may fail as specified later in this comment.

  • The function returns C_OK if the item was added, this is always true

  • if the ID was generated by the function. However the function may return

  • C_ERR if an ID was given via ‘use_id’, but adding it failed since the

  • current top ID is greater or equal. */

int streamAppendItem(stream *s, robj **argv, int64_t numfields, streamID *added_id, streamID *use_id) {

/* Generate the new entry ID. */

streamID id;

if (use_id)

id = *use_id;

else

streamNextID(&s->last_id,&id);

/* Check that the new ID is greater than the last entry ID

  • or return an error. Automatically generated IDs might

  • overflow (and wrap-around) when incrementing the sequence

part. */

if (streamCompareID(&id,&s->last_id) <= 0) return C_ERR;

/* Add the new entry. */

raxIterator ri;

raxStart(&ri,s->rax);

raxSeek(&ri,“$”,NULL,0);

size_t lp_bytes = 0; /* Total bytes in the tail listpack. */

unsigned char lp = NULL; / Tail listpack pointer. */

/* Get a reference to the tail node listpack. */

if (raxNext(&ri)) {

lp = ri.data;

lp_bytes = lpBytes(lp);

}

raxStop(&ri);

/* We have to add the key into the radix tree in lexicographic order,

  • to do so we consider the ID as a single 128 bit number written in

  • big endian, so that the most significant bytes are the first ones. */

uint64_t rax_key[2]; /* Key in the radix tree containing the listpack.*/

streamID master_id; /* ID of the master entry in the listpack. */

/* Create a new listpack and radix tree node if needed. Note that when

  • a new listpack is created, we populate it with a “master entry”. This

  • is just a set of fields that is taken as references in order to compress

  • the stream entries that we’ll add inside the listpack.

  • Note that while we use the first added entry fields to create

  • the master entry, the first added entry is NOT represented in the master

  • entry, which is a stand alone object. But of course, the first entry

  • will compress well because it’s used as reference.

  • The master entry is composed like in the following example:

  • ±------±--------±-----------±--------±-/–±--------±--------±+

  • | count | deleted | num-fields | field_1 | field_2 | … | field_N |0|

  • ±------±--------±-----------±--------±-/–±--------±--------±+

  • count and deleted just represent respectively the total number of

  • entries inside the listpack that are valid, and marked as deleted

  • (deleted flag in the entry flags set). So the total number of items

  • actually inside the listpack (both deleted and not) is count+deleted.

  • The real entries will be encoded with an ID that is just the

  • millisecond and sequence difference compared to the key stored at

  • the radix tree node containing the listpack (delta encoding), and

  • if the fields of the entry are the same as the master entry fields, the

  • entry flags will specify this fact and the entry fields and number

  • of fields will be omitted (see later in the code of this function).

  • The “0” entry at the end is the same as the ‘lp-count’ entry in the

  • regular stream entries (see below), and marks the fact that there are

  • no more entries, when we scan the stream from right to left. */

/* First of all, check if we can append to the current macro node or

  • if we need to switch to the next one. ‘lp’ will be set to NULL if

  • the current node is full. */

if (lp != NULL) {

if (server.stream_node_max_bytes &&

lp_bytes >= server.stream_node_max_bytes)

{

lp = NULL;

} else if (server.stream_node_max_entries) {

int64_t count = lpGetInteger(lpFirst(lp));

if (count >= server.stream_node_max_entries) lp = NULL;

}

}

int flags = STREAM_ITEM_FLAG_NONE;

if (lp == NULL || lp_bytes >= server.stream_node_max_bytes) {

master_id = id;

streamEncodeID(rax_key,&id);

/* Create the listpack having the master entry ID and fields. */

lp = lpNew();

lp = lpAppendInteger(lp,1); /* One item, the one we are adding. */

lp = lpAppendInteger(lp,0); /* Zero deleted so far. */

lp = lpAppendInteger(lp,numfields);

for (int64_t i = 0; i < numfields; i++) {

sds field = argv[i*2]->ptr;

lp = lpAppend(lp,(unsigned char*)field,sdslen(field));

}

lp = lpAppendInteger(lp,0); /* Master entry zero terminator. */

raxInsert(s->rax,(unsigned char*)&rax_key,sizeof(rax_key),lp,NULL);

/* The first entry we insert, has obviously the same fields of the

  • master entry. */

flags |= STREAM_ITEM_FLAG_SAMEFIELDS;

} else {

serverAssert(ri.key_len == sizeof(rax_key));

memcpy(rax_key,ri.key,sizeof(rax_key));

/* Read the master ID from the radix tree key. */

streamDecodeID(rax_key,&master_id);

unsigned char *lp_ele = lpFirst(lp);

/* Update count and skip the deleted fields. */

int64_t count = lpGetInteger(lp_ele);

lp = lpReplaceInteger(lp,&lp_ele,count+1);

lp_ele = lpNext(lp,lp_ele); /* seek deleted. */

lp_ele = lpNext(lp,lp_ele); /* seek master entry num fields. */

/* Check if the entry we are adding, have the same fields

  • as the master entry. */

int64_t master_fields_count = lpGetInteger(lp_ele);

lp_ele = lpNext(lp,lp_ele);

if (numfields == master_fields_count) {

int64_t i;

for (i = 0; i < master_fields_count; i++) {

sds field = argv[i*2]->ptr;

int64_t e_len;

unsigned char buf[LP_INTBUF_SIZE];

unsigned char *e = lpGet(lp_ele,&e_len,buf);

/* Stop if there is a mismatch. */

if (sdslen(field) != (size_t)e_len ||

memcmp(e,field,e_len) != 0) break;

lp_ele = lpNext(lp,lp_ele);

}

/* All fields are the same! We can compress the field names

  • setting a single bit in the flags. */

if (i == master_fields_count) flags |= STREAM_ITEM_FLAG_SAMEFIELDS;

}

}

/* Populate the listpack with the new entry. We use the following

  • encoding:

  • ±----±-------±---------±------±------±/-±------±------±-------+

  • |flags|entry-id|num-fields|field-1|value-1|…|field-N|value-N|lp-count|

  • ±----±-------±---------±------±------±/-±------±------±-------+

  • However if the SAMEFIELD flag is set, we have just to populate

  • the entry with the values, so it becomes:

  • ±----±-------±------±/-±------±-------+

  • |flags|entry-id|value-1|…|value-N|lp-count|

  • ±----±-------±------±/-±------±-------+

  • The entry-id field is actually two separated fields: the ms

  • and seq difference compared to the master entry.

  • The lp-count field is a number that states the number of listpack pieces

  • that compose the entry, so that it’s possible to travel the entry

  • in reverse order: we can just start from the end of the listpack, read

  • the entry, and jump back N times to seek the “flags” field to read

  • the stream full entry. */

lp = lpAppendInteger(lp,flags);

lp = lpAppendInteger(lp,id.ms - master_id.ms);

lp = lpAppendInteger(lp,id.seq - master_id.seq);

if (!(flags & STREAM_ITEM_FLAG_SAMEFIELDS))

lp = lpAppendInteger(lp,numfields);

for (int64_t i = 0; i < numfields; i++) {

sds field = argv[i2]->ptr, value = argv[i2+1]->ptr;

if (!(flags & STREAM_ITEM_FLAG_SAMEFIELDS))

lp = lpAppend(lp,(unsigned char*)field,sdslen(field));

lp = lpAppend(lp,(unsigned char*)value,sdslen(value));

}

/* Compute and store the lp-count field. */

int64_t lp_count = numfields;

lp_count += 3; /* Add the 3 fixed fields flags + ms-diff + seq-diff. */

if (!(flags & STREAM_ITEM_FLAG_SAMEFIELDS)) {

/* If the item is not compressed, it also has the fields other than

  • the values, and an additional num-fileds field. */

lp_count += numfields+1;

}

lp = lpAppendInteger(lp,lp_count);

/* Insert back into the tree in order to update the listpack pointer. */

if (ri.data != lp)

raxInsert(s->rax,(unsigned char*)&rax_key,sizeof(rax_key),lp,NULL);

s->length++;

s->last_id = id;

if (added_id) *added_id = id;

return C_OK;

}

流程简析:

1、获取rax最后一个key所在的节点,由于Rax树是按照消息id的顺序存储的,所以最后一个key节点存储了上一次插入的消息。

2、查看该节点是否可以插入这条新的消息。

3、如果该节点已经不能再插入新的消息(listpack为空或已经到达最大存储值),初始化新建的listpack;如果还可以用,则对比插入的消息与listpack中master消息对应的fields内容是否完全一致,完全一致则表明该消息可以复用master的field。

4、将待插入的消息内容插入到新建的listpack中或者原来的rax的最后一个key节点对应的listpack中。


新增消费组

/* Create a new consumer group in the context of the stream ‘s’, having the

  • specified name and last server ID. If a consumer group with the same name

  • already existed NULL is returned, otherwise the pointer to the consumer

  • group is returned. */

streamCG *streamCreateCG(stream *s, char *name, size_t namelen, streamID *id) {

if (s->cgroups == NULL) s->cgroups = raxNew();

if (raxFind(s->cgroups,(unsigned char*)name,namelen) != raxNotFound)

return NULL;

streamCG *cg = zmalloc(sizeof(*cg));

cg->pel = raxNew();

cg->consumers = raxNew();

cg->last_id = *id;

raxInsert(s->cgroups,(unsigned char*)name,namelen,cg,NULL);

return cg;

}

为消息流新增一个消费组,以消费组的名称为key,该消费组的streamCG结构为value,放入rax中。


删除消息

/* Remove the current entry from the stream: can be called after the

  • GetID() API or after any GetField() call, however we need to iterate

  • a valid entry while calling this function. Moreover the function

  • requires the entry ID we are currently iterating, that was previously

  • returned by GetID().

  • Note that after calling this function, next calls to GetField() can’t

  • be performed: the entry is now deleted. Instead the iterator will

  • automatically re-seek to the next entry, so the caller should continue

  • with GetID(). */

void streamIteratorRemoveEntry(streamIterator *si, streamID *current) {

unsigned char *lp = si->lp;

int64_t aux;

/* We do not really delete the entry here. Instead we mark it as

  • deleted flagging it, and also incrementing the count of the

  • deleted entries in the listpack header.

  • We start flagging: */

int flags = lpGetInteger(si->lp_flags);

flags |= STREAM_ITEM_FLAG_DELETED;

lp = lpReplaceInteger(lp,&si->lp_flags,flags);

/* Change the valid/deleted entries count in the master entry. */

unsigned char *p = lpFirst(lp);

aux = lpGetInteger§;

if (aux == 1) {

/* If this is the last element in the listpack, we can remove the whole

  • node. */

lpFree(lp);

raxRemove(si->stream->rax,si->ri.key,si->ri.key_len,NULL);

} else {

/* In the base case we alter the counters of valid/deleted entries. */

lp = lpReplaceInteger(lp,&p,aux-1);

p = lpNext(lp,p); /* Seek deleted field. */

aux = lpGetInteger§;

lp = lpReplaceInteger(lp,&p,aux+1);

/* Update the listpack with the new pointer. */

if (si->lp != lp)

raxInsert(si->stream->rax,si->ri.key,si->ri.key_len,lp,NULL);

}

/* Update the number of entries counter. */

si->stream->length–;

/* Re-seek the iterator to fix the now messed up state. */

streamID start, end;

if (si->rev) {

streamDecodeID(si->start_key,&start);

end = *current;

} else {

start = *current;

streamDecodeID(si->end_key,&end);

}

streamIteratorStop(si);

streamIteratorStart(si,si->stream,&start,&end,si->rev);

/* TODO: perform a garbage collection here if the ration between

  • deleted and valid goes over a certain limit. */

}

该操作只是设置待移除消息的标志位为已删除,并不会真正删除。只有当一整个listpack都被删除时,才会从rax中释放节点。


裁剪信息流

这是什么意思?举个例子:我只留最近十条信息。就是这个意思。

/* Trim the stream ‘s’ to have no more than maxlen elements, and return the

  • number of elements removed from the stream. The ‘approx’ option, if non-zero,

  • specifies that the trimming must be performed in a approximated way in

  • order to maximize performances. This means that the stream may contain

  • more elements than ‘maxlen’, and elements are only removed if we can remove

  • a whole node of the radix tree. The elements are removed from the head

  • of the stream (older elements).

  • The function may return zero if:

    1. The stream is already shorter or equal to the specified max length.

小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
img

om the head

  • of the stream (older elements).

  • The function may return zero if:

    1. The stream is already shorter or equal to the specified max length.

小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-okdWgFSu-1710901864254)]
[外链图片转存中…(img-VoKE2dxU-1710901864255)]
[外链图片转存中…(img-fdzAD39o-1710901864255)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
[外链图片转存中…(img-I3E0UpMn-1710901864256)]

  • 14
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值