1. Redis简介
Redis是一个开源的内存数据结构存储系统,可以用作数据库、缓存和消息代理。Redis的设计目标是高性能、易扩展和高可用。由于其快速的I/O操作和完全基于内存的存储方式,Redis在读取速度和写入速度方面都表现出色,并且可以支持多种数据结构,例如字符串、哈希、列表、集合和有序集合等。
2. zmalloc函数的实现原理
zmalloc函数是Redis内部用于内存分配的函数,其在Unix系统上使用了多种不同的方式进行内存分配,例如malloc、posix_memalign和mmap等。这种多样性设计是为了在不同的情况下选择最适合的内存分配方式。
2.1 zmalloc函数的实现过程
zmalloc的具体实现过程主要分为以下几个步骤:
1、如果分配的内存大小小于REDIS_MAX_ALLOC_SIZE(宏定义为1MB),则使用标准的malloc函数进行内存分配。
2、如果分配的内存大小大于等于REDIS_MAX_ALLOC_SIZE,则调用mmap函数对内存进行映射。
3、如果使用mmap函数进行内存分配失败,则使用posix_memalign函数对内存进行分配。
2.2 内存对齐
在使用posix_memalign函数进行内存分配时,Redis代码有一个比较有趣的实现,即使用了pthread_once函数在程序启动时进行内存对齐的处理。这种方式确保了Redis服务器线程在启动时只做一次内存对齐处理,避免了性能上的浪费。其代码实现如下所示:
static void freeMemoryIfNeeded(void);
static pthread_once_t init_memory_once = PTHREAD_ONCE_INIT;
static void init_memory(void) {
/* Call any multiplexing API cleanup/teardown functions */
aeApiFree(g_el);
/* Setup the allocator function pointers */
zmalloc_enable_thread_safeness();
if (!g_config.mem_allocator) {
if (!strcmp(g_config.mem_allocator_name,"libc"))
g_config.mem_allocator = zmalloc_libc_malloc;
else if (!strcmp(g_config.mem_allocator_name,"jemalloc"))
g_config.mem_allocator = zmalloc_jemalloc_malloc;
else if (!strcmp(g_config.mem_allocator_name,"tcmalloc"))
g_config.mem_allocator = zmalloc_tcmalloc_malloc;
else if (!strcmp(g_config.mem_allocator_name,"huge"))
g_config.mem_allocator = zmalloc_huge_malloc;
else {
serverLog(LL_WARNING,"Unknown allocator \"%s\" in mem_allocator_name configuration directive, using libc allocator.", g_config.mem_allocator_name);
g_config.mem_allocator = zmalloc_libc_malloc;
}
}
/* Test the allocator */
void *test_alloc = g_config.mem_allocator(1);
g_config.mem_allocator_free(test_alloc);
/* Install the out-of-memory handler. */
pthread_atfork(NULL,NULL,freeMemoryIfNeeded);
/* Update the peak memory stats. */
mem_alloc_peak();
}
void zmalloc_enable_thread_safeness(void) {
pthread_once(&init_memory_once,init_memory);
}
3. zmalloc函数的应用
除了在Redis服务器本身中使用zmalloc函数外,其还广泛应用于其他开源软件中,例如Nginx和Hiredis等。下面简单介绍一下Hiredis中对zmalloc函数的使用。
在Hiredis中,zmalloc函数主要用于内存分配相关的操作,例如创建redisReply结构体(该结构体用于保存Redis服务器的响应信息)等。其代码实现如下所示:
redisReply *createReplyObject(int type, const void *reply) {
redisReply *r = zmalloc(sizeof(redisReply));
r->type = type;
switch(type) {
case REDIS_REPLY_INTEGER:
r->integer = *(long long*)reply;
break;
case REDIS_REPLY_DOUBLE:
r->doubleval = *(double*)reply;
break;
case REDIS_REPLY_ARRAY:
r->elements = *(int*)reply;
if (r->elements > 0) {
r->element = zmalloc(sizeof(redisReply*)*r->elements);
memset(r->element,0,sizeof(redisReply*)*r->elements);
}
break;
case REDIS_REPLY_STATUS:
case REDIS_REPLY_ERROR:
case REDIS_REPLY_STRING:
if (reply == NULL) {
r->len = 0;
r->str = NULL;
} else {
r->len = strlen(reply);
r->str = zmalloc(r->len+1);
memcpy(r->str,reply,r->len+1);
}
break;
case REDIS_REPLY_NIL:
break;
default:
redisAssert(0);
break;
}
return r;
}
总结
在Redis中,zmalloc函数是非常重要的内存分配函数。作为一个高性能、易扩展和高可用的内存数据结构存储系统,Redis充分利用zmalloc函数在内存分配上的多样性设计,保证了系统在读取速度和写入速度方面都有较好的表现。此外,zmalloc函数也在其他开源软件中得到广泛的应用,例如Nginx和Hiredis等。