`
yiihsia
  • 浏览: 68353 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

【备份】redis源码分析-如何rehash

阅读更多

原文地址:redis源码分析-如何rehash

dict实现中主要用到如下结构体,其实就是个典型的链式hash。

一个dict会有2个hash table,由dictht结构管理,编号为0和1.

使用是优先使用0号hash table,当空间不足时会调用dictExpand来扩展hash table,此时准备1号hash table用于增量的rehash使用。rehash完成后把0号释放,1号保存到0号。

rehashidx是下一个需要rehash的项在ht[0]中的索引,不需要rehash时置为-1。也就是说-1时,表示不进行rehash。

iterators记录当前dict中的迭代器数,主要是为了避免在有迭代器时rehash,在有迭代器时rehash可能会造成值的丢失或重复,

dictht中的table是一个数组+指针形式的hash表,size表hash数组(桶)的大小,used表示hash表的元素个数,这两个值与rehash、resize过程密切相关。sizemask等于size-1,这是为了方便将hash值映射到数组中。

typedef struct dictEntry {
void *key;
void *val;
struct dictEntry *next;
} dictEntry;
typedef struct dictht {
dictEntry **table;
unsigned long size;//hash桶的个数
unsigned long sizemask;//hash取模的用到
unsigned long used;//元素个数
} dictht;
typedef struct dict {
dictType *type;
void *privdata;
dictht ht[2];
int rehashidx; /* rehashing not in progress if rehashidx == -1 */
int iterators; /* number of iterators currently running */
} dict;
typedef struct dictIterator {
dict *d;
int table;
int index;
dictEntry *entry, *nextEntry;
} dictIterator;

rehash有2种工作模式

lazy rehashing:在每次对dict进行操作的时候执行一个slot的rehash

active rehashing:每100ms里面使用1ms时间进行rehash。

什么时候dict做扩容

在数据插入的时候会调用dictKeyIndex,该方法里会调用_dictExpandIfNeeded,判断dict是否需要rehash,当dict中桶的个数大于元素的个数时,调用dictExpand扩展hash

/* Expand the hash table if needed */
static int _dictExpandIfNeeded(dict *d)
{
/* If the hash table is empty expand it to the intial size,
* if the table is “full” dobule its size. */
if (dictIsRehashing(d)) return DICT_OK;
if (d->ht[0].size == 0)
return dictExpand(d, DICT_HT_INITIAL_SIZE);
if (d->ht[0].used >= d->ht[0].size && dict_can_resize)
return dictExpand(d, ((d->ht[0].size > d->ht[0].used) ?
d->ht[0].size : d->ht[0].used)*2);
return DICT_OK;
}

dictExpand的工作主要是初始化hash表,默认是扩大两倍(并不单纯是桶的两倍),然后赋值给ht[1],然后状态改为rehashing,此时该dict开始rehashing

扩容过程如何进行

rehash主要在dictRehash中完成。先看下什么时候进行rehash。

active rehashing :serverCron中,当没有后台子线程时,会调用incrementallyRehash,最终调用dictRehashMilliseconds。incrementallyRehash的时间较长,rehash的个数也比较多。这里每次执行 1 millisecond rehash 操作;如果未完成 rehash,会在下一个 loop 里面继续执行。

/* Rehash for an amount of time between ms milliseconds and ms+1 milliseconds */

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;

}

lazy rehashing:_dictRehashStep中,也会调用dictRehash,而_dictRehashStep每次仅会rehash一个值从ht[0]到 ht[1],但由于_dictRehashStep是被dictGetRandomKey、dictFind、 dictGenericDelete、dictAdd调用的,因此在每次dict增删查改时都会被调用,这无疑就加快rehash了过程。

我 们再来看看做rehash的方法。dictRehash每次增量rehash n个元素,由于在自动调整大小时已设置好了ht[1]的大小,因此rehash的主要过程就是遍历ht[0],取得key,然后将该key按ht[1]的 桶的大小重新rehash,并在rehash完后将ht[0]指向ht[1],然后将ht[1]清空。在这个过程中rehashidx非常重要,它表示上次rehash时在ht[0]的下标位置。

int dictRehash(dict *d, int n) {

if (!dictIsRehashing(d)) return 0;

while(n–) {

dictEntry *de, *nextde;

/* Check if we already rehashed the whole table… */

if (d->ht[0].used == 0) {

_dictFree(d->ht[0].table);

d->ht[0] = d->ht[1];//1幅值给0

_dictReset(&d->ht[1]);//rehash完成后把0号释放,1号保存到0号

d->rehashidx = -1;

return 0;

}

/* Note that rehashidx can’t overflow as we are sure there are more

* elements because ht[0].used != 0 */

while(d->ht[0].table[d->rehashidx] == NULL) d->rehashidx++;//在当前桶为null,找下一个

de = d->ht[0].table[d->rehashidx];

/* Move all the keys in this bucket from the old to the new hash HT */

while(de) {

unsigned int h;

nextde = de->next;

/* Get the index in the new hash table */

h = dictHashKey(d, de->key) & d->ht[1].sizemask;

de->next = d->ht[1].table[h];

d->ht[1].table[h] = de;

d->ht[0].used–;

d->ht[1].used++;

de = nextde;

}

d->ht[0].table[d->rehashidx] = NULL;//rehash过的ht[0]桶置为null

d->rehashidx++;//偏移+1

}

return 1;

}

可以看到,redis对dict的rehash是分批进行的,这样不会阻塞请求,设计的比较优雅。

但是在调用dictFind的时候,可能需要对两张dict表做查询。唯一的优化判断是,当key在ht[0]不存在且不在rehashing状态时,可以速度返回空。如果在rehashing状态,当在ht[0]没值的时候,还需要在ht[1]里查找。

dictAdd的时候,如果状态是rehashing,则把值插入到ht[1],否则ht[0]

 

分享到:
评论

相关推荐

    redis-5.0.14-1.el7.remi.x86-64.rpm安装包(含有部署手册)

    redis-5.0.14-1.el7.remi.x86_64.rpm安装包(含有部署手册) redis-5.0.14-1.el7.remi.x86_64.rpm安装包(含有部署手册) redis-5.0.14-1.el7.remi.x86_64.rpm安装包(含有部署手册) redis-5.0.14-1.el7.remi.x86_64.rpm...

    redis校验工具redis-full-check

    **Redis 全面检查工具:redis-full-check** Redis 是一款高性能的键值存储系统,广泛应用于缓存、数据库和消息中间件等场景。在实际应用中,为了确保 Redis 的稳定性和数据一致性,需要定期对 Redis 实例进行健康...

    flink-connector-redis-2.10-1.1.5-API文档-中文版.zip

    赠送jar包:flink-connector-redis_2.10-1.1.5.jar; 赠送原API文档:flink-connector-redis_2.10-1.1.5-javadoc.jar; 赠送源代码:flink-connector-redis_2.10-1.1.5-sources.jar; 赠送Maven依赖信息文件:flink-...

    tomcat-redis-session-manager的jar包-包含Tomcat7和Tomcat8

    《深入理解Tomcat-Redis-Session-Manager:在Tomcat7和Tomcat8中的应用》 在现代Web应用程序开发中,session管理是一个至关重要的环节,它涉及到用户会话的持久化和跨请求的数据共享。传统的session管理方式在高...

    tomcat-redis-session-manager源码

    《深入解析Tomcat-Redis-Session-Manager源码》 在现代Web应用中,服务器端会话管理是一个至关重要的部分,特别是在高并发、分布式环境中。Tomcat作为最流行的Java Servlet容器,提供了丰富的功能来支持这一需求。...

    redis-rdb-tools-master的安装与简单使用.zip

    为了方便管理和维护Redis的数据,开发者设计了一系列的工具,其中`redis-rdb-tools`是一个专门用于分析Redis的RDB(持久化文件)的工具集。RDB是Redis默认的持久化方式之一,它会定期将内存中的数据快照保存到磁盘上...

    redis+redis-desktop-manager-0.8.3.3850+笔记

    1. 下载源码包:`redis-2.8.13.tar.gz` 是Redis的源码包,解压后进行编译和安装。 2. 解压:`tar -zxvf redis-2.8.13.tar.gz` 3. 编译:`cd redis-2.8.13`,然后`make` 4. 安装:`sudo make install` 5. 启动Redis...

    Another-Redis-Desktop-Manager.1.6.1

    《Redis桌面管理器Another-Redis-Desktop-Manager详解》 Redis,全称为Remote Dictionary Server,是一种高性能的键值存储系统,常被用作数据库、缓存和消息中间件。其简洁的数据结构和丰富的数据类型使其在分布式...

    redis++使用说明,windows下编译redis-plus-plus

    "Redis++使用说明,windows下编译Redis-Plus-Plus" 在这篇文章中,我们将详细介绍如何在Windows平台下编译Redis++,包括编译hiredis.lib和Win32_Interop.lib静态库文件的过程,然后安装Cmake并编译Redis++,最后...

    redis-stack-server-6.2.6-v7.rhel7.x86-64.tar.gz

    这里的 "redis-stack-server-6.2.6-v7.rhel7.x86-64.tar.gz" 文件是一个针对 Red Hat Enterprise Linux 7 (RHEL7) 平台的 64 位版本的 Redis Stack 6.2.6 包。这个压缩包包含了运行 Redis Stack 所需的所有组件,...

    tomcat-redis-session-manager for tomcat8.5

    压缩文件包括tomcat-redis-session-manager-master-2.0.0.jar、jedis-2.7.3.jar、commons-pool2-2.3.jar三个jar包使用方法请参照https://github.com/jcoleman/tomcat-redis-session-manager。apache-tomcat-8.5.33....

    redis-windows-7.2.4.zip

    - 解压"redis-windows-7.2.4.zip",找到`redis-server.exe`启动文件。 - 运行`redis-server.exe`,默认情况下,Redis监听6379端口。 - 可以通过配置文件`redis.windows.conf`修改默认设置,如端口、内存限制、...

    Another-Redis-Desktop-Manager-v1.5.5 | redis 桌面视图工具 |windows

    《Redis桌面视图工具Another-Redis-Desktop-Manager在Windows环境下的应用详解》 Redis,全称Remote Dictionary Server,是一款开源、高性能的键值存储系统,常被用作数据库、缓存和消息中间件。其丰富的数据结构和...

    Another-Redis-Desktop-Manager.1.5.5

    标题 "Another-Redis-Desktop-Manager.1.5.5" 指向的是一款名为 Another Redis Desktop Manager 的软件,其版本号为 1.5.5。这是一款图形用户界面(GUI)工具,专为管理和操作 Redis 数据库而设计。Redis 是一个开源...

    redis-windows-7.0.10.zip

    Redis的核心组件包括`redis-server.exe`(服务器进程)、`redis-cli.exe`(命令行客户端)以及`redis-benchmark.exe`(性能测试工具)等。用户需要通过`redis-server.exe`启动服务,并通过`redis-cli.exe`进行交互式...

    redis-desktop-manager-0.8.8.384.exe安装包

    关于标签"redis-desktop-ma",这可能是简写的“redis-desktop-manager”,暗示了这个软件的主要功能是作为Redis的桌面管理工具。 至于“新建文件夹”,这可能是压缩包内的一个文件夹名,但没有具体的文件列表,所以...

    tomcat-redis-session-manager-2.0.0.jar

    tomcat-redis-session-manager-2.0.0.jar,可用于Tomcat8下Redis的Session共享,亲测可用,还需要下载另外两个jar包:commons-pool2-2.4.2.jar和jedis-2.9.0.jar,maven仓库有,此处不再上传

    redis-mac-6.2.2

    1. **bin**:这个目录包含了Redis服务器(redis-server)、客户端(redis-cli)以及其他命令行工具,如用于清除Redis实例数据的`redis-check-aof`和`redis-check-rdb`。安装完成后,可以通过这个目录下的可执行文件...

    redis可视化工具redis-desktop-manager-0.8.8.384

    redis可视化工具redis-desktop-manager-0.8.8.384。。。。

    tomcat-redis-session-manager-1.2-tomcat-6.jar

    用于配置 tomcat-redis-session-manager

Global site tag (gtag.js) - Google Analytics