[Redis]基础数据结构(一)String

前言:

最近在准备面试,把一年前看的redis深度历险和当时看的源码,重新拾到一下。

快速过一遍,顺便整理以便后续的成长所需。

以下内容整理自《redis深度历险》

1. 字符串内部结构

动态字符串,类似于ArrayList。

用途:常常用来存放一些json,如果存放数字可以用于自增(我们业务中的有需要递增的房间号就是用的redis~)

分配空间:也类似于ArrayList,实际空间会比字符串大。(但是指的是如果发生追加操作了)

扩容规则:如果字符串长度小于1MB的话,那么扩容是成倍的增加空间。

                  如果大于1MB的话,那么每次都会扩容1MB的空间

                   字符串的最大长度是500MB(应该指的是Redis中的结构类型)

1.1 字符串在java中的最大长度

关于存放位置:java字符串存在字符串常量池。字符串的常量池 是在方法区中。堆中存放实例对象。

字符串可以承受的最大长度,要分为两个阶段,编译时期还是运行时期。

因为Java有JVM常量池,对于字符串的性能优化,所以不用重复创建新的字符串,那么当我们用字符串

直接定义String时候,会把字符串存在常量池里面存储一份。

编译期看了一下资料65534是最大长度,

运行期:

String内部是以char数组的形式存储,数组的长度是int类型,那么String允许的最大长度就是Integer.MAX_VALUE了。又由于java中的字符是以16位存储的,因此大概需要4GB的内存才能存储最大长度的字符串。(如果使用javac的话编译不过去)

@Native public static final int   MAX_VALUE = 0x7fffffff; // 2 的 31 次方 - 1 = 2147483648 - 1 = 2147483647

1.2 位图数据结构

字符串由多个字节组成,一个字节是8个bit 所以可以说是bitmap的数据结构。

2. 源码篇

redis的编程语言:Redis是一个开源的使用ANSI C语言编写

字符串的内部实现是什么样呢?

redis中的字符串是以字节数组的形式存在的。bitmap

redis中的字符串叫做SDS 也就是Simple Dynamic String

String结构:

struct SDS<T> {

T capacity; //shuzu 容量

T len; //数组长度

byte flags; //特殊标志位,不用理睬

byte[] content; //数组内容

}

字符串是可以修改的,所以支持append的,那么需要冗余空间(但是也是后面扩容之后有冗余空间)。

  总之扩容机制概括:1.看冗余空间是否可以容纳

                                   2.如果不能够,那么扩容,然后复制原字符串到新的数组中。

                                   3.但是创建字符串的时候,len和capacity是一样长的,不会多分配冗余空间(第一次的时候),因为大部分                                           的场景下面,我们并不会用到append来操作修改字符串。

                                  

不过为什么SDS中的结构里面的容量和数组长度使用的是泛型T呢?

因为当字符串比较短的时候,len和和其中capacity

可以使用byte和short来表示吗,不一定一定要用int(感觉做设计就是能节省一点十一点)

不同长度的字符串使用不同的结构体表示。

redis规定字符串的长度不能超过512MB。

2.1 字符串的两种存储方式

2.2.1 embstr

在字符串特别短的时候使用这种方式

2.2.2 raw

超过44个字节使用这种方式。

2.2.3 为什么是44个字节?

主要原因是redis的的对象头结构,所有的redis对象都有下面的头结构!!!!

struct RedisObject {

int4 type;

int4 encoding; 

int24 lru; //是一种缓存机制

int32 refcount; //引用计数,引用为0的时候,被销毁 (可以关联到JVM的内存回收理解)

void *ptr;  //指向对象内容的具体存储位置,8byte

}

不同的对象有着不同的type。但是同一个type会有不同的存储形式也就是encoding.

LRU是一种内存淘汰机制。

那么我们需要上面的4 + 4 + 24 + 32 =64

然后再加上8字节的存储引用的空间,也就是16字节。

对于SDS的结构体,需要最小3个字节

struct SDS {

int8 capacity; //数组容量

int8 len; //数组长度

int8 flags; //特殊标志位,不用理睬

byte[] content; //数组内容

}

那么embstr就是一种将RedisObject 和 SDS的对象连续的存在了一起,然后使用malloc方法进行一次分配。

而raw是进行两次malloc的方法,也就是两个对象头在内存地址上面是不连续的。

总之因为字符串如果总体超过了64个字节,那么redis会认为是一个打的字符串不在适合用emdstr形式存储,应该用raw形式。

所以64个字节 应该是 44个字符串长度 + 字符串用null结尾(占了一个字节) + 19字节的头信息(是SDS+RedisObject 头信息)。

 

PS: 多出的null字节是为了方便直接使用glibc的字符串处理函数。

(需要知道redis字符串存储是null结尾的 占用一个字节)

2.2.4 为什么字符串长度大于1MB之后,每次只多分配1MB的冗余空间?

因为避免加倍之后冗余空间过大而浪费。

3. redis中的int4 ,int8

这个对java开发的我并不是很友好,因为忘记了是什么意思啊。

看了一下这个老哥的帖子: https://www.cnblogs.com/daguonice/p/11193884.html

这样会不会更直观:

       Int8, 等于Byte, 占1个字节.

    Int16, 等于short, 占2个字节. -32768 32767

    Int32, 等于int, 占4个字节. -2147483648 2147483647

    Int64, 等于long, 占8个字节. -9223372036854775808 9223372036854775807

    这样, 看起来比short,int,long更加直观些!

  另外, 还有一个Byte, 它等于byte, 0 - 255.    

那么这个时候,熟悉开发的肯定会联想到:

在mysql中int和bigint对应的默认是int(11) bigint(20)

但是他们实际上还是4个和8个字节。

因为:

int有符号类型 取值在 - 2147483648 ~ 2147483647之间

int无符号类型 取值在 0 ~ 4294967295之间

那么其实mysql中的int(11) 这个11也就是数字是多少位~

我们要在mysql中展示多少位~

 

总结:

接下来还要看其他四种的源码和底层实现,还有HashMap和ArrayList

也会跟这个这些底层结构应运而生~

加油DK 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值