以下注释的源代码都在memcached项目的assoc.c文件中

 /* how many powers of 2's worth of buckets we use */
unsigned int hashpower = HASHPOWER_DEFAULT; /* 哈希表bucket的级别,(1<<hashpower) == bucket的个数 */ /* Main hash table. This is where we look except during expansion. */
/**主要的哈希表, 用来存储memcached的key-value数据
* 扩容时会将数据暂存在另一个指针,之后重新分配空间,再以bucket为单位
* 将已有的数据迁移到这张表,所以这张表始终代表最新的数据
*/
static item** primary_hashtable = ; /*
* Previous hash table. During expansion, we look here for keys that haven't
* been moved over to the primary yet.
*/
static item** old_hashtable = ; /**原有的哈希表,只有在扩容时才会使用,保存原有的哈希表的数据 */ /* Number of items in the hash table. */
static unsigned int hash_items = ; /** 整个哈希表中item的个数*/ /* Flag: Are we in the middle of expanding now? */
static bool expanding = false; /** 标识是否正在扩容哈希表*/
static bool started_expanding = false; /* 是否已经开始扩容*/ /*
* During expansion we migrate values with bucket granularity; this is how
* far we've gotten so far. Ranges from 0 .. hashsize(hashpower - 1) - 1.
*/
/**
* 在扩容期间,数据的迁移是以bucket为单位进行迁移, expand_bucket表示迁移进行到第几个bucket
*/
static unsigned int expand_bucket = ; /**
* 哈希表的初始化
* 整个哈希表类似于一个二维数组,初始化分配bucket的空间,具体的item直接申请空间链接在bucket子链上(拉链法解决key冲突)
*/
void assoc_init(const int hashtable_init) {
if (hashtable_init) {
hashpower = hashtable_init;
}
/**初始化哈希表的存储空间*/
primary_hashtable = calloc(hashsize(hashpower), sizeof(void *));
if (! primary_hashtable) {
fprintf(stderr, "Failed to init hashtable.\n");
exit(EXIT_FAILURE);
}
STATS_LOCK();
stats.hash_power_level = hashpower;
stats.hash_bytes = hashsize(hashpower) * sizeof(void *);
STATS_UNLOCK();
} /**
* 根据key查找item
* hv 表示key的hash值
*/
item *assoc_find(const char *key, const size_t nkey, const uint32_t hv) {
item *it; //指向所属的bucket地址
unsigned int oldbucket;
//先判断是否正在扩容
if (expanding && //正在扩容时继续判断key所属的bucket是否已经迁移到old_hashtable
(oldbucket = (hv & hashmask(hashpower - ))) >= expand_bucket)
{
it = old_hashtable[oldbucket]; //已经迁移到old_hashtable则在这里查找bucket
} else {
it = primary_hashtable[hv & hashmask(hashpower)];//没有迁移或者尚未迁移所属的bucket
} item *ret = NULL;
int depth = ;
/** 循环比较拉链上的每个item的key值,相等则返回item的引用*/
while (it) {
if ((nkey == it->nkey) && (memcmp(key, ITEM_key(it), nkey) == )) {
ret = it;
break;
}
it = it->h_next;
++depth;
}
MEMCACHED_ASSOC_FIND(key, nkey, depth);
return ret;
}
/* returns the address of the item pointer before the key. if *item == 0,
the item wasn't found */
/**
* 查找item的地址
*/
static item** _hashitem_before (const char *key, const size_t nkey, const uint32_t hv) {
item **pos;
unsigned int oldbucket;
/** 同样是先确定在哪一张表里找*/
if (expanding &&
(oldbucket = (hv & hashmask(hashpower - ))) >= expand_bucket)
{
pos = &old_hashtable[oldbucket];
} else {
pos = &primary_hashtable[hv & hashmask(hashpower)];
} /** */
while (*pos && ((nkey != (*pos)->nkey) || memcmp(key, ITEM_key(*pos), nkey))) {
pos = &(*pos)->h_next;
}
return pos;
} /* grows the hashtable to the next power of 2. */
/** 将已有的哈希表扩容为原来的2倍buckets数量*/
static void assoc_expand(void) {
/** old_hashtable指向已有的primary_hashtable*/
old_hashtable = primary_hashtable; /**重新为 primary_hashtable分配空间*/
primary_hashtable = calloc(hashsize(hashpower + ), sizeof(void *));
if (primary_hashtable) { /** 分配成功*/
if (settings.verbose > )
fprintf(stderr, "Hash table expansion starting\n");
hashpower++;
expanding = true; /** 设置开始扩容标识*/
expand_bucket = ; /** 已迁移的bucket序号*/
STATS_LOCK();
stats.hash_power_level = hashpower;
stats.hash_bytes += hashsize(hashpower) * sizeof(void *);
stats.hash_is_expanding = ;
STATS_UNLOCK();
} else {
primary_hashtable = old_hashtable; /** 分配失败*/
/* Bad news, but we can keep running. */
}
} static void assoc_start_expand(void) {
if (started_expanding)
return;
started_expanding = true;
pthread_cond_signal(&maintenance_cond);
} /* Note: this isn't an assoc_update. The key must not already exist to call this */
/** 插入一个item到哈希表,这里必须保证item->key尚未存在已有的哈希表*/
int assoc_insert(item *it, const uint32_t hv) {
unsigned int oldbucket; // assert(assoc_find(ITEM_key(it), it->nkey) == 0); /* shouldn't have duplicately named things defined */ if (expanding &&
(oldbucket = (hv & hashmask(hashpower - ))) >= expand_bucket)
{ /** 正在扩容且对应的bucket尚未被迁移到primary_hashtable*/
it->h_next = old_hashtable[oldbucket];
old_hashtable[oldbucket] = it;
} else { /** 没有在扩容或者对应的bucket已经被迁移到primary_hashtable*/
it->h_next = primary_hashtable[hv & hashmask(hashpower)];
primary_hashtable[hv & hashmask(hashpower)] = it;
} // 更新hash_item的数量
hash_items++; /** 哈希表item的数量超过bucket数的3分之2, 这里表示只关心由于存储item数量增长必须引起的扩容*/
if (! expanding && hash_items > (hashsize(hashpower) * ) / ) {
assoc_start_expand(); //发送条件变量满足的信号
} MEMCACHED_ASSOC_INSERT(ITEM_key(it), it->nkey, hash_items);
return ;
}
/** 删除一个item*/
void assoc_delete(const char *key, const size_t nkey, const uint32_t hv) {
/** 找到指向item地址的指针*/
item **before = _hashitem_before(key, nkey, hv); if (*before) {
item *nxt;
hash_items--; /** 减少1个 */
/* The DTrace probe cannot be triggered as the last instruction
* due to possible tail-optimization by the compiler
*/
MEMCACHED_ASSOC_DELETE(key, nkey, hash_items);
/** 链表操作删除一个元素*/
nxt = (*before)->h_next;
(*before)->h_next = ; /* probably pointless, but whatever. */
*before = nxt;
return;
}
/* Note: we never actually get here. the callers don't delete things
they can't find. */
assert(*before != );
} /** 标识是否需要执行维护线程主要逻辑*/
static volatile int do_run_maintenance_thread = ; #define DEFAULT_HASH_BULK_MOVE 1
int hash_bulk_move = DEFAULT_HASH_BULK_MOVE; /** 哈希表维护线程的主要逻辑*/
static void *assoc_maintenance_thread(void *arg) { /** 主线程未退出时,这里基本是进入一个无限循环*/
while (do_run_maintenance_thread) {
int ii = ; /* Lock the cache, and bulk move multiple buckets to the new
* hash table. */
/** 获取worker线程共享的item_global_lock锁,批量迁移buckets到新的哈希表*/
item_lock_global();
mutex_lock(&cache_lock); /** 这个循环默认只走一次,主要目的是不想过久的占用item全局锁,影响worker线程工作效率*/
for (ii = ; ii < hash_bulk_move && expanding; ++ii) {
item *it, *next;
int bucket; /** 对bucket上拉链的每一个item进行重新hash到primary_hashtable*/
for (it = old_hashtable[expand_bucket]; NULL != it; it = next) {
next = it->h_next; /** 重新计算所属的bucket*/
bucket = hash(ITEM_key(it), it->nkey) & hashmask(hashpower);
/** 加入到primary_hashtable对应bucket的头部*/
it->h_next = primary_hashtable[bucket];
primary_hashtable[bucket] = it;
} /** 将old_hashtable上已经被迁移的bucket置为NULL*/
old_hashtable[expand_bucket] = NULL; /** 递增迁移的bucket序号*/
expand_bucket++;
/** 判断是否已经迁移完*/
if (expand_bucket == hashsize(hashpower - )) {
expanding = false;
free(old_hashtable); //释放old_hashtable
STATS_LOCK();
stats.hash_bytes -= hashsize(hashpower - ) * sizeof(void *);
stats.hash_is_expanding = ;
STATS_UNLOCK();
if (settings.verbose > )
fprintf(stderr, "Hash table expansion done\n");
}
} /** 释放锁*/
mutex_unlock(&cache_lock);
/** 释放全局锁,这样worker线程才有机会获得锁进而服务用户请求,减少等待时间*/
item_unlock_global(); /** 未进行扩容或者扩容结束*/
if (!expanding) {
/** 通知其他线程使用细粒度的锁,通过线程pipe进行通信*/
/* finished expanding. tell all threads to use fine-grained locks */
switch_item_lock_type(ITEM_LOCK_GRANULAR); /**恢复slabs的自平衡锁,确保哈希表扩容不会与slabs重新分配同时进行*/
slabs_rebalancer_resume(); /**本次扩容完成,等待下一次调用*/
/* We are done expanding.. just wait for next invocation */
mutex_lock(&cache_lock);
started_expanding = false;
/** 刚启动系统时尚未需要进行扩容,线程会阻塞到这里等待线程条件信号*/
/** 等待条件变量满足信号*/
pthread_cond_wait(&maintenance_cond, &cache_lock);
/* Before doing anything, tell threads to use a global lock */
mutex_unlock(&cache_lock); /** 确保slabs没有正在进行重新分配*/
slabs_rebalancer_pause(); /**通过pipe的方式通知worker线程改变使用锁的粒度为全局锁*/
switch_item_lock_type(ITEM_LOCK_GLOBAL);
mutex_lock(&cache_lock);
/** 开始扩容*/
assoc_expand();
mutex_unlock(&cache_lock);
}
}
return NULL;
} static pthread_t maintenance_tid; /** 启动哈希表扩容监听线程*/
int start_assoc_maintenance_thread() {
int ret;
char *env = getenv("MEMCACHED_HASH_BULK_MOVE");
if (env != NULL) {
hash_bulk_move = atoi(env);
if (hash_bulk_move == ) {
hash_bulk_move = DEFAULT_HASH_BULK_MOVE;
}
}
/** 创建线程*/
if ((ret = pthread_create(&maintenance_tid, NULL,
assoc_maintenance_thread, NULL)) != ) {
fprintf(stderr, "Can't create thread: %s\n", strerror(ret));
return -;
}
return ;
} /** 停止扩容线程,基本是在主线程退出时才会被调用*/
void stop_assoc_maintenance_thread() {
mutex_lock(&cache_lock);
do_run_maintenance_thread = ;
pthread_cond_signal(&maintenance_cond);
mutex_unlock(&cache_lock); /* Wait for the maintenance thread to stop */
pthread_join(maintenance_tid, NULL);
}

memcached哈希表操作主要逻辑笔记的更多相关文章

  1. 分布式缓存系统 Memcached 哈希表操作

    memcached 中有两张hash 表,一个是“主hash 表”(primary_hashtable),另外一个是“原hash 表”(old_hashtable).一般情况下都在主表中接受操作,在插 ...

  2. memcached set命令的大致处理逻辑笔记

    这次记录状态机的主要逻辑,跟踪set命令的执行流程,暂不涉及到内存申请这一块,下面内容基本都是代码注释 首先还是补充了解下客户连接在发送数据到数据被处理并返回过程中conn的各种状态的表示 enum ...

  3. 理解Golang哈希表Map的元素

    目录 概述 哈希函数 冲突解决 初始化 结构体 字面量 运行时 操作 访问 写入 扩容 删除 总结 在上一节中我们介绍了 数组和切片的实现原理,这一节会介绍 Golang 中的另一个集合元素 - 哈希 ...

  4. libevent中evmap实现(哈希表)

    libevent中,需要将大量的监听事件event进行归类存放,比如一个文件描述符fd可能对应多个监听事件,对大量的事件event采用监听的所采用的数据结构是event_io_map,其实现通过哈希表 ...

  5. 【Python算法】哈希存储、哈希表、散列表原理

    哈希表的定义: 哈希存储的基本思想是以关键字Key为自变量,通过一定的函数关系(散列函数或哈希函数),计算出对应的函数值(哈希地址),以这个值作为数据元素的地址,并将数据元素存入到相应地址的存储单元中 ...

  6. 第三十四篇 玩转数据结构——哈希表(HashTable)

    1.. 整型哈希函数的设计 小范围正整数直接使用 小范围负整数整体进行偏移 大整数,通常做法是"模一个素数"   2.. 浮点型哈希函数的设计 转成整型进行处理   3.. 字符串 ...

  7. freeswitch APR库哈希表

    概述 freeswitch的核心源代码是基于apr库开发的,在不同的系统上有很好的移植性. 哈希表在开发中应用的非常广泛,主要场景是对查询效率要求较高的逻辑,是典型的空间换时间的数据结构实现. 大多数 ...

  8. [译]聊聊C#中的泛型的使用(新手勿入) Seaching TreeVIew WPF 可编辑树Ztree的使用(包括对后台数据库的增删改查) 字段和属性的区别 C# 遍历Dictionary并修改其中的Value 学习笔记——异步 程序员常说的「哈希表」是个什么鬼?

    [译]聊聊C#中的泛型的使用(新手勿入)   写在前面 今天忙里偷闲在浏览外文的时候看到一篇讲C#中泛型的使用的文章,因此加上本人的理解以及四级没过的英语水平斗胆给大伙进行了翻译,当然在翻译的过程中发 ...

  9. Java基础知识笔记(一:修饰词、向量、哈希表)

    一.Java语言的特点(养成经常查看Java在线帮助文档的习惯) (1)简单性:Java语言是在C和C++计算机语言的基础上进行简化和改进的一种新型计算机语言.它去掉了C和C++最难正确应用的指针和最 ...

随机推荐

  1. python的subprocess的简单使用和注意事项

    subprocess是python在2.4引入的模块, 主要用来替代下面几个模块和方法: os.systemos.spawn*os.popen*popen2.*commands.* 可以参考PEP32 ...

  2. linux常用命令:tar 命令

    通过SSH访问服务器,难免会要用到压缩,解压缩,打包,解包等,这时候tar命令就是是必不可少的一个功能强大的工具.linux中最流行的tar是麻雀虽小,五脏俱全,功能强大. tar 命令可以为linu ...

  3. python选择排序算法总结

    选择排序算法: a=[6,5,4,3,2,1] 算法思路: 第一步:在列表的第一个位置存放此队列的最小值 声明一个变量min_index等于列表的第一个坐标值0 从第一个位置0坐标开始,和它后边所有的 ...

  4. 20165207 Exp1 PC平台逆向破解

    20165207 Exp1 PC平台逆向破解 0.写在最前面 在做三个实验的前两个的时候,我还没有到博客里去看作业的要求.当时我的主机名是kali5207也就是用我的学号命名的,要求的是姓名全拼命名k ...

  5. jenkin环境搭建

      Jenkins是一个用Java编写的开源的持续集成(CI)工具,可持续.自动地构建/测试软件项目,监控一些定时执行的任务.具有开源,支持多平台和插件扩展,安装简单,界面化管理等特点. 1.下载并解 ...

  6. Python入门之面向对象的多态和继承

    本章内容 Python面向对象的多态和继承对比 ========================================= 在OOP程序设计中,当我们定义一个class的时候,可以从某个现有的 ...

  7. JS中怎么调用<%%>的值

    JS中怎么调用<%%>的值 示例代码一: <% String usercode = request.getParameter("usercode");//用req ...

  8. Tomcat上发布webservices的war工程,访问异常404

    Tomcat上发布webservices的war工程,访问异常404 Tomcat部署正常.war导出工程正常.Tomcat自带的工程可以正常访问: 问题: webservices工程访问异常404 ...

  9. mysql与oracle常用函数及数据类型对比00持续补充

    最近在转一个原来使用oracle,改为mysql的系统,有些常用的oracle函数的mysql实现顺便整理了下,主要是系统中涉及到的(其实原来是专门整理过一个详细doc的,只是每次找word麻烦). ...

  10. C#工程详解

    转:https://www.cnblogs.com/zhaoqingqing/p/5468072.html 前言 写这篇文章的目地是为了让更多的小伙伴对VS生成的工程有一个清晰的认识.在开发过程中,为 ...