Redis在内存分配和使用统计的技巧有哪些
具体来说就是:
若系统中存在Google的TC_MALLOC库,则使用tc_malloc一族函数代替原本的malloc一族函数。
若当前系统是Mac系统,则使用中的内存分配函数。
其他情况,在每一段分配好的空间前头,同时多分配一个定长的字段,用来记录分配的空间大小。
源代码分别在 config.h 和 zmalloc.c 中:
/* config.h */
#if defined(USE_TCMALLOC)
#include
#if TC_VERSION_MAJOR >
= 1 &
&
TC_VERSION_MINOR >
= 6
#define HAVE_MALLOC_SIZE 1
#define redis_malloc_size(p) tc_malloc_size(p)
#endif
#elif defined(__APPLE__)
#include
#define HAVE_MALLOC_SIZE 1
#define redis_malloc_size(p) malloc_size(p)
#endif
/* zmalloc.c */
#ifdef HAVE_MALLOC_SIZE
#define PREFIX_SIZE (0)
#else
#if defined(__sun)
#define PREFIX_SIZE (sizeof(long long))
#else
#define PREFIX_SIZE (sizeof(size_t))
#endif
#endif
因为 tc_malloc 和 Mac平台下的 malloc 函数族提供了计算已分配空间大小的函数(分别是tc_malloc_size和malloc_size),所以就不需要单独分配一段空间记录大小了。而针对linux和sun平台则要记录分配空间大小。对于linux,使用sizeof(size_t)定长字段记录;
对于sun os,使用sizeof(long long)定长字段记录。也就是上边源码中的 PREFIX_SIZE 宏。
重新表达这句话:
这个记录的作用在于计算当前进程占用了多少内存。在 zmalloc.c 中,有这样一个静态变量:
static size_t used_memory = 0;
它记录了进程当前占用的内存总数。每当要分配内存或是释放内存的时候,都要更新这个变量。因为分配内存的时候,可以明确知道要分配多少内存。但是释放内存的时候,(对于未提供malloc_size函数的平台)仅通过指向要释放内存的指针是不能知道释放的空间到底有多大的。在这种情况下,PREFIX_SIZE所规定的固定长度字段就有了作用,通过其中记录的信息可以得出空间的大小。zmalloc函数如下(去掉无关代码):
void *zmalloc(size_t size) {
void *ptr = malloc(size+PREFIX_SIZE);
if (!ptr) zmalloc_oom(size);
*((size_t*)ptr) = size;
update_zmalloc_stat_alloc(size+PREFIX_SIZE,size);
return (char*)ptr+PREFIX_SIZE;
#endif
}
Redis在内存分配和使用统计方面有哪些技巧
当分配空间时,注意空间大小要加上PREFIX_SIZE。当运行在Mac系统或使用tc_malloc时,PREFIX_SIZE的值为零。接下来会将指针ptr所指向的内存块的前面size_t个字节用于记录该内存块的分配大小。最后返回的是越过记录区的指针。zfree函数类似(去掉无关代码):
void zfree(void *ptr) {
void *realptr;
size_t oldsize;
if (ptr == NULL) return;
realptr = (char*)ptr-PREFIX_SIZE;
oldsize = *((size_t*)realptr);
update_zmalloc_stat_free(oldsize+PREFIX_SIZE);
free(realptr);
#endif
}
先将指针向前移动PREFIX_SIZE,然后取出分配空间时保存的空间长度。最后free整个空间。
update_zmalloc_stat_alloc(__n,__size) 和 update_zmalloc_stat_free(__n) 这两个宏负责在分配内存或是释放内存的时候更新used_memory变量。定义成宏主要是出于效率上的考虑。将其还原为函数,就是下边这个样子:
void update_zmalloc_stat_alloc(__n,__size)
{
do {
size_t _n = (__n);
size_t _stat_slot = (__size <
ZMALLOC_MAX_ALLOC_STAT) ? __size : ZMALLOC_MAX_ALLOC_STAT;
if (_n&
(sizeof(long)-1)) _n += sizeof(long)-(_n&
(sizeof(long)-1));
if (zmalloc_thread_safe) {
pthread_mutex_lock(&
used_memory_mutex);
used_memory += _n;
zmalloc_allocations[_stat_slot]++;
pthread_mutex_unlock(&
used_memory_mutex);
} else {
used_memory += _n;
zmalloc_allocations[_stat_slot]++;
}
} while(0)
}
void update_zmalloc_stat_free(__n)
{
do {
size_t _n = (__n);
if (_n&
(sizeof(long)-1)) _n += sizeof(long)-(_n&
(sizeof(long)-1));
if (zmalloc_thread_safe) {
pthread_mutex_lock(&
used_memory_mutex);
used_memory -= _n;
pthread_mutex_unlock(&
used_memory_mutex);
} else {
used_memory -= _n;
}
} while(0)
}
代码中除了更新used_memory变量外,还有几个要关注的地方:
先对_n的低位向上取整,最后_n变为sizeof(long)的倍数,比如对于32位系统,sizeof(long) == 100(二进制),_n向上取整之后,低两位都变为0。
如果进程中有多个线程存在,则在更新变量的时候要加锁。
在zmalloc函数中还有一个统计量要更新:zmalloc_allocations[]。
在 zmalloc.c 中,zmalloc_allocations是这样定义的:
size_t zmalloc_allocations[ZMALLOC_MAX_ALLOC_STAT+1];
其作用是统计程序分配内存时,对不同大小空间的请求次数。统计的空间范围从1字节到256字节,大于256字节的算为256。统计结果通过调用 zmalloc_allocations_for_size 函数返回:
size_t zmalloc_allocations_for_size(size_t size) {
if (size >
ZMALLOC_MAX_ALLOC_STAT) return 0;
return zmalloc_allocations[size];
}
另一个对内存使用量的统计通过调用 zmalloc_used_memory 函数返回:
size_t zmalloc_used_memory(void) {
size_t um;
if (zmalloc_thread_safe) pthread_mutex_lock(&
used_memory_mutex);
um = used_memory;
if (zmalloc_thread_safe) pthread_mutex_unlock(&
used_memory_mutex);
return um;
}
另外 zmalloc.c 中,还针对不同的系统实现了 zmalloc_get_rss 函数,在linux系统中是通过读取/proc/$pid/stat文件获得系统统计的内存占用量。
Redis是一个非常流行的内存数据库,其出色的性能和高速的响应速度使得越来越多的开发者使用Redis作为应用程序的缓存存储。然而,对于Redis的内存分配和使用,可能有一些开发者仍然存在一些疑问。本文将为大家介绍七个技巧,帮助你更好地管理你的Redis内存。
1. 监控内存的使用情况
Redis自己的monitor命令可以打印出所有处理Redis请求的详细过程,包括内存使用情况,以及处理每个请求时所消耗的时间和资源。在Redis的开发和调试过程中,建议使用monitor命令监控内存的使用情况,以便及时发现内存使用情况异常。
2. 慎用大量内存的资源
由于Redis的内存缓存限制,建议进行内存限制规划,避免Redis单实例使用大量内存资源导致缓存命中率下降。
3. 存储小数据原则
使用Redis存储数据时,应该尽可能的使用小数据,避免存储大数据、大文件等。不仅可以节省内存资源,还可以提高应用程序的性能。
4. 使用Redis中的数据类型
Redis内置了多种数据类型,如字符串、哈希表、列表、集合和有序集合。开发者应该选择合适的数据类型,避免存储大量重复的数据,以便更好地使用内存资源。
5. 合理设置过期时间
使用Redis时,可以通过设置数据的过期时间,让Redis自动清除不需要的数据,避免占用过多的内存空间。合理设置过期时间能有效减少内存占用,提高缓存的利用率。
6. 合理采用持久化机制
Redis提供的持久化机制包括快照和AOF(append-only file),可以将Redis的数据定期或实时写入到硬盘中。通过合理使用持久化机制,可以减少Redis节点崩溃时数据丢失的可能性,并根据实际需求选择合适的持久化方式。
7. 合理使用内存优化参数
Redis提供了一些内存优化参数,如maxmemory、maxmemory-policy和mem-overhead等,可以帮助开发者更好的控制Redis的内存使用情况。开发者应该根据实际需求,合理配置这些参数,以便更好的管理内存资源。
总结
Redis作为一种非常流行的内存数据库,其高性能和高速的响应速度受到开发者的青睐。通过本文介绍的七个技巧,可以帮助开发者更好地管理Redis的内存使用情况,提高缓存命中率和应用程序的性能。当然,对于Redis的内存使用,还有很多需要了解和研究的地方,开发者需要不断积累经验,进行针对性的优化和调整。