mysql内存表和redis

发布时间: 2023-11-21 12:27 阅读: 文章来源:1MUMB3656PS

在安装的redis/src文件夹下可以看到有很多后缀名为.h、.c、.o的文件,其中.h代表的是.c文件中用到的变量、数组、函数的声明,.c文件是.h文件中声明的变量、数组、函数具体的定义,而.o就是编译后的汇编文件。

大家可以看到有dict.h文件,这个文件里面即定义了字典的数据结构,我们打开源码可以看到如下四个C语言的结构体(struct):

typedef struct dictEntry {void *key;union {void *val;uint64_t u64;int64_t s64;double d;} v;struct dictEntry *next;} dictEntry;typedef struct dictType {uint64_t (*hashFunction)(const void *key);void *(*keyDup)(void *privdata, const void *key);void *(*valDup)(void *privdata, const void *obj);int (*keyCompare)(void *privdata, const void *key1, const void *key2);void (*keyDestructor)(void *privdata, void *key);void (*valDestructor)(void *privdata, void *obj);} dictType;typedef struct dictht {dictEntry **table;unsigned long size;unsigned long sizemask;unsigned long used;} dictht;typedef struct dict {dictType *type;void *privdata;dictht ht[2];long rehashidx; /* rehashing not in progress if rehashidx == -1 */unsigned long iterators; /* number of iterators currently running */} dict;

用一张图来表述他们之间的关系如下:

当我们执行一条如下语句的时候:

set testKey testValue

如果是首次redis写入,会创建一个dict字典对象,字典对象的数据如下:

当然如果你写入的不是字符串类型的数据类型,而是List、Hash、Set、ZSet四种数据,也和上图的数据结构一样,只是dictEntry里面的值对象*val指针会指向不同的对象,不同的对象会有不同的数据结构,强烈推荐大家阅读《redis设计与实现》这本书,深读此书将会彻底搞清楚redis。

redis内存计算

上节从redis的字典说了redis的底层数据结构是如何保存我们写入的key的,那么当我们执行命令写入key到redis中,redis的内存具体是如何分配的呢?我们一起来实验一下:

首先执行FLUSHALL命令来清空我们的redis,保证没有其他key干扰,然后执行:

src/redis-cli info | grep mem

获取redis初始内存信息:

关键属性说明如下(更多属性说明请查阅redis官网):

redis初始占用内存:1039472字节,当我们执行:

set testKey testValue

再查看内存变化为:

也就是说上面的语句执行后吃了redis内存为:1057472-1039472=18000b=17.58K,那是不是代表上面的执行吃了18K的内存呢?

我们再写入一个key:

set testKey1 testValue1

通过上文对字典的描述可以知道testKey1在redis中的存储应该如下图所示:

查看内存变化为:

used_memory:1057552

才发现吃了80字节的内存。

所以我们可以知道的是redis启动之后需要占用一部分内存,这部分内存1039472字节用于redis服务的运行以及初始化一些数据。另外首次写入redis的key之后,需要构造上文所说的redis字典结构,因此需要占用一些内存。

我们需要知道的是当我们写入一个key的时候占用的内存到底是多少,由于我们写的值都没有超过44个字节,所以采用EMBSTR数据结构存储。所以我们可以查看object.c源码里面是如何创建对象的:

分配内存的代码:

robj *o = zmalloc(sizeof(robj)+sizeof(struct sdshdr8)+len+1);

可以看到redis为我们分配了:

sizeof(robj)+sizeof(struct sdshdr8)+len+1

这么大的内存,其中的robj代表的是redisObject,查看server.h中关于redisObject对象的定义:

因此sizeof(robj) = 16字节。

sdshdr8即上图中的sdshdr中的头部3个字节。

因此testValue1这个采用EMBSTR编码的存储需要内存:16+3+10+1=30字节,redis内存分配器为其分配32字节。

我们再来计算testKey1占用的内存,testKey1存储的就是一个SDS简单动态对象,少了robj的内存占用,因此需要内存:3+8+1 = 12字节,redis分配器为其分配16字节。

总共需要内存为32+16=48字节,那为什么占用的是80字节呢?剩下的32字节谁吃了呢?大家不要忘记了dictEntry这个结构还有三个指针呢:

三个指针占用内存:3*8-24字节,jemalloc会为其分配32个字节。

至此,我们便能清晰的知道当我们执行一个字符串对象(字符串长度不超过44!)写入的时候,需要占用内存多少了。

即80-18(testKey1&testValue1) = 62的长度。但是我们需要知道这62个长度都吃在什么地方了。

上面说的是当写入String类型的数据且长度值不超过44的时候占用的内存计算方法。其他数据类型如List、Hash、Set、Zset大家可以参考我上面的方法和思路并查看相关redis源码以及redis技术资料即可得知。

redis-benchmark压测

src目录下redis-benchmark是redis自带的压测工具,压测语法格式:

redis-benchmark [option] [option value]

option可选参数如下:

执行压测语句:

src/redis-benchmark -p 6379 -t set -c 100 -n 1000000 -r 1000000

输出压测结果:

➜redis-5.0.7 src/redis-benchmark -p 6379 -t set -c 100 -n 1000000 -r 1000000====== SET ======1000000 requests completed in 20.04 seconds100 parallel clients3 bytes payloadkeep alive: 144.04%
•••展开全文