起因
今天面试惨败,面试官问了不到10个问题就让我出来写题了……对其中的一个题目印象深刻:
Redis获取字符串长度的复杂度是多少?
刚开始我是一脸懵逼的,因为不清楚Redis
的字符串类型是怎么实现的,所以完全没法答下去了……回来后马上开始学习。
字符串类型
字符串是Redis
里非常常见的类型,而用C
实现的Redis
和Java
不一样。在C
里字符串是用长度为N+1
的字符数组实现的,且使用空字符串'\0'
作为结束符号。获取字符串的长度需要遍历一遍,找到空字符串'\0'
才知道字符串的长度,复杂度是O(N)
。
如果有一个长度非常大的字符串,单线程的Redis
获取它的长度就可能会阻塞很久,这是不能接受的,所以Redis
需要一种更高效的字符串类型。
SDS
Redis
实现了一个叫SDS(simple dynamic string)
的字符串类型,其中有两个变量来分别代表字符串的长度和字符数组未使用的字符数量,这样就可以用O(1)
的复杂度来获取字符串的长度了,而且同样也是使用空字符串'\0'
作为结束符号。
struct sdshdr {
// 字符串长度
int len;
// 字符数组未使用的字符数量
int free;
// 保存字符串的字符数组
char buf[];
}
现在已经可以回答上面的面试题了,其实是非常简单的一个问题,怪不得答不出来面试官马上就说面试结束了……
扩容机制
SDS
在字符数组空间不足于容纳新字符串的时候会自动扩容。
如果把一个C
字符串拼接到一个SDS
后面,当字符数组空间不足时,SDS
会先扩容到刚好可以容纳新字符串的长度,然后再扩充新字符串的空字符长度,最终SDS
的字符数组长度等于 2 * 新字符串 + 1(结束符号'\0')
。不过当新字符串的大小超过1MB
后,扩充的空字符长度大小会固定为1MB
。
之所以会有这个机制,是因为Redis
作为一个NoSQL
数据库,会频繁的修改字符串,扩容机制相当于给SDS
做了一个缓冲池。把SDS
连续增长N
次字符串需要内存重分配N
次优化成了SDS
连续增长N
次字符串最多需要内存重分配N
次,这其实和Java
里的StringBuilder
实现思想是一样的。
后记
这次翻车是有原因的,我看过两本关于Redis
的书,里面都是讲Redis
如何实战的但是并没有讲Redis
的设计和实现。这也就导致了面试很尴尬,因为面试官最喜欢问原理相关的东西了,所以以后学习技术的时候不要从实战类的书籍开始了,还是先看懂原理比较好。
这是《Redis设计与实现》
里字符串一节的总结。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。