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];
这是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 表示正在索引的索引值,字典正在赋值的索引号。
题外话:
如果用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的情况。
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
REDIS 字典数据结构相关推荐
- 你真的懂redis的数据结构了吗?redis内部数据结构和外部数据结构揭秘
Redis有哪些数据结构? 字符串String.字典Hash.列表List.集合Set.有序集合SortedSet. 很多人面试时都遇到过这种场景吧? 其实除了上面的几种常见数据结构,还需要加上数据结 ...
- 将一个键值对添加入一个对象_细品Redis高性能数据结构之hash对象
背景 上一节讲Redis的高性能字符串结构SDS,今天我们来看一下redis的hash对象. Hash对象 简介 redis的hash对象有两种编码(底层实现)方式,字典编码和压缩列表编码.在使用字典 ...
- redis内部数据结构深入浅出
最大感受,无论从设计还是源码,Redis都尽量做到简单,其中运用到的原理也通俗易懂.特别是源码,简洁易读,真正做到clean and clear, 这篇文章以unstable分支的源码为基准,先从大体 ...
- Redis之数据结构底层实现
目录 redis底层数据结构实现 Redis数据结构 String字符串 常用命令 SDS的定义 SDS的好处 应用场景 List列表 常用命令 压缩列表ziplist quicklist 应用场景 ...
- Redis基础数据结构内部实现简单介绍
5种基础数据结构 Redis有5种基础数据结构,分别是:String(字符串),list(列表),hash(字典),set(集合),zset(有序集合),这五种是我们开发种经常用的到的,是Redis种 ...
- 保存到redis的字符串类型出现斜杆_深入浅出Redis:这次从Redis底层数据结构开始...
1.概述 相信使用过Redis 的各位同学都很清楚,Redis 是一个基于键值对(key-value)的分布式存储系统,与Memcached类似,却优于Memcached的一个高性能的key-valu ...
- Redis中数据结构和编码详细图解(应用场景及优缺点)
专业术语 sds:simple dynamic string 简单动态字符串,redis自己开发的一个字符串的抽象类型 embstr:embedded sds string embstr编码的SDS, ...
- Redis小记——数据结构
本文是基于<Redis设计与实现>的读书笔记. 一.命令 1. 开启服务端:redis-server redis.windows.conf 2. 开启服务端:redis-cli 3. 查看 ...
- redis 基础数据结构实现
参考文献 redis数据结构分析 Skip List(跳跃表)原理详解 redis 源码分析之内存布局 Redis 基础数据结构与对象 Redis设计与实现-第7章-压缩列表 在redis中构建了自己 ...
最新文章
- AS1.0(2.0)中的XML示例
- [BZOJ4756] [Usaco2017 Jan]Promotion Counting(线段树合并)
- C# 中Bitmap图像处理含增强对比度的三种方法
- 有什么好办法说服孩子不玩游戏?
- sqlite expert 未找到提供程序。该程序可能未正确安装_SolidWorks2019安装过程中出现常见问题及解决方案...
- 2007年教育学专业基础综合考试大纲(重要部分) ——下载地址
- PipeMapRed.waitOutputThreads(): subprocess failed with code N
- 关于彻底卸载手心输入法的终极操作
- python:talib 计算 SAR
- AR和VR现在还火爆吗?
- 《国际名酒知识与品鉴》学习笔记
- php充值注入,PHPAPP注入第二枚(漏洞打包)
- Henri Bergson and the Perception of Time
- Composer 国内镜像大全(可用镜像列表)
- 信息安全-零信任技术-SDP是什么,SDP可以防御哪些安全威胁
- 全能型终端神器——MobaXterm安装教程
- asp毕业设计——基于asp+access的中学网站设计与实现(毕业论文+程序源码)——中学网站
- MIT公开课: Python 笔记6 二分法,牛顿-拉夫森方法,列表
- android手机 不显示本地视频,Android手机,如何使用VR观看本地视频?
- sping boot集成多数据源的时候会出现 unsatisfied dependency expressed through method
热门文章
- 【英语天天读】第一场雪
- 张宏江:工程水平决定创新能力(转载于百度百科)
- Silverlight 动态调用 WebService
- java/j2ee中文问题终极解决之道
- ES6模块与commonJS模块的差异
- django的contenttype表
- 【Python】list和tuple 区别比较
- 一张图看懂单机/集群/热备/磁盘阵列(RAID)
- Codeforces Round #324 (Div. 2) B. Kolya and Tanya 快速幂
- struts.properties文件