基础编程学习快乐每一天
首页
留言
Siddim.com
当前位置:
首页
>
编程知识库
>
后端开发知识
>
你知道Redis的字符串是怎么实现的吗?
你知道Redis的字符串是怎么实现的吗?
阅读
1
2019-12-09
之前本人在找工作面试时在
Redis
相关问题上可栽了跟头。在面试前按常规套路准备了一下,比如
Redis
的常用
5
种数据结构,
Redis
持久化策略,
Redis
实现分布式锁,简单发布订阅等等都准备了,当时不知天高地厚以为十拿九稳了,可是万万没想到我终究还是在
Redis
的被问的第一个问题上翻船了~~
面试官 :看你简历上写了熟悉常用数据结构,都有哪些说说
本人 :常用有
5
种,
string
,
list
,
set
,
zset
,
hash
(内心很得意)
面试官 :那你说说都用过哪些数据结构
本人 :用的最多的是
string
,通常会把
json
字符串存进去
面试官 :那你知道
Redis
内部是怎么实现它的
string
的么?
本人 :呃~,我了解
Redis
是用
C
语言写的,至于具体实现就不清楚了~
到此一面卒~~~
有相同经历的朋友么?
回去后恶补了一下
Redis
有关原理性的知识点,恰好最近在最总结面试经历于是有了今天这篇文章。
本篇会讲以下内容:
Redis
字符串的实现
Redis
字符串的性能优势
Redis字符串的实现
Redis
虽然是用
C
语言写的,但却没有直接用
C
语言的字符串,而是自己实现了一套字符串。目的就是为了提升速度,提升性能,可以看出
Redis
为了高性能也是煞费苦心。
Redis
构建了一个叫做简单动态字符串(
Simple
Dynamic
String
),简称
SDS
1.SDS 代码结构
struct sdshdr{ // 记录已使用长度 int len; // 记录空闲未使用的长度 int free; // 字符数组 char[] buf; };
SDS
?什么鬼?可能对此陌生的朋友对这个名称有疑惑。只是个名词而已不必在意,我们要重点欣赏借鉴
Redis
的设计思路。下面画个图来说明,一目了然。
Redis
的字符串也会遵守
C
语言的字符串的实现规则,即最后一个字符为空字符。然而这个空字符不会被计算在
len
里头。
2.SDS 动态扩展特点
SDS
的最厉害最奇妙之处在于它的
Dynamic
。动态变化长度。举个例子
如上图所示刚开始
s1
只有
5
个空闲位子,后面需要追加'
world
'
6
个字符,很明显是不够的。那咋办?
Redis
会做以下三个操作:
计算出大小是否足够
开辟空间至满足所需大小
开辟与已使用大小
len
相同长度的空闲
free
空间(如果
len
&
lt
;
1M
)开辟
1M
长度的空闲
free
空间(如果
len
&
gt
;=
1M
)
看到这儿为止有没有朋友觉得这个实现跟
Java
的列表
List
实现有点类似呢?看完后面的会觉得更像了。
Redis字符串的性能优势
快速获取字符串长度
避免缓冲区溢出
降低空间分配次数提升内存使用效率
1.快速获取字符串长度
再看下上面的
SDS
结构体:
struct sdshdr{ // 记录已使用长度 int len; // 记录空闲未使用的长度 int free; // 字符数组 char[] buf; };
由于在
SDS
里存了已使用字符长度
len
,所以当想获取字符串长度时直接返回
len
即可,时间复杂度为
O
(
1
)。如果使用
C
语言的字符串的话它的字符串长度获取函数时间复杂度为
O
(
n
),
n
为字符个数,因为他是从头到尾(到空字符'\
0
')遍历相加。
2.避免缓冲区溢出
对一个
C
语言字符串进行
strcat
追加字符串的时候需要提前开辟需要的空间,如果不开辟空间的话可能会造成缓冲区溢出,而影响程序其他代码。如下图,有一个字符串
s1
="
hello
" 和 字符串
s2
="
baby
",现在要执行
strcat
(
s1
,"
world
"),并且执行前未给
s1
开辟空间,所以造成了缓冲区溢出。
而对于
Redis
而言由于每次追加字符串时都会检查空间是否够用,所以不会存在缓冲区溢出问题。每次追加操作前都会做如下操作:
计算出大小是否足够
开辟空间至满足所需大小
3.降低空间分配次数提升内存使用效率
字符串的追加操作会涉及到内存分配问题,然而内存分配问题会牵扯内存划分算法以及系统调用所以如果频繁发生的话影响性能,所以对于性能至上的
Redis
来说这是万万不能忍受的。所以采取了以下两种优化措施
空间与分配
惰性空间回收
1
. 空间预分配
对于追加操作来说,
Redis
不仅会开辟空间至够用而且还会预分配未使用的空间(
free
)来用于下一次操作。至于未使用的空间(
free
)的大小则由修改后的字符串长度决定。
当修改后的字符串长度
len
&
lt
;
1M
,则会分配与
len
相同长度的未使用的空间(
free
)
当修改后的字符串长度
len
&
gt
;=
1M
,则会分配
1M
长度的未使用的空间(
free
)
有了这个预分配策略之后会减少内存分配次数,因为分配之前会检查已有的
free
空间是否够,如果够则不开辟了~
2
. 惰性空间回收
与上面情况相反,惰性空间回收适用于字符串缩减操作。比如有个字符串
s1
="
hello
world
",对
s1
进行
sdstrim
(
s1
,"
world
")操作,执行完该操作之后
Redis
不会立即回收减少的部分,而是会分配给下一个需要内存的程序。当然,
Redis
也提供了回收内存的
api
,可以自己手动调用来回收缩减部分的内存。
到此为止结束了~
下次在遇到这个问题可以侃侃而谈了,哈哈哈
来源:
juejin
.
im
/
post
/
5ca9d8ae6fb9a05e5c05c4e8
最近三期
【
28
期】
ZooKeeper
面试那些事儿
【
29
期】
Java
集合框架
10
连问,你有被问过吗?
【
30
期】说一下
HashMap
的实现原理?
? ~
以上数据来源于网络,如有侵权,请联系删除。
上一篇:
了解什么是 redis 的雪崩、穿透和击穿?redis 崩溃之后会怎么样?应对措施是什么
下一篇:
分别谈谈联合索引生效和失效的条件
评论
(0)
提交
类别
基础编程学习
HTML
PHP
Python
编程知识库
后端开发知识
热门文章
Java并发中的同步容器与并发容器,你了解多少?
Innodb中的事务隔离级别和锁的关系,难倒一半面试者!
SpringBoot + minio实现分片上传、秒传、续传
面试官:你知道消息队列如何保证数据不丢失吗?
JAVA知识 Java8新特性
面试官:谈谈为什么要限流,有哪些限流方案?
说说动态代理与静态代理区别
面试官:思考Tomcat 类加载器为什么要违背双亲委派模型?
boot-admin 基于SpringBoot的后台权限管理系统,可作为脚手架,用于快速搭建项目
SpringBoot+Vue+App+硬件实现智能家居系统项目