CodeBear的园子资料

本文主要介绍CodeBear的园子资料 方法和在新技术下所面对的“挑战”,方便大家深入理解CodeBear的园子资料 过程。本文也将分享CodeBear的园子资料 所遇到的问题和应对策略,怎么解决怎么做的问题。
通过深入本文可以理解代码原理,进行代码文档的下载,也可以查看相应 Demo 部署效果。

本篇博客参考:掘金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:
CodeBear的园子

"e"的ascii码是65,二进制是101,二进制是1100101:
CodeBear的园子

然后形成如下的位图:
CodeBear的园子

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

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就是一个数组,而且还是一个byte[]。

SDS

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

Redis是C语言开发的,Redis为什么不直接利用C语言的字符串,而要“别出心裁”的自己构建SDS数据结构来实现字符串呢?

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

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

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

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

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

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

扩容策略

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

最大长度

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

embstr raw

Redis的字符串有两种存储方式,一种是embstr,一种是raw,当长度<=44,采用embstr 来存储:

set codebear abcdefghijklmnopqrstuvwxyz012345678912345678 "OK" debug object codebear "Value at:0x7f4050476880 refcount:1 encoding:embstr serializedlength:45 lru:1999016 lru_seconds_idle:36" 

当长度>44,改用raw来存储:

set codebear abcdefghijklmnopqrstuvwxyz0123456789123456781 "OK" debug object codebear "Value at:0x7f404ac30100 refcount:1 encoding:raw serializedlength:46 lru:1999188 lru_seconds_idle:3" 

网上也有一些博客说是以39为分界线,为什么会有两种答案呢?继续看下去就明白了。

我们先来看看Redis的对象头,查看:

#define LRU_BITS 24 typedef struct redisObject {     unsigned type:4;     unsigned encoding:4;     unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or                             * LFU data (least significant 8 bits frequency                             * and most significant 16 bits access time). */     int refcount;     void *ptr; } robj; 

Redis对象头占 4bit+4bit+24bit+4byte+8byte(指针,在64 bit system下,占8byte)=32bit+12byte=4byte+12byte=16byte。

再来看看这两种存储形式有什么区别:
CodeBear的园子
embstr的存储形式比较紧凑,Redis的对象头和SDS对象存在一起(连续)。

CodeBear的园子
一般来说,在raw的存储形式下,Redis的对象头和SDS对象不存在一起(不连续)。

我们可以简单的理解为,一块内存的大小为64byte。

好了,前置内容介绍完毕了,我们来看看Redis3.0版本的SDS的定义,查看:

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

Redis对象头占了16byte,SDS对象的len和free又占了8byte,64-16-8=40,同时保存的字符串会以结尾,又占用了1byte,所以实际存储的字符串只能<=39位,所以在低版本的Redis下,embstr、raw的分界线为39。

再来看看Redis5.0版本的SDS的定义,查看:
CodeBear的园子
可以看到变化很大,为什么要做那么大的改变?更节省内存,当字符串长度比较小的时候,会用
sdshdr8来存储,len和alloc共占用2byte,flags占用1byte,结尾占用1byte,一共是4byte,64byte-16byte(对象头)-4byte=44byte,所以在高版本的Redis下,embstr、raw的分界线为44。

怎么样,没想到吧,我们Redis经常使用的String竟然牵扯到那么多东西,而这些东西就可以区分平庸开发和优秀开发,成为一个优秀的开发,要学习的东西还有很多很多。

CodeBear的园子资料部分资料来自网络,侵权毕设源码联系删除

区块链毕设网(www.qklbishe.com)全网最靠谱的原创区块链毕设代做网站
部分资料来自网络,侵权联系删除!
资源收费仅为搬运整理打赏费用,用户自愿支付 !
qklbishe.com区块链毕设代做网专注|以太坊fabric-计算机|java|毕业设计|代做平台 » CodeBear的园子资料

提供最优质的资源集合

立即查看 了解详情