设为首页 收藏本站
查看: 901|回复: 0

[经验分享] Redis 增加intset的降级特性

[复制链接]

尚未签到

发表于 2015-7-21 07:37:16 | 显示全部楼层 |阅读模式
  Redis的数据结构非常丰富,比如实现了内存紧凑型的数据结构:intset、ziplist等. Redis 2.3.16版本只提供了数据升级功能(比如16bit->32bit等)。
  然而这种存在一个问题就,增加1个大数据,然后删除此大数据后,那么可能存在内存浪费现象,比如:ADD N 个16bit的数据,再ADD 1个64bit的数据,
  再删除此64bit的数据,那么浪费的空间大小为:N*(64-16)bits。不知道redis里面为什么没有增加降级功能,而且代码量和复杂度都不高,也许intset的
  使用都是在数据规模小的时候才用吧。所以我增加了降级功能(degrade)。
  思路:类似于升级功能,升级是通过判断添加的数据的编码和intset头中存储的当前编码来判断是否需要升级,因为intset是有序的(小->大),所以此升
  级的Value要么插在contents最后(length),要么在最前(0).
  同样的道理,检测是通过intset里面有多少个64bit的数据和32bit的数据来决策的。比如:intset里面此时有1个64bit的数据,有非0个32bit的数据,要
  删除这个64bit的数据,那么就需要降级。所以在intset的结构里增加num64bit、 num32bit两个即可。(不增加num16bit的字段,不是可以通过length
  算出来,而是根本不需要此字段,减少内存的消耗)。
  修改inset.h和inset.c文件,具体如下:
  1、intset.h
  intset的结构中增加num64bit、 num32bit字段,用来判断是否需要降级.
  而且在intset.c的功能代码中增加了调试日志代码,可通过编译宏来控制。


DSC0000.gif DSC0001.gif


1 #ifndef DEGRADE
2     #define DEGRADE 1
3 #endif
4
5 #ifndef INTSET_DEBUG
6     #define INTSET_DEBUG
7 #endif
8
9 typedef struct intset {
10     uint32_t encoding;
11     uint32_t length;
12 #ifdef DEGRADE
13     uint32_t num64bit;
14     uint32_t num32bit;
15 #endif   
16     int8_t contents[];
17 } intset;
View Code   2、intset.c
  (1)intsetNew
  修改函数,初始化新增字段值0.





1 /* Create an empty intset. */
2 intset *intsetNew(void) {
3     intset *is = zmalloc(sizeof(intset));
4     is->encoding = intrev32ifbe(INTSET_ENC_INT16);
5     is->length = 0;
6 #ifdef DEGRADE
7     is->num32bit = 0;
8     is->num64bit = 0;
9 #endif // DEGRADE
10     return is;
11 }
View Code   
  (2)_intsetSetTypeNum
  新增函数,根据add和remove来增加和减少num64bit、 num32bit值。





1 #ifdef DEGRADE
2 static void _intsetSetTypeNum(intset* is,uint8_t enc,int change)
3 {
4     if(enc == INTSET_ENC_INT32)
5     {
6      is->num32bit = intrev32ifbe(intrev32ifbe(is->num32bit) + change);
7     }
8     else if(enc == INTSET_ENC_INT64)
9     {
10          is->num64bit = intrev32ifbe(intrev32ifbe(is->num64bit) + change);
11     }
12 }
13 #endif
View Code   
  (3)intsetUpgradeAndAdd和intsetAdd
  修改函数,升级增加和普通增加,需要增加num64bit、 num32bit值。





1 /* Upgrades the intset to a larger encoding and inserts the given integer. */
2 static intset *intsetUpgradeAndAdd(intset *is, int64_t value) {
3     uint8_t curenc = intrev32ifbe(is->encoding);
4     uint8_t newenc = _intsetValueEncoding(value);
5     int length = intrev32ifbe(is->length);
6     int prepend = value < 0 ? 1 : 0;
7
8     /* First set new encoding and resize */
9     is->encoding = intrev32ifbe(newenc);
10     is = intsetResize(is,intrev32ifbe(is->length)+1);
11
12     /* Upgrade back-to-front so we don't overwrite values.
13      * Note that the "prepend" variable is used to make sure we have an empty
14      * space at either the beginning or the end of the intset. */
15     while(length--)
16         _intsetSet(is,length+prepend,_intsetGetEncoded(is,length,curenc));
17
18     /* Set the value at the beginning or the end. */
19     if (prepend)
20         _intsetSet(is,0,value);
21     else
22         _intsetSet(is,intrev32ifbe(is->length),value);
23     is->length = intrev32ifbe(intrev32ifbe(is->length)+1);
24 #ifdef DEGRADE
25     _intsetSetTypeNum(is,newenc,1);
26 #endif
27 #ifdef INTSET_DEBUG
28     redisLog(3,"after intsetUpgradeAndAdd func, num:%lld,lenght:%d,32bitnum:%d,64bitnum:%d,encoding:%d\n",
29              value,intrev32ifbe(is->length),intrev32ifbe(is->num32bit),intrev32ifbe(is->num64bit),
30              intrev32ifbe(is->encoding));
31 #endif
32     return is;
33 }
34
35 /* Insert an integer in the intset */
36 intset *intsetAdd(intset *is, int64_t value, uint8_t *success) {
37     uint8_t valenc = _intsetValueEncoding(value);
38     uint32_t pos;
39     if (success) *success = 1;
40
41     /* Upgrade encoding if necessary. If we need to upgrade, we know that
42      * this value should be either appended (if > 0) or prepended (if < 0),
43      * because it lies outside the range of existing values. */
44     if (valenc > intrev32ifbe(is->encoding)) {
45         /* This always succeeds, so we don't need to curry *success. */
46         return intsetUpgradeAndAdd(is,value);
47     } else {
48         /* Abort if the value is already present in the set.
49          * This call will populate "pos" with the right position to insert
50          * the value when it cannot be found. */
51         if (intsetSearch(is,value,&pos)) {
52             if (success) *success = 0;
53             return is;
54         }
55
56         is = intsetResize(is,intrev32ifbe(is->length)+1);
57         if (pos < intrev32ifbe(is->length)) intsetMoveTail(is,pos,pos+1);
58     }
59
60     _intsetSet(is,pos,value);
61     is->length = intrev32ifbe(intrev32ifbe(is->length)+1);
62 #ifdef DEGRADE
63     _intsetSetTypeNum(is,valenc,1);
64 #endif
65
66 #ifdef INTSET_DEBUG
67     redisLog(3,"after intsetAdd func, num:%lld,lenght:%d,32bitnum:%d,64bitnum:%d,encoding:%d\n",
68              value,intrev32ifbe(is->length),intrev32ifbe(is->num32bit),intrev32ifbe(is->num64bit),
69              intrev32ifbe(is->encoding));
70 #endif
71     return is;
72 }
View Code   
  (4)isDegradeAndRemove
  新增函数,判断删除1个数据时,是否需要降级.





1 /**< want to delete value whose type is enc. */
2 static uint8_t isDegradeAndRemove(intset* is,uint64_t value, uint8_t *degradeEnc)
3 {
4
5     uint8_t enc = _intsetValueEncoding(value);
6     if(intrev32ifbe(is->length)>1&&intsetSearch(is,value,NULL))
7     {
8         if((enc == INTSET_ENC_INT64) && (intrev32ifbe(is->num64bit) == 1))
9         {
10             if(intrev32ifbe(is->num32bit)>0)
11             {
12                 if(degradeEnc)*degradeEnc = INTSET_ENC_INT32;
13                 return 1;
14             }
15             else
16             {
17                 if(degradeEnc)*degradeEnc = INTSET_ENC_INT16;
18                 return 1;
19             }
20         }
21         else if((enc == INTSET_ENC_INT32) && (intrev32ifbe(is->num32bit) == 1))
22         {
23             if(intrev32ifbe(is->num64bit) == 0)
24             {
25                 if(degradeEnc)*degradeEnc = INTSET_ENC_INT16;
26                 return 1;
27             }
28         }
29     }
30     return 0;
31 }
View Code   
  (5)intSetDegradeAndRemove
  新增函数,删除降级





1 static intset *intSetDegradeAndRemove(intset *is, int64_t value, uint8_t degradeEnc)
2 {
3     uint8_t curEnc = intrev32ifbe(is->encoding);
4     int length = intrev32ifbe(is->length);
5     uint8_t loop,temp,prepend = value>0?0:1;
6
7     //first:set the encode
8     is->encoding = intrev32ifbe(degradeEnc);
9 #if 0
10     if(!prepend)//delete +
11     {
12         for(loop=0;looplength),intrev32ifbe(is->num32bit),intrev32ifbe(is->num64bit),
36              intrev32ifbe(is->encoding));
37 #endif
38     
39     return is;
40
41 }
View Code   
  (6)intsetRemove
  修改函数,增加了删除降级代码和普通删除操作,需要减少num64bit、 num32bit值。





1 /* Delete integer from intset */
2 intset *intsetRemove(intset *is, int64_t value, int *success) {
3     uint8_t valenc = _intsetValueEncoding(value);
4     uint32_t pos;
5     if (success) *success = 0;
6
7 #ifdef DEGRADE
8     uint8_t degrade;
9     if(isDegradeAndRemove(is,value,&degrade))
10     {
11         if(success) *success = 1;
12         return intSetDegradeAndRemove(is,value,degrade);
13     }
14 #endif
15     if (valenc encoding) && intsetSearch(is,value,&pos)) {
16         uint32_t len = intrev32ifbe(is->length);
17
18         /* We know we can delete */
19         if (success) *success = 1;
20
21         /* Overwrite value with tail and update length */
22         if (pos < (len-1)) intsetMoveTail(is,pos+1,pos);
23         is = intsetResize(is,len-1);
24         is->length = intrev32ifbe(len-1);
25 #ifdef DEGRADE
26         _intsetSetTypeNum(is,valenc,-1);
27 #ifdef INTSET_DEBUG
28         redisLog(3,"after intsetRemove func, num:%lld,lenght:%d,32bitnum:%d,64bitnum:%d,encoding:%d\n",
29              value,intrev32ifbe(is->length),intrev32ifbe(is->num32bit),intrev32ifbe(is->num64bit),
30              intrev32ifbe(is->encoding));
31 #endif
32 #endif   
33     }
34     return is;
35 }
View Code   总结:刚学习redis,发现里面丰富的数据结构深深的吸引住了我,学习intset的时候,发现没有降级功能(当然redis对intset的entry使用做了限制,默认不超过512个),自己就手动加了这个功能。
  也希望对redis的源码感兴趣的同学,能动手实践一下。抽空把在linux下的实验结果贴出来,可以明显看见内存是消耗是减少的,而且代码里面还增加了调试,可以看出数据编码和插入删除数据
  的变化。
  ps:其实5.23号写了这篇文章,今天修改了些内容,重新发布一下。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-88817-1-1.html 上篇帖子: Redis入门 下篇帖子: Redis 经验谈
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表