Chinaunix首页 | 论坛 | 博客
  • 博客访问: 4407
  • 博文数量: 2
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 30
  • 用 户 组: 普通用户
  • 注册时间: 2019-06-03 14:09
文章分类
文章存档

2019年(2)

我的朋友

分类: Oracle

2019-06-03 14:13:43

同时也再次感受到良好的基础是深入学习的前提。特别强调本文档仅用于学习,并非是要修改redis源码。 建议读者阅读本文档时实际动手敲一下代码,然后翻阅下redis源码,debug下redis

本文档分为三大部分:

  • 环境介绍与效果演示

  • redis接收命令到返回数据的执行逻辑

  • 代码实现

文档的重点和难点在第三部分,完全阅读本文档需要读者具备基本的c语言和数据结构知识。

环境介绍和效果演示

环境介绍

redis版本为5.0.3 64 bit
操作系统版本为Ubuntu 18.10 64bit
源码可以用gedit查看 gdb调试
ide 可以用eclipse+CDT

效果演示

本案例实现了一个链表,对应redis的list数据类型,对链表的操作实现了插入、设置某个节点的值、新建节点、获取一定范围内的节点、获取列表长度等.下面表格列出具体实现的命令与对应的redis原生命令

实现命令 redis原生命令 命令含义
myrpush rpush 从尾部插入链表
mylrange lrange 获取范围内值
myrpop rpop 从右侧弹出值
myllen llen 获取list长度
mylset lset 设置某个节点值
myinsert linsert 在指定位置插入值
mylindex lindex 获取指定位置值

实现命令演示:

  1. 127.0.0.1:6379> myrpush mygjw gjw0(integer) 1127.0.0.1:6379> myrpush mygjw gjw1
  2. (integer) 2 127.0.0.1:6379> myrpush mygjw gjw2
  3. (integer) 3 127.0.0.1:6379> mylrange 0 -1 (error) ERR wrong number of arguments for 'mylrange' command 127.0.0.1:6379> mylrange mygjw 0 -1 1) "gjw0" 2) "gjw1" 3) "gjw2" 127.0.0.1:6379> myrpop mygjw "gjw2" 127.0.0.1:6379> mylrange mygjw 0 -1 1) "gjw0" 2) "gjw1" 127.0.0.1:6379> myllen mygjw
  4. (integer) 2 127.0.0.1:6379> mylset mygjw 0 gjw00 OK127.0.0.1:6379> mylset mygjw 1 gjw01
  5. OK127.0.0.1:6379> mylrange mygjw 0 -1 1) "gjw00" 2) "gjw01" 127.0.0.1:6379> mylinsert mygjw 0 gjw0 (integer) 3 127.0.0.1:6379> mylinsert mygjw 1 gjw1
  6. (integer) 4 127.0.0.1:6379> mylrange mygjw 0 -1 1) "gjw0" 2) "gjw1" 3) "gjw00" 4) "gjw01" 127.0.0.1:6379> mylindex mygjw 0 "gjw0" 127.0.0.1:6379> mylindex mygjw 1 "gjw1"

redis接收命令到返回数据的执行逻辑

此部分只选择与本文档相关部分做简单介绍,实际命令执行涉及到保存文件、主备同步、集群分发等等会复杂很多。下面从server.c文件的processCommand函数开始。

640?wx_fmt=png

下面以myrpush命令执行的时序图来说明具体调用的函数及其所在的文件,其他命令执行方式都类似.图中方框中字符串是文件名,所有关于list类型的命令处理函数都在文件t_list.c中,其他类型的命令处理函数需要找找对应的源文件,redis命令非常规范,找起来不难。

640?wx_fmt=png

代码实现与redis源码阅读

代码实现

郑州做无痛人流多少钱:http://jbk.39.net/yiyuanzaixian/sysdfkyy/

1.添加自己的源文件和头文件
mylinkedlist.h、mylinkedlist.h
mylist类型采用双向链表数据结构,数据域采用了哑型指针,方便插入任何类型数据,为了方便代码仅仅实现了char型,node 结构体有个size属性用来存储数据域占用的字节数。

此部分比redis源码实现简单很多,但是如果能看懂这部分代码,也应该可以比较容易的看懂redis源码。redis中list类型数据域是ziplist,默认情况下数据域最大8k字节,ziplist有自己的数据结构,这里列出redis 列表类型处理的相关源文件:quicklist.h、quicklist.c、ziplist.h、ziplist.c
头文件内容如下:

  1. /*
  2. * mylineklist.h
  3. *
  4. * Created on: Dec 29, 2018
  5. * Author: gjw
  6. */typedef void * ADT; typedef const void * CADT; typedef int (*LIDESTROY) (ADT e); typedef void (*LITRVAVEL) (ADT e); struct NODE; typedef struct NODE * PNODE; typedef struct MylinkedListS MylinkedList; typedef MylinkedList * MYLINKEDLIST; MYLINKEDLIST MyCreateList(); void MyAppend(MYLINKEDLIST list,ADT data,int len); int MyInsert(MYLINKEDLIST list,ADT data,unsigned int pos,int len); void MyDelete(MYLINKEDLIST list,unsigned int pos,LIDESTROY destroy); ADT Myrpop(MYLINKEDLIST list,int * sz); int Mylset(MYLINKEDLIST list, ADT data, unsigned int pos,int len); void MyClear(MYLINKEDLIST list,LIDESTROY destroy); int MyLength(MYLINKEDLIST list); PNODE MyIndex(MYLINKEDLIST list,unsigned int index); void MyTraverse(MYLINKEDLIST list,LITRVAVEL litravel); int MyIsEmpty(MYLINKEDLIST list); PNODE MyNext(PNODE node); void getData(PNODE n,char ** data,int * sz);

在本例中并非所有的声明函数都已实现,仅仅实现了上面演示的几个命令,源文件内容如下:

  1. /*
  2. * mylinkedlist.c
  3. *
  4. * Created on: Dec 29, 2018
  5. * Author: gjw
  6. */ #include #include #include #include "mylinkedlist.h"
  7. struct NODE {
  8. ADT data; int size;
  9. PNODE next;
  10. PNODE previous;
  11. } ;
  12. typedef struct NODE NODE;
  13. struct MylinkedListS {
  14. unsigned int len;
  15. PNODE head, tail;
  16. };
  17. MylinkedList* MyCreateList() {
  18. MylinkedList* list = (MylinkedList*) malloc(sizeof(MylinkedList));
  19. PNODE head = (NODE *) malloc(sizeof(NODE));
  20. PNODE tail = (NODE *) malloc(sizeof(NODE)); list->head = head; list->tail = tail; list->tail->next=NULL; list->tail->previous=NULL; list->head->next=NULL; list->head->previous=NULL; list->len=0;
  21. printf("init a list"); return list;
  22. }
  23. void MyAppend(MYLINKEDLIST list, ADT data,int len) { if (list->head->next == NULL) {
  24. MyInsert(list, data, 0,len); return;
  25. }
  26. printf("->");
  27. PNODE ctail = (NODE *) malloc(sizeof(NODE));
  28. ctail->data=(char *)malloc(len);
  29. memcpy(ctail->data,data,len);
  30. ctail->size=len;
  31. ctail->next=NULL;
  32. ctail->previous=list->tail->next; list->tail->next->next = ctail; list->tail->next = ctail; list->len = list->len + 1;
  33. } int MyInsert(MYLINKEDLIST list, ADT data, unsigned int pos,int len) { if ( pos > list->len) return 0;
  34. printf("insert success!\n");
  35. PNODE node = list->head; for (unsigned int i = 0; i < pos; i++) {
  36. node = node->next;
  37. }
  38. PNODE newNode = (NODE *) malloc(sizeof(NODE));
  39. newNode->data=(char *)malloc(len);
  40. memcpy(newNode->data,data,len);
  41. newNode->size=len;
  42. newNode->next = node->next;
  43. newNode->previous=node; if(node->next){
  44. newNode->next->previous=newNode;
  45. }
  46. node->next = newNode; list->tail->next=newNode; list->len = list->len + 1; return 1;
  47. }
  48. ADT Myrpop(MYLINKEDLIST list,int * sz){
  49. PNODE tail=list->tail->next; if(list->len==0){ return NULL;
  50. }
  51. ADT data=tail->data;
  52. *sz=tail->size; list->tail->next=tail->previous;
  53. tail->previous->next=NULL; list->len=list->len-1;
  54. free(tail); return data;
  55. }int MyLength(MYLINKEDLIST list) { return list->len;
  56. } int MyIsEmpty(MYLINKEDLIST list) { return !list->len;
  57. }
  58. void getData(PNODE n,char ** data,int * sz) {
  59. *data=n->data;
  60. *sz=n->size;
  61. }
  62. PNODE MyNext(PNODE node) { return node->next;
  63. }
  64. PNODE MyIndex(MYLINKEDLIST list,unsigned int index){
  65. PNODE n=list->head->next; if(index>=list->len){ return NULL;
  66. } for(int i=0;i
  67. n=n->next;
  68. } return n;
  69. } int Mylset(MYLINKEDLIST list, ADT data, unsigned int pos,int len) {
  70. PNODE node=list->head->next; if(list->len==0){
  71. MyInsert(list,data,pos,len); return 1;
  72. } if(pos>=list->len){ return 0;
  73. } for(int i=0;i
  74. node=node->next;
  75. }
  76. realloc(node->data,len);
  77. node->size=len;
  78. memcpy(node->data,data,len); return 1;
  79. }

2.修改server.h主要是声明自己的类型,添加自定义类型的命令处理函数,加入自定义头文件,添加以下内容 :

server.h #include "mylinkedlist.h" /* mylist ,guojiagnwei add */ #define MYOBJ_LIST 11 /* MYList object. */ void myrpushCommand(client *c); void mylrangeCommand(client *c); void myrpopCommand(client *c); void myllenCommand(client *c); void mylsetCommand(client *c); void mylinsertCommand(client *c); void mylindexCommand(client *c); robj *createMylistObject(void);

3.修改object.c 此文件主要用来操作redis object,添加以下内容

  1. robj *createMylistObject(void) {
  2. MylinkedList* l = MyCreateList(); //quicklist *l = quicklistCreate();
  3. robj *o = createObject(MYOBJ_LIST,l);
  4. o->encoding = OBJ_ENCODING_QUICKLIST; return o;
  5. }

4.修改 t_list.c文件,实现server.h中声明的命令处理函数

  1. //added by guojiangweivoid myrpushCommand(client *c){
  2. int j, pushed = 0; robj *lobj = lookupKeyWrite(c->db,c->argv[1]); for (j = 2; j < c->argc; j++) { if (!lobj) {
  3. lobj = createMylistObject(); dbAdd(c->db,c->argv[1],lobj);
  4. }
  5. MyAppend(lobj->ptr,c->argv[j]->ptr,sdslen(c->argv[j]->ptr));
  6. pushed++;
  7. } addReplyLongLong(c, MyLength(lobj->ptr)); if (pushed) {
  8. char *event = "rpush"; signalModifiedKey(c->db,c->argv[1]); notifyKeyspaceEvent(1,event,c->argv[1],c->db->id);
  9. }
  10. server.dirty += pushed;
  11. }
  12. void mylrangeCommand(client *c){
  13. robj *o;
  14. long start, end, llen, rangelen;
  15. char * data;
  16. int sz; if ((getLongFromObjectOrReply(c, c->argv[2], &start, NULL) != C_OK) ||
  17. (getLongFromObjectOrReply(c, c->argv[3], &end, NULL) != C_OK)) return; if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL
  18. || checkType(c,o,MYOBJ_LIST)) return; llen = MyLength(o->ptr); /* convert negative indexes */
  19. if (start < 0) start = llen+start; if (end < 0) end = llen+end; if (start < 0) start = 0; /* Invariant: start >= 0, so this test will be true when end < 0.
  20. * The range is empty when start > end or start >= length. */
  21. if (start > end || start >= llen) {
  22. addReply(c,shared.emptymultibulk);
  23. return;
  24. } if (end >= llen) end = llen-1;
  25. rangelen = (end-start)+1; /* Return the result in form of a multi-bulk reply */
  26. addReplyMultiBulkLen(c,rangelen); if (o->encoding == OBJ_ENCODING_QUICKLIST) {
  27. PNODE n = MyIndex(o->ptr, start); while(rangelen--) {
  28. getData( n,&data,&sz);
  29. addReplyBulkCBuffer(c,data,sz);
  30. n=MyNext(n);
  31. }
  32. } else {
  33. serverPanic("List encoding is not QUICKLIST!");
  34. }
  35. }
  36. void myrpopCommand(client *c){ robj *o = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk);
  37. int sz; if (o == NULL || checkType(c,o,MYOBJ_LIST)) return; char * data=Myrpop(o->ptr,&sz);
  38. robj *value = createStringObject(data,sz);
  39. free(data); if (value == NULL) {
  40. addReply(c,shared.nullbulk);
  41. } else {
  42. char *event = "rpop";
  43. addReplyBulk(c,value);
  44. decrRefCount(value); notifyKeyspaceEvent(NOTIFY_LIST,event,c->argv[1],c->db->id); if (listTypeLength(o) == 0) {
  45. notifyKeyspaceEvent(NOTIFY_GENERIC,"del", c->argv[1],c->db->id); dbDelete(c->db,c->argv[1]);
  46. } signalModifiedKey(c->db,c->argv[1]);
  47. server.dirty++;
  48. }
  49. }
  50. void myllenCommand(client *c){ robj *o = lookupKeyReadOrReply(c,c->argv[1],shared.czero); if (o == NULL || checkType(c,o,MYOBJ_LIST)) return; addReplyLongLong(c,MyLength(o->ptr));
  51. }
  52. void mylsetCommand(client *c){ robj *o = lookupKeyWriteOrReply(c,c->argv[1],shared.nokeyerr); if (o == NULL || checkType(c,o,MYOBJ_LIST)) return;
  53. long index; robj *value = c->argv[3]; if ((getLongFromObjectOrReply(c, c->argv[2], &index, NULL) != C_OK))
  54. return; if (o->encoding == OBJ_ENCODING_QUICKLIST) {
  55. MYLINKEDLIST ql = o->ptr; int replaced = Mylset(ql,value->ptr, index,sdslen(value->ptr)); if (!replaced) {
  56. addReply(c,shared.outofrangeerr);
  57. } else {
  58. addReply(c,shared.ok); signalModifiedKey(c->db,c->argv[1]); notifyKeyspaceEvent(NOTIFY_LIST,"lset",c->argv[1],c->db->id);
  59. server.dirty++;
  60. }
  61. } else {
  62. serverPanic("Unknown list encoding");
  63. }
  64. }
  65. void mylinsertCommand(client *c){
  66. long index;
  67. robj *subject;
  68. int inserted = 0; if ((getLongFromObjectOrReply(c, c->argv[2], &index, NULL) != C_OK)){
  69. addReply(c,shared.syntaxerr);
  70. return;
  71. } if ((subject = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
  72. checkType(c,subject,MYOBJ_LIST)) return; inserted=MyInsert(subject->ptr,c->argv[3]->ptr,index,sdslen(c->argv[3]->ptr)); if (inserted) { signalModifiedKey(c->db,c->argv[1]);
  73. notifyKeyspaceEvent(NOTIFY_LIST,"linsert", c->argv[1],c->db->id);
  74. server.dirty++;
  75. } else { /* Notify client of a failed insert */
  76. addReply(c,shared.cnegone);
  77. return;
  78. } addReplyLongLong(c,MyLength(subject->ptr));
  79. }
  80. void mylindexCommand(client *c){
  81. long index;
  82. int size; robj *o = lookupKeyWriteOrReply(c,c->argv[1],shared.nokeyerr); if (o == NULL || checkType(c,o,MYOBJ_LIST)) return; if ((getLongFromObjectOrReply(c, c->argv[2], &index, NULL) != C_OK))
  83. return;
  84. PNODE n=MyIndex(o->ptr,index); if(n==NULL){
  85. addReply(c,shared.nullbulk);
  86. return;
  87. }
  88. char *data;
  89. getData( n,&data,&size); if (o->encoding == OBJ_ENCODING_QUICKLIST) { if (data) {
  90. robj *value=createStringObject(data,size);
  91. addReplyBulk(c,value);
  92. decrRefCount(value);
  93. } else {
  94. addReply(c,shared.nullbulk);
  95. }
  96. } else {
  97. serverPanic("Unknown list encoding");
  98. }
  99. }

5.修改server.c 文件,在变量struct redisCommand redisCommandTable[] 中添加自定义类型命令字符串与命令处理函数映射

  1. {"myrpush",myrpushCommand,-3,"wmF",0,NULL,1,1,1,0,0},
  2. {"mylrange",mylrangeCommand,4,"wmF",0,NULL,1,1,1,0,0},
  3. {"myrpop",myrpopCommand,2,"wmF",0,NULL,1,1,1,0,0},
  4. {"myllen",myllenCommand,2,"wmF",0,NULL,1,1,1,0,0},
  5. {"mylset",mylsetCommand,4,"wmF",0,NULL,1,1,1,0,0},
  6. {"mylinsert",mylinsertCommand,4,"wmF",0,NULL,1,1,1,0,0},
  7. {"mylindex",mylindexCommand,3,"r",0,NULL,1,1,1,0,0},

另外还需要编辑下make文件,比较简单这里不单独列出。

redis源码阅读

代码实现部分列出了需要修改的文件和修改的内容,redis源码设计的非常好,命名非常规范,如果读者按照文档所列进行了操作,对redis 源码结构和程序逻辑应该有了一个大致的认识,作者本来打算详细写下redis list类型的源码实现,但是想了想,水平有限,很难做到严谨与通俗,推荐大家看看《redis 设计与实现》这本书吧。个人觉得看源码最主要的是自己修改和不断的debug。



阅读(613) | 评论(0) | 转发(0) |
0

上一篇:没有了

下一篇:MySQL学习笔记(二)函数篇之文本处理函数

给主人留下些什么吧!~~