欢迎访问shiker.tech

请允许在我们的网站上展示广告

您似乎使用了广告拦截器,请关闭广告拦截器。我们的网站依靠广告获取资金。

REDIS内存模型和使用优化
(last modified May 13, 2023, 7:31 PM )
by
侧边栏壁纸
  • 累计撰写 178 篇文章
  • 累计创建 62 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

REDIS内存模型和使用优化

橙序员
2023-05-13 / 0 评论 / 0 点赞 / 418 阅读 / 1,916 字 / 正在检测百度是否收录... 正在检测必应是否收录...
文章摘要(AI生成)

Redis内存模型使用hash表来管理存放在Redis中的键值对。数据的扩容和缩容是通过多次渐进式的rehash过程完成的。具体步骤如下:首先为ht[1]分配空间,字典同时持有ht[0]和ht[1]两个哈希表,然后通过维持一个索引计数器变量rehashidx来表示rehash工作开始。在rehash进行期间,每次对字典执行操作时,程序会将ht[0]哈希表在rehashidx索引上的所有键值对rehash到ht[1],完成后将rehashidx的值增一。随着字典操作的执行,最终ht[0]的所有键值对都会被rehash至ht[1],此时将rehashidx设为-1,表示rehash操作完成。整个Redis的存储模型包括dictEntry结构存放数据、redisObject对象维护键值对的值和指向具有不同数据结构的数据对象的数据指针。常用的数据类型和编码方式包括整型字符串、小于等于44字节的简单动态字符、大于44字节的简单动态字符串、压缩列表实现的列表对象等。

REDIS内存模型

对redis有了解的同学都知道,redis内部底层使用hash表管理我们存放到redis中的键值对,通过对两个hash表完成数据的扩容和缩容。当然,数据由一个表到另一个表的rehash过程是通过多次渐进式的rehash过程完成的。

以下是哈希表渐进式 rehash 的详细步骤:

  1. ht[1] 分配空间, 让字典同时持有 ht[0]ht[1] 两个哈希表。
  2. 在字典中维持一个索引计数器变量 rehashidx , 并将它的值设置为 0 , 表示 rehash 工作正式开始。
  3. 在 rehash 进行期间, 每次对字典执行添加、删除、查找或者更新操作时, 程序除了执行指定的操作以外, 还会顺带将 ht[0] 哈希表在 rehashidx 索引上的所有键值对 rehash 到 ht[1] , 当 rehash 工作完成之后, 程序将 rehashidx 属性的值增一。
  4. 随着字典操作的不断执行, 最终在某个时间点上, ht[0] 的所有键值对都会被 rehash 至 ht[1] , 这时程序将 rehashidx 属性的值设为 -1 , 表示 rehash 操作已完成。

整个redis的存储模型如下:

image-20230513173054539

其中,存放数据的dictEntry结构,存放了我们放入的键值对,其中键值对中的值redis通过redisObject对象进行维护,主要用于表述实际值的不同数据类型,和指向具有不同数据结构的数据对象的数据指针。

常用的数据类型以及数据编码如下(参考官方文档):

类型 编码 OBJECT ENCODING命令输出对象 备注
REDIS_STRING(整型) REDIS_ENCODING_INT int 使用整数值实现的字符串对象
REDIS_STRING(<=44字节) REDIS_ENCODING_EMBSTR embstr 使用embstr编码的简单动态字符(redisObject和sds连续,只需要分配一次内存空间)
REDIS_STRING(>44字节) REDIS_ENCODING_RAW raw 使用简单动态字符串实现的字符串对象(redisObject和sds不连续,需要分配两次次内存空间)
REDIS_LIST(redis3.0之前) REDIS_ENCODING_ZIPLIST ziplist 使用压缩列表实现的列表对象
REDIS_LIST(redis3.0之前) REDIS_ENCODING_LINKEDLIST linkedlist 使用快速链表实现的列表对象
REDIS_LIST(redis3.0之后) REDIS_ENCODING_QUICKLIST quicklist 使用双端链表实现的列表对象
REDIS_HASH(元素<512&k、v长度<64) REDIS_ENCODING_ZIPLIST ziplist 使用压缩列表实现的哈希对象
REDIS_HASH(元素>=512||k、v长度>=64) REDIS_ENCODING_HT hashtable 使用字典实现的哈希对象
REDIS_SET(元素<512&元素全为整数) REDIS_ENCODING_INTSET intset 使用整数集合实现的集合对客
REDIS_SET(元素>=512&元素不全是整数 REDIS_ENCODING_HT hashtable 使用字典实现的集合对象
REDIS_ZSET(元素<128&元素长度<64) REDIS_ENCODING_ZIPLIST ziplist 使用压缩列表实现的有序集合对
REDIS_ZSET(元素>=128&元素长度>=64) REDIS_ENCODING_SKIPLIST skiplist 使用跳跃表和字典实现的有序集合对象

此外还有几种拓展数据类型使用场景较小,如下:

类型 type命令输出对象 作用 用途
BitMap string 对字符串执行按位运算 状态统计
HyperLogLog ---- 提供大型集合的基数(即元素数量)的概率估计 记录网站IP注册数,每日访问的IP数,页面实时UV、在线用户人数
Geospatial zset 存储地理空间索引 查找给定地理半径或边界框内的位置

REDIS设计优化

jemalloc作为Redis的默认内存分配器,在减小内存碎片方面做的相对比较好。jemalloc在64位系统中,将内存空间划分为小、大、巨大三个范围,每个范围内又划分了许多小的内存块单位;当Redis存储数据时,会选择大小最合适的内存块进行存储。

示例,假如我们有一个大小为190个字节的对象,jemalloc会一律为我们划分吃192(8X2+16X7+32X2)个字节。

image-20230513191219372

内存使用估算

每个dictEntry占据的空间包括:

  1. dictEntry结构,24字节,jemalloc会分配32字节的内存块(64位操作系统下,一个指针8字节,一个dictEntry由三个指针组成)
  2. key的字节数为sds字节数,而sds字节数=4+字符串key自身长度
  3. redisObject的固定结构会占用16字节,jemalloc会分配16字节的内存块(4bit+4bit+24bit+4Byte+8Byte=16Byte)
  4. value大小根据我们的数据结构而定,结构越复杂占用字节数越多

优化内存占用

  1. 利用jemalloc特性优化,设置k-v对象时,尽可能为8的整数倍,使jemalloc可以连续分配,避免占用内存波动
  2. 使用整型/长整型数据,节省更多空间
  3. 使用整型数据时,可以调整OBJ_SHARED_INTEGERS参数设置合适的共享对象个数来减少对象创建,节省内存空间
  4. 缩短键值对的存储长度,键值对越长,写入越慢

查看内存用量

使用info memory命令即可查看redis内存使用情况,在输出信息中,我们通常只关注下面几个信息:

  1. used_memory:由Redis内存分配器分配的数据内存和缓冲内存的内存总量 (单位是字节),包括使用的虚拟内存 (即swap)
  2. used memory_rss:记录的是由操作系统分配的Redis进程内存和Redis内存中无法再被jemalloc分配的内存碎片 (单位是字节)
  3. mem_fragmentation_ratio:由于在实际应用中,Redis的数据量会比较大,此时进程运行占用的内存与Redis数据量和内存碎片相比,都会小得多;因此used_memory_rssused_memory的比例,便成了衡量Redis内存碎片率的参数内存碎片比率,mem_fragmentation_ratio一般大于1,且该值越大,内存碎片比例越大。mem_fragmentation ratio<1,说明Redis使用了虚拟内存,由于虚拟内存的媒介是磁盘,比内存速度要慢很多,当这种情况出现时,应该及时排查,如果内存不足应该及时处理,如增加Redis节点、增加Redis服务器的内存、优化应用等。一般来说,mem _fragmentation_ratio在1.03左右是比较健康的状态 (对于jemalloc来说);刚开始的mem_fragmentation_ratio值很大,是因为还没有向Redis中存入数据,Redis进程本身运行的内存使得used_memory_rssused memory大得多
  4. mem_allocator
    Redis使用的内存分配器,在编译时指定;可以是 libcjemalloc或者tcmalloc,默认是iemalloc;

used_memoryused memory_rss的区别:
前者是从Redis角度得到的量,后者是从操作系统角度得到的量。二者之所以有所不同,一方面是因为内存碎片和Redis进程运行需要占用内存,使得前者可能比后者小,另一方面虚拟内存的存在,使得前者可能比后者大。

0

评论区