蜻蜓点水说说Redis的String的奥秘

本篇博客参考:掘金Redis小册 敖丙

如果面试官问你,单线程的Redis为什么那么快,你可能脱口而出,因为单线程,避免上下文切换;因为基于内存,比硬盘读写快很多;因为采用的是多路复用网络模型。不管你是否真的理解了,这个回答足以应付一半以上的面试官了,但是如果可以再进行补充就更好了:因为Redis对各种数据结构进行了精心的设计,比如String采用的是SDS,比如list采用的是ziplist,quicklist等等,可能这样的回答就比较出彩了,至少可以说出部分面试者不太清楚的事情。今天我们就来看看Redis中最常用的String数据结构的奥秘。

从位操作说起

bitmap的应用场景很多,比如大名鼎鼎的布隆过滤器(之前的博客有介绍过:《大白话布隆过滤器》),比如统计指定用户在一年内任意日期内的登录情况,统计任意日期内,所有用户的登录情况等等,都可以用bitmap来实现(之前的博客也有介绍过:《有点长的博客:Redis不是只有get set那么简单》),所以好好看看bitmap还是很有必要的,不过本篇博客不打算详细介绍bitmap,只是通过bitmap引出我们今天的话题,而bitmap的核心就是位操作。

如果我们要往Redis塞入一个value为”hello”的key,这个所有人都会:

test:1>set key hello
"OK"
test:1>get key
"hello"

如果我们要利用位操作实现这个需求呢?什么,我没听错把,位操作也可以实现这个需求吗?当然可以,因为在Redis中,String就是用byte数组来存储的。

什么,你不信?那请继续看下去。

要用位操作实现这个需求,我们要获得”hello”的ascii码,接着计算出二进制:

比如,”h”的ascii码是104,二进制是1101000:

蜻蜓点水说说Redis的String的奥秘

“e”的ascii码是65,二进制是101,二进制是1100101:

蜻蜓点水说说Redis的String的奥秘

然后形成如下的位图:

蜻蜓点水说说Redis的String的奥秘

下面就需要利用位操作来进行设置:

test:1>setbit s 1 1
"0"
test:1>setbit s 2 1
"0"
test:1>setbit s 4 1
"0"
test:1>setbit s 10 1
"0"
test:1>setbit s 13 1
"0"
test:1>setbit s 9 1
"0"
test:1>setbit s 15 1
"0"

setbit的顺序可以随意调整,只要最终得到的位图是如上形式的就OK了。(我这里就调整了下seitbit的顺序,好吧,我承认其实我是打错了,又懒得再去打一遍,反正最终形成的位图是一样的)。

然后我们get一下:

test:1>get s
"he"

很神奇,有木有,这也说明了在Redis的底层,String就是一个数组。

SDS

不管在什么编程语言、存储引擎中,String都是应用最广泛的,而在不同的编程语言、存储引擎中,String可能有不同的实现,在Redis中,String的底层就是SDS,它的全称是Simple Dynamic String。

Redis是C语言开发的,C语言是没有现成的字符串类型的,而是用字符数组来表示字符串,Redis为什么不直接这么做呢,而要”别出心裁”的自己构建SDS数据结构来实现字符串呢?

我们先来这个SDS是个什么鬼:

struct sdshdr {
    int len;
    int free;
    char buf[];
};

SDS的定义比较简单,只有3个字段,而且从字面上就可以看出是什么意思:

  • len:存储字符串的实际长度
  • free:存储剩余(空闲)的空间
  • buf[]:存储实际数据

我们可以看到,其实SDS结构也包含了字节数组,但是不同的是,新增了两个字段。

为了方便起见,下面的文章把直接使用字节数组来表示字符串称为C语言的字符串,但是大家要明白,C语言是没有现成的字符串类型的。

下面我们来看下SDS和C语言的字符串有什么区别:

  • 求字符串长度
    C语言的字符串,求字符串的长度只能遍历,时间复杂度是O(N),单线程的Redis表示鸭梨山大,但是现在引入了一个字段来存储字符串的实际长度,时间复杂度瞬间降低成了O(1)。
  • 二进制安全
    在C语言中,读取字符串遵循的是”遇零则止”,即,读取字符串,当读取到”\0″,就认为已经读到了结尾,哪怕后面还有字符串也不会读取了,像图片、音频等二进制数据,经常会穿插”\0″在其中,好端端的图片、音频就毁了…但是现在有了一个字段来存储字符串的实际长度,读取字符串的时候,先看下这个字符串的长度是多少,然后往后读多少位就可以了。
  • 缓冲区溢出
    字符串拼接是开发中常见的操作,C语言的字符串是不记录字符串长度的,一旦我们调用了拼接函数,而没有提前计算好内存,就会产生缓冲区溢出的情况,但是现在引入了free字段,来记录剩余的空间,做拼接操作之前,先去看下还有多少剩余空间,如果够,那就放心的做拼接操作,不够,就进行扩容。
  • 减少内存重分配次数

  • 空间预分配:当对字符串进行拼接操作的时候,Redis会很贴心的分配一定的剩余空间,这块剩余空间现在看起来是有点浪费,但是我们如果继续拼接,这块剩余空间的作用就出来了。

  • 惰性空间释放:当我们做了字符串缩减的操作,Redis并不会马上回收空间,因为你可能即将又要做字符串的拼接操作,如果你再次操作,还是没有用到这部分空间,Redis也会去回收这部分空间。

扩容策略

字符串小于1M,采用的是加倍扩容的策略,也就是多分配100%的剩余空间,当大于1M,每次扩容,只会多分配1M的剩余空间。

最大长度

Redis 规定字符串的长度不得超过 512M 字节。

embstr raw

Redis的字符串有两种存储方式,一种是embstr,一种是raw,当长度

Original: https://www.cnblogs.com/CodeBear/p/13385727.html
Author: CodeBear
Title: 蜻蜓点水说说Redis的String的奥秘

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/714899/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Using Wireshark SIP Analysis for VoIP scenarios

    Using Wireshark SIP Analysis for VoIP scenarios Using Wireshark SIP Analysis for VoIP scen…

    技术杂谈 2023年5月31日
    0137
  • 神经网络那些事儿(二)

    在上一篇中,我们看到了神经网络是怎样使用梯度下降算法来学习它们的权值和偏置。然而,我们还有一些没有解释:我们没有讨论怎样计算损失函数的梯度。本篇中将解释著名的BP算法,它是一个快速…

    技术杂谈 2023年5月31日
    0109
  • 【SSM框架】Spring笔记 – 事务详解

    1、Spring的事务管理: 事务原本是数据库中的概念,在实际项目的开发中,进行事务的处理一般是在业务逻辑层, 即 Service 层。这样做是为了能够使用事务的特性来管理关联操作…

    技术杂谈 2023年7月10日
    086
  • CGContext图形上下文详解

    CGContextSaveGState函数的作用是将当前图形状态推入堆栈。之后,您对图形状态所做的修改会影响随后的描画操作,但不影响存储在堆栈中的拷贝。在修改完成后,您可以通过CG…

    技术杂谈 2023年5月30日
    093
  • Java基础语法(真随笔,不完整)

    ; ; 一、注释 (1)单行注释 // (2)多行注释 / / (3)文档注释 JavaDoc /* /(用来生成自己的API文档) 二、标识符 (1)都以A-Z、a-z、$、_ …

    技术杂谈 2023年6月21日
    096
  • selenium-禁止图片加载

    csharp;gutter:true;</p> <h1>!encoding=utf-8</h1> <p>from lxml impo…

    技术杂谈 2023年5月31日
    094
  • 有意义的学习,都要先回答三个问题

    我们都知道, 在现代经济中, 我们不能停止学习。但如何保持自我教育是一个复杂的问题。 获得一个正式学位,比如 MBA 或博士学位, 是否值得? 你是否应该采取更有针对性的方法, 参…

    技术杂谈 2023年5月31日
    095
  • 快速上手Mybatis项目

    1、搭建实验数据库 CREATE DATABASE mybatis; USE mybatis; DROP TABLE IF EXISTS user; CREATE TABLE us…

    技术杂谈 2023年7月11日
    075
  • 八荣八耻千字文

    爹精娘血,怀胎十月;赤赤条条, 送到世界。高级动物,精神不灭。路该咋走?人该咋做?八荣八耻,当今准则。知道美丑,懂得善恶;分辨真假,不再冷漠。人心人道,人禽有别。人格人德,山峰一座…

    技术杂谈 2023年6月1日
    082
  • Mybatis缓存机制

    MyBatis是常见的 Java数据库访问层框架。在日常工作中,多数情况下是使用 MyBatis的默认缓存配置减轻数据库压力,提高数据库性能,但是 MyBatis缓存机制有一些不足…

    技术杂谈 2023年7月24日
    088
  • 初步体验 Kubernetes 的 Custom Resource Definition

    CRD(Custom Resource Definition) 是 kubernetes 强大扩展能力的一处体现,联系到编程场景,CRD 相当于是类(class),custom r…

    技术杂谈 2023年5月31日
    0105
  • IDEA中单元测试

    创建存放测试文件的目录 需要在project下新建一个文件夹,用于存放自动生成的测试.java文件,比如 Factorial.java类对应的 FactorialTest.java…

    技术杂谈 2023年5月31日
    0105
  • 深入理解完美哈希

    深入理解完美哈希 https://mp.weixin.qq.com/s/M8Wcj8sZ7UF1CMr887Puog 搜索 复制 Original: https://www.cnb…

    技术杂谈 2023年5月31日
    087
  • redis启动报无权限

    设置一下SELINUX 关闭SElinux 查看selinux状态 [root@localhost ~]# getenforce Enforcing 表示启动 临时关闭 [root…

    技术杂谈 2023年7月25日
    0100
  • Node.js 执行系统命令

    我们在使用Node.js的时候总有一些时候需要执行一些系统命令,与系统做一些交互,或者调用其他语言,这个时候该怎么做呢?当然是使用 child_process模块了,这个模块翻译过…

    技术杂谈 2023年5月31日
    073
  • 渗透测试过程参考

    序列号 测试漏洞 是否安全 后台路径泄露 post 注入 数据库备份 getwebshell 配置文件写入木马 任意文件上传漏洞 万能密码登陆 开源编辑器、插件漏洞 后台越权访问 …

    技术杂谈 2023年6月1日
    088
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球