持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第24天,点击查看活动详情
前言
Redis常用数据结构有String、List、Hash、Set、ZSet,而String类型在实际的应用中是最为广泛的,那么Redis的String底层数据结构和存储是怎样实现呢?
String数据结构
Redis是基于C语言开发的,Redis没有直接使用C语言传统的char,而是自己构建了一种名为简单动态字符串(simple dynamic string SDS)的抽象类型,并将SDS用作Redis的默认字符串表示,SDS的组成结构如下:
说明:
- len:表示字符串的长度
- alloc:分配的空间长度
- buf:字符数组,用来保存实际数据,Redis为了表示数组的结束会在数组后加一个"\0"表示结尾。
为什么Redis字符采用SDS的数据结构
- 优化了获取字符串长度为 O(1)
- 传统的C字符串: 使用长度为N+1的字符串数组来表示长度为N的字符串,所以为了获取一个长度为C字符串的长度,必须遍历整个字符串。
- SDS:有专门用于保存字符串长度的变量,我们可以通过获取len属性的来获取字符串长度。
- 杜绝缓冲区溢出 Redis中SDS的空间分配策略完全杜绝了发生缓冲区溢出的可能性。 修改SDS时候Redis会在执行拼接操作之前,预先检查给定SDS空间是否足够,如果不够会先拓展SDS的空间,然后再执行相关拼接操作。
3.减少内存重分配
- 1.字符串拼接会产生字符串的内存空间的扩充,在拼接的过程中,原来的字符串的大小很可能小于拼接后的字符串的大小,那么这样的话,就会导致一旦忘记申请分配空间,就会导致内存的溢出。
- 2.字符串在进行收缩的时候,内存空间会相应的收缩,而如果在进行字符串的切割的时候,没有对内存的空间进行一个重新分配,那么这部分多出来的空间就成为了内存泄露。
String数据存储
Redis的String类型存储分为分别是int编码格式、embstr编码格式、raw编码格式。
-
1.当长度小于20,并且是数值类型时,其底层就是用long来进行数据存储;这样做的好处就是可以减少空间占用,使用long的话最多也就占8个字节,使用char类型的话,会占用更多的内存空间。
-
2.当存储是字符串的数据时,并且字符串的长度小于等于44字节时,以及长度大于22时,RedisObject和SDS会被分配到同一块内存空间,这样可以避免内存碎片化的问题,这种方式称为embstr编码方式。
-
3.当存储是字符串的数据时,并且字符串的长度大于44字节时,那么RedisObject和SDS不会被分配在同一片内存空间,这种方式称为raw编码方式.
String数据存储组成部分
说明:String数据的存储分为下列几个部分 SDS存储部分:包含len、alloc、buf的存储空间。
- RedisObject:包含了元数据和一个SDS指针,如果是String类型的指针将指向SDS类型,如果是int类型,Redis对于存储进行了优化,无需进行引用。
- dictEntry:Redis的全局哈希,包含了key、value、next的字节数,为了减少频繁分配的次数,Redis的jemalloc在分配内存时,会根据我们申请字节数N,找一个比N大,但是最接近N的2的幂次数作为分配空间。例如如果申请为8字节,那么dictEntry结构会占用16个字节。
优化建议
- 1.设置Key的名称,注意控制key的长度,因为key过长会消耗过多的内存空间
- 2.采用高效的序列方法和压缩方法,不同的序列化方法,在序列化速度和数据序列化后的占用内存空间这两个方面,效果是不一样的。比如说,protostuff 和 kryo 这两种序列化方法,要比Java内置的序列化方法(java-build-in-serializer)效率更高。
总结
本文讲解了Redis的String的数据结构和数据存储,通过本文我们知道Redis的String类型占用的空间开销还是比较大的,所以我们在实际的项目中需要进行相关的优化,从而优化存储空间。