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

[经验分享] REDIS 字典数据结构

[复制链接]

尚未签到

发表于 2015-7-22 12:25:25 | 显示全部楼层 |阅读模式
  对于REDIS来讲  其实就是一个字典结构,key ---->value  就是一个典型的字典结构
  【当然  对于vaule来讲的话,有不同的内存组织结构 这是后话】
试想一个这样的存储场景:
  key:"city"
  value:"beijing"
  如果有若干个这样的键值对,你该怎么去存储它们呢 要保证写入和查询速度非常理想~!
  抛开redis不说,如果你想要存储 快速查找的话, Hash算法是最快的,理想的哈希函数可以带来O(1)的查找速度,你都这样想,那么redis也的确采用这种方法来做~!
  但是HASH算法有2个致命的弱点:1)填充因子不能太满 2)不好的HASH算法可能会导致一个冲突率非常高。
填充因子不能太满
这个理论上一般为0.5左右  过高 就是哈希槽都被塞满了 ,即使在好的哈希分布算法 也无法避免key冲突。
不好的哈希分布算法
  丢到第一个因素来讲, 如果一个不好的哈希分布算法会导致了key分布不均匀,也就是通过哈希函数计算出来的哈希槽都是落在了一个桶里,这样的哈希分布算法是最不理想的,最理想的情况下是 保证每个key都落在不同的哈希槽里【哈希槽>key】
   实际存储的哈希存储设计
      1)一般来讲,哈希分布函数确定后,可调控的因子就是这个填充因子 如果填充因子大于你卡的某个阈值,那么你就要做哈希结构迁移工作,迁移到一个更大的哈希槽中。而对用同用的这种哈希分布 函数,有许多人用各种数学方法计算过,这里也没有深入研究这个分布函数,倒是在这个填充因子上面,卡的阈值是需要仔细思考。
      2) 哈希槽迁移   哈希槽在迁移的过程中,无论是单线程环境还是多线程环境,都会造成一个短暂的停止服务过程。这个对生产环境会造成非常短暂的影响  我个人认为在服务器 特别存储服务器过程中,本来就是面向大量高并发存储,应该可以把哈希槽设置的更加大一些,这样尽可能避免哈希槽的一个迁移。
REDIS哈希存储设计
      前面说到的一些场景是一些哈希存储引擎都会面临到的问题,REDIS的解决方面如下:
      1)代码层面  我觉得REDIS的代码开发者写代码风格真的是太棒了 封装性,易看性都是很值得学习的  一步一步的看看:
      用C写的redis,但是里面有很多STL的那种设计理念: 迭代器  动态内存管理 等
      如果你写一个哈希存储,最基本的几个子数据结构是必须的:
  每个基本的元素
  struct DicElement
{
/* data */
void* key;
void* value;
struct DicElement *next;
};
  哈希槽
  struct DicElement **HASHTABLE[HASHSOLT];
  
   DSC0000.png 这是redis的真实源码,中间用了一个union联合体 要么是指针,要么就是一个64位的数字。
  typedef struct dictht {
      dictEntry **table;      
unsigned long size;     
unsigned long sizemask;
unsigned long used;     
} dictht;
  dictht就是一个完整的哈希槽,这里面记录了table有多少个哈希槽被用了,【used】 已经哈希槽有多少个 【size】
  一般对于静态的哈希存储结构来讲 上面2个数据结构就可以了,但是redis有一个特性:就是支持扩容,动态扩容,和stl的vector的策略是相似的 当达到临界阈值时,就会增加的到一倍。
  真正的dic结果如下:

  •   typedef struct dict {
  •       //这里封装了dic的函数指针结构体 典型的C写法 如果是c++ 就是一个类 更易读
  •       dictType *type;
  •   void *privdata;
  •      //2个字典  一个空 一个是需要写入的
  •      dictht ht[2];      
  •      //如果重新哈希  就是扩容 这个标记位就会改写
  •   int rehashidx;
  •   int iterators;      
  •   } dict;
      rehashidx 表示正在索引的索引值,字典正在赋值的索引号。
  题外话: DSC0001.gif 如果用C++来写  代码片段更加容易看懂。
字典迭代器讨论
  typedef struct dictIterator {
// 正在迭代的字典
    dict *d;               
int table,              // 是哈希表1还是2
        index,              // 迭代那个哈希槽
        safe;              
    dictEntry *entry,       // 现在哈希结点
*nextEntry;   // 后面一个
} dictIterator;
  
  这里的迭代器提出了safe字段:迭代器的安全
  迭代器安全:REDIS不是一次性全部迁移过来的,而是根据时间片来迁移,这样的话也就是如果没有迁移完的话,如果有插入迭代器或者删除迭代器存在的话,可能会导致漏掉或者多复制现象存在。
  这样的话 还是采用最好的战术模式:记录操作这个dic的迭代器数量,只有当全部是安全迭代器时,才可以进行迁移工作。
  在生产环境下,如果是HASHTABLE是多线程的呢? 多个线程进行读和写,可控制性将会变得非常不可控啊~!  而且如果是多线程,一致性怎么能够得到保证呢~!

  •   在每次迁移完  ht会释放内存 然后制空。 没迁移完之前,就会查看2个字典桶。
关于REDIS哈希槽扩容设计
  1) 每次进行add del,lookfor操作时,都会做执行dicRehashStep函数一次,在调用dictRehash(d,1)一次,这里的一就是执行rehashidex那个下一个不为null的值一次,也就是把一个槽给迁移到ht[1]中,只执行一次 也是为了不会让redis出现太长时间的暂停服务而考虑的一种设计。 但是这里的前提就是安全iterator迭代器的数量为0 也就是不包含增 删 改这3个操作的iterator~! 如果含有增,删,改,那么有可能会出现漏掉entry的情况。
   DSC0002.png
  2)这里是提示用多少毫秒作为一个间隔来做rehash操作,也就是把ht[0]迁移到ht[1]上,每次的base值是100,时间是由服务器来控制,这是第2种迁移方式,这种迁移方式每次迁移的槽多,相对来讲所需要的时间更多,所以ms间隔是需要仔细评估,如果没有弄好,会造成一个时间上的空档。
  int dictRehashMilliseconds(dict *d, int ms) {
long long start = timeInMilliseconds();
int rehashes = 0;
while(dictRehash(d,100)) {
        rehashes += 100;
if (timeInMilliseconds()-start > ms) break;
    }
return rehashes;
}
  
  
  

运维网声明 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-89470-1-1.html 上篇帖子: 指令脚本redis线上环境监控脚本(python脚本) 下篇帖子: Redis安装与基本配置
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

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

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

扫描微信二维码查看详情

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


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


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


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



合作伙伴: 青云cloud

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