前言
先来回忆一下redis的基本数据类型:
String, List, set, zSet, hash
顺便想想这些问题:String在Redis底层是怎么存储的?这些数据类型在Redis中是怎么存放的?Redis快的原因就只有单线程和基于内存么?
今天先复习String在redis底层的存储
String在redis底层如何存储
redis是C语言开发的,但是没直接采用c语言的字符串类型,而是自己构建了 动态字符串(SDS) 的抽象类型
struct sdshdr{
int len;//字符串长度
int free;//未使用长度
char buf[];//字符数组
}
SDS在redis中除了用作字符串,还用作缓冲区(buffer).
两个SDS,一个是名为aobing的Key SDS,另一个是名为cool的Value SDS.
SDS与字符串的区别
1.1 计数方式不同
C语言对于字符串的统计,完全来自遍历,从头遍历到末尾,知道发现空字符串就停止,这样统计出来的字符串长度时间复杂度为O(n)
SDS会保留长度信息,免去每次遍历获取字符串长度。所以redis速度提升了
1.2 杜绝缓冲区溢出
字符串拼接的时候,由于c语言没有记录字符串长度,没有提前计算好内存,调用拼接函数的时候可能会产生缓存区溢出。
SDS根据free判断是否可以存放拼接的字符串,如果不够进行扩容。
1.3 减少修改字符串时带来的内存重分配字数
C语言字符串底层也是数组,每次创建的时候就创建长度为N+1的字符,多的1是为了保存空字符’\0’。
SDS不对空字符串进行判断,只需判断len和free
Redis是个高速缓存数据库,如果频繁对字符串进行拼接和截断,很容易造成缓冲区溢出和内存泄露。内存分配算法很耗时开销很大。
Redis为了避免C字符串这样的缺陷,就分别采用了两种解决方案,去达到性能最大化,空间利用最大化:
- 空间预分配:当我们对SDS进行扩展操作的时候,Redis会为SDS分配好内存,并且根据特定的公式,分配多余的free空间,还有多余的1byte空间(这1byte也是为了存空字符),这样就可以避免我们连续执行字符串添加所带来的内存分配消耗。
- 惰性空间释放:刚才提到了会预分配多余的空间,很多小伙伴会担心带来内存的泄露或者浪费,别担心,Redis大佬一样帮我们想到了,当我们执行完一个字符串缩减的操作,redis并不会马上收回我们的空间,因为可以预防你继续添加的操作,这样可以减少分配空间带来的消耗,但是当你再次操作还是没用到多余空间的时候,Redis也还是会收回对于的空间,防止内存的浪费的。
1.4 二进制安全
C语言根据空字符串判断字符长度,但是图片,音频,视频,压缩文件的二进制文件会穿插空字符串在中间。
Redis由于保存了字符串的长度, 避免判断空字符,直接判断长度,所以redis也经常被我们拿来保存各种二进制数据。