Redis的字符串是怎么实现的?

来源:juejin.im/post/5ca9d8ae6fb9a05e5c05c4e8

之前本人在找工作面试时在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会做以下三个操作:

  1. 计算出大小是否足够

  2. 开辟空间至满足所需大小

  3. 开辟与已使用大小len相同长度的空闲free空间(如果len < 1M)开辟1M长度的空闲free空间(如果len >= 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而言由于每次追加字符串时都会检查空间是否够用,所以不会存在缓冲区溢出问题。每次追加操作前都会做如下操作:

  1. 计算出大小是否足够

  2. 开辟空间至满足所需大小

3.降低空间分配次数提升内存使用效率

字符串的追加操作会涉及到内存分配问题,然而内存分配问题会牵扯内存划分算法以及系统调用所以如果频繁发生的话影响性能,所以对于性能至上的Redis来说这是万万不能忍受的。所以采取了以下两种优化措施

  • 空间与分配

  • 惰性空间回收

1. 空间预分配

对于追加操作来说,Redis不仅会开辟空间至够用而且还会预分配未使用的空间(free)来用于下一次操作。至于未使用的空间(free)的大小则由修改后的字符串长度决定。

当修改后的字符串长度len < 1M,则会分配与len相同长度的未使用的空间(free)

当修改后的字符串长度len >= 1M,则会分配1M长度的未使用的空间(free)

有了这个预分配策略之后会减少内存分配次数,因为分配之前会检查已有的free空间是否够,如果够则不开辟了~

2. 惰性空间回收

与上面情况相反,惰性空间回收适用于字符串缩减操作。比如有个字符串s1="hello world",对s1进行sdstrim(s1," world")操作,执行完该操作之后Redis不会立即回收减少的部分,而是会分配给下一个需要内存的程序。当然,Redis也提供了回收内存的api,可以自己手动调用来回收缩减部分的内存。

到此为止结束了~

--完--

如果需要Redis视频,可以在公众号后台聊天框回复【Redis视频】,可以免费获取编程视频 。

Redis其它推荐阅读:

Redis 零基础入门视频教程

Linux 安装 Redis 图文教程

刚接触学Redis,看这一篇就够了!

再来聊聊Redis到底是什么?

说说 Redis 数据结构和常用命令

Redis最常见面试问题,附答案!

阿里巴巴官方 Redis 开发规范!

如何借助 Redis 实现秒杀系统?

史上最全 Redis 高可用解决方案总结

10 个正确使用 Redis 的技巧

Redis 分布式锁的正确实现方式

Redis 源码学习之 Redis 事务

详解Redis的内存淘汰策略

Redis 的各项功能,都解决了哪些问题?

为什么单线程的Redis却能支撑高并发?

Redis应用场景,实现功能 “附近的人”

超详细揭秘 Redis 持久化,建议收藏!

后端开发都应该掌握的Redis基础

Redis是如何实现点赞、取消点赞的?

Redis实践:网站搜索的热搜词

如何用 Redis 做实时订阅推送的?

Redis 主从复制以及主从复制原理

如何访问 Redis 中的海量数据?

一文深入了解Redis内存模型!

Redis常见的几种缓存模式

长按加入10W+朋友的IT圈

觉得内容还不错的话,给我点个“在看”呗

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值