REDIS 字典数据结构

对于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];

 

360154641这是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结果如下:

  1. typedef struct dict {

  2.     //这里封装了dic的函数指针结构体 典型的C写法 如果是c++ 就是一个类 更易读

  3.     dictType *type;

  4. void *privdata;

  5.    //2个字典  一个空 一个是需要写入的

  6.    dictht ht[2];      

  7.    //如果重新哈希  就是扩容 这个标记位就会改写

  8. int rehashidx;

  9. int iterators;     

  10. } dict;

    rehashidx 表示正在索引的索引值,字典正在赋值的索引号。

题外话:em1如果用C++来写  代码片段更加容易看懂。

字典迭代器讨论

typedef struct dictIterator {
// 正在迭代的字典
    dict *d;               
int table,              // 是哈希表1还是2
        index,              // 迭代那个哈希槽
        safe;             
    dictEntry *entry,       // 现在哈希结点
*nextEntry;   // 后面一个
} dictIterator;

 

这里的迭代器提出了safe字段:迭代器的安全

迭代器安全:REDIS不是一次性全部迁移过来的,而是根据时间片来迁移,这样的话也就是如果没有迁移完的话,如果有插入迭代器或者删除迭代器存在的话,可能会导致漏掉或者多复制现象存在。

这样的话 还是采用最好的战术模式:记录操作这个dic的迭代器数量,只有当全部是安全迭代器时,才可以进行迁移工作。

在生产环境下,如果是HASHTABLE是多线程的呢? 多个线程进行读和写,可控制性将会变得非常不可控啊~!  而且如果是多线程,一致性怎么能够得到保证呢~!

  • 在每次迁移完  ht[i]会释放内存 然后制空。 没迁移完之前,就会查看2个字典桶。

关于REDIS哈希槽扩容设计

1) 每次进行add del,lookfor操作时,都会做执行dicRehashStep函数一次,在调用dictRehash(d,1)一次,这里的一就是执行rehashidex那个下一个不为null的值一次,也就是把一个槽给迁移到ht[1]中,只执行一次 也是为了不会让redis出现太长时间的暂停服务而考虑的一种设计。 但是这里的前提就是安全iterator迭代器的数量为0 也就是不包含增 删 改这3个操作的iterator~! 如果含有增,删,改,那么有可能会出现漏掉entry的情况。

8201846

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;
}

 

 

 

转载于:https://www.cnblogs.com/sfwtoms/p/3946554.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/407264.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JS之返回指定位置字符的charAt方法

作用:charAt() 方法可返回指定位置的字符,返回的字符是长度为 1 的字符串 语法:stringObject.charAt(index) 参数:必需。表示字符串中某个位置的数字,即字符在字符串中的下标 注意:字符串中第一个字符的…

OOB套接字传输实例(达不到预期结果)

参考资料&#xff1a;<<Linux网络编程.pdf>>page119-205 代码本来是全照书上抄的&#xff0c;后来发现编译不成功&#xff0c;所以就稍微改了下。下面是我修改后的代码&#xff1a; server.c // OOB套接字传输服务端(Server.c)#include <stdio.h>#include…

sql,dateadd,datediff

本文转载自MSDN&#xff0c;为了方便访问&#xff0c;谢谢&#xff01;DATEADD (Transact-SQL)http://msdn.microsoft.com/zh-cn/library/ms186819.aspx将指定 number 时间间隔&#xff08;有符号整数&#xff09;与指定 date 的指定 datepart 相加后&#xff0c;返回该 date。…

公众号H5 VUE获取CODE

回调地址设置页面向导&#xff1a;开发>接口权限>网页服务>网页授权>修改。开发的项目需要放到已经解析好服务器域名的服务器下&#xff0c;同时把Mp***.text文件放到服务器根目录下&#xff0c;此时你的服务器必须能联通外网也就是有公网IP,并且80端口是打开的&am…

javascript学习系列(18):数组中的include方法

最好的种树是十年前,其次是现在。歌谣 每天一个前端小知识 提醒你改好好学习了 知乎博主 csdn博主 b站博主 放弃很容易但是坚持一定很酷 我是歌谣 喜欢就一键三连咯 你得点赞是对歌谣最大的鼓励 1前言 在我们的日常开发中 不免会有很多需要处理数据的方法 本节主要说一说…

curPos和tgtPos

curpos tgtpos 乍一看以为是当前位置和目标位置&#xff0c;但在项目里面这两个位置有点坑 当客户端玩家移动或者AI里面的位置&#xff0c;会把获得的位置付给tgtpos 而以前的tgtpos会付给curpos 所以这个tgtpos是当前玩家或者怪物站立的位置&#xff0c;而curpos是上一个位置 …

JS之获取指定位置Unicode的charCodeAt()方法

用法&#xff1a;charCodeAt() 方法可返回指定位置的字符的 Unicode 编码。这个返回值是 0 - 65535 之间的整数 语法&#xff1a;stringObject.charCodeAt(index) 参数&#xff1a;必需。表示字符串中某个位置的数字&#xff0c;即字符在字符串中的下标 注:1&#xff1a;方法…

利用ioctl获取本机指定设备的MAC地址

// 利用ioctl获取本机指定设备的MAC地址#include<stdio.h>#include<string.h>#include<stdlib.h>#include<errno.h>#include<unistd.h>#include<sys/types.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/i…

日本語趣味読み 一 星とり

星とり ある夜のこと、お寺の庭で、小僧さんが、長い竹竿を、あっちこっち振り回りしておりました。 和尚さんがこれを見つけて、 「これこれ、そこで何をしているのじゃ。」 と聞きますと、小僧さんは、 「お空の星がほしくって、打ち落とそうとしているのでございますが、 ひ…

usaco-sprime-superprime-pass

这个题目开始真正用C&#xff0b;&#xff0b;了&#xff0c;因为&#xff0c;数组的分配有限制了&#xff0c;只好用c中的vector: /* ID: qq104801 LANG: C TASK: sprime */#include <iostream> #include <cstdio> #include <string> #include <cstring&…

vue-router使用next()跳转到指定路径时会无限循环

解释1 beforeRouteLeave (to, from, next) {console.log(离开路路由)if(to.fullPath/home){next();}else{next(/home)}这个是组件路由&#xff0c;我想实现的效果是在这个页面点击浏览器的返回按钮后要返回 /home页面而不是上一个页面&#xff0c;上面的代码是没问题的&#x…

JS之按照Unicode返回指定字符串

用法&#xff1a;fromCharCode() 可接受一个指定的 Unicode 值&#xff0c;然后返回一个字符串 语法&#xff1a;String.fromCharCode(numX,numX,…,numX) 参数&#xff1a;必需。一个或多个 Unicode 值&#xff0c;即要创建的字符串中的字符的 Unicode 编码 注意&#xff1…

顺序容器STL::list用法

C Code: // 顺序容器STL::list用法#include<iostream>#include <stdio.h>#include <stdlib.h>#include <list>using namespace std;void appendItems(list<int> &li, int n){for(int i 0; i < n; i){li.push_back(i1);}}void appendIte…

搭建自己的base.js(2)-其他事件方法

获取鼠标按键 // 获取鼠标按键,getButton:function(event) {//DOM,先检测是否支持DOM鼠标事件if(document,implementation.hasFeature("MouseEvents","2.0")) {return event.button; //0主键&#xff0c;1滚轮&#xff0c;2次键} else { //IE8及之前switc…

Linux下的压缩zip,解压缩unzip命令详解及实例

zip all.zip *.jpg #将所有.jpg的文件压缩成一个zip包unzip all.zip #将all.zip中的所有文件解压到当前目录中unzip all.zip -d all #将all.zip 中的所有文件解压到当前目录中的all文件夹中zip -r hy.zip hy #将当前目录下的hy文件夹压缩为hy.zipzip -r hy.zip hy 123.tx…

JSP标签中不要省略引号

<th> 输入po:<input name"po" value"<%po%>"/></th> <th>输入ip:<input name"ip" value"<%ip%>"/></th> 打算出来的界面如图 这个里面如果&#xff0c;"<%ip%>" 不…

JS之返回字符首次出现位置的indexOf

作用&#xff1a;indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置 语法&#xff1a;stringObject.indexOf(searchvalue,fromindex) 参数1&#xff1a;必需。规定需检索的字符串值 参数2&#xff1a;可选的整数参数。规定在字符串中开始检索的位置。它的合法…

vsftpd默认用户名/密码

我的Linux系统是Fedora12&#xff0c;在它上面安装vsftpd的步骤是&#xff1a; yum install vsftpd 安装好后&#xff0c;要启动vsftpd服务&#xff1a;service vsftpd start 停止服务&#xff1a;service vsftpd stop 重启服务&#xff1a;service vsftpd restart 查看状态…