Redis字符串原理的深入理解

前言

来掘进都有两年多了一直当个小透明,今天终于发一次文章了.

最近在看 Redis,感觉收获很多,写篇博客记录一下.

Redis 有五种基础数据结构:string,list,set,zset,hash.其中 string是最最最简单的也是最常用的.这个数据类型虽然简单但是内部的结构设计却很是精致.

基本介绍

相比于 Java,在 Redis 中 string 是可以修改的,是动态字符串(Simple Dynamic String 简称 SDS)他的内部结构更像是一个 ArrayList,维护一个字节数组并预分配冗余空间以减少内存的频繁分配.当字符串的长度小于 1MB时,每次扩容都是加倍现有的空间,如果字符串长度超过 1MB 时,每次扩容时只会扩展 1MB 的空间.

ps:字符串长度为最大长度 512MB.

> set name test
OK
> get name
"test"
> mset name1 test1 name2 test2
OK
> mget name1 name2
1) "test1"
2) "test2"
> del name
(integer) 1

上面是字符串的基本操作 命令mset 和 mget 可以对多个字符串读写 节省网络开销

不仅如此redis 的字符串还可以用来储存整数(更不像Java 的字符串了),并且可以自增操作.字符串保存整数类型的的范围在 至
如果保存的数大于这个取值范围就会变成普通字符类型 无法自增操作.这将由字符串编码格式决定.

字符串由多个字节组成,每个字节有 8bit.这样的数据结构还可以当做 bitmap 去使用.

> set foo 1
OK
> get foo
"1"
> incr foo
(integer) 2
> get foo
"2"

内部原理

基本实现

上图所示为字符串的基本结构,其中 content 里面保存的是字符串内容,和 c 一样用 0x\0作为结束字符.这个结束字符不会被计算len 中.代码如下:

struct SDS{
  T capacity;		//数组容量
  T len;			//实际长度
  byte flages;	//标志位,低三位表示类型
  byte[] content;	//数组内容
}

可以看到 capacity和len 都是泛型,为什么不直接使用 int 呢?因为 Redis 内部做了很多优化,为了减少内存的使用不同长度的字符串会使用不同的数据类型去表示.并且在创建字符串的时候 len 会和 capacity 一样大,没有冗余的空间,因为修改字符串的场景很少.(Redis 真的将内存优化到了极致)

编码格式

Redis 字符串编码格式有这么几种:int 编码、embstr编码和raw 编码 下面就详细介绍下这几种编码的区别.

在这之前先要说说RedisObject. Redis 的对象头,所有的 Redis 对象都有下面这个头部结构.

struct RedisObject{
  int4 type;		//数据类型 5 种
  int4 encoding;	//键值内部编码格式 int 或 embstr 等等
  int24 lru;		// 当内存超限时采用LRU算法清除内存中的对象

  int32 refcount;	//改键值被引用的数量
  void *ptr;		//对象内容
}

int 编码

当储存的值是64 位有符号整数类型的时候将会采用 int  编码,这时可以使用键值自增操作.Redis 在启动时会建立1w 个redisObject共享对象下文会讲到,值在[0,1000)之间.如果存入整数的值在[0,1000)中Redis将不会创建新的对象,而是直接指向共享对象,键值不额外占用空间.

使用 object encoding命令可以查看编码格式 使用 debug object命令可以查看更多信息

> set foo 1
OK
> object encoding foo
"int"
> set foo2 1
OK
> debug object foo
Value at:0x7f44b020aca0 refcount:2147483647 encoding:int serializedlength:2 lru:14691591 lru_seconds_idle:72588
> debug object foo2
Value at:0x7f44b020aca0 refcount:2147483647 encoding:int serializedlength:2 lru:14691591 lru_seconds_idle:72594

可以看到 foo 和 foo2  都在0x7f44b020aca0这里指向的是同一个对象

embstr 编码

当存储的字符串长度较短时(len<=44 字节),Redis将会采用 embstr 编码.embstr 即embedded string 嵌入式的字符串.将SDS结构体嵌入RedisObject对象中, 使用 malloc 方法一次分配内存地址是连续的.

如图所示:

raw 编码

当存储的字符串长度较长时(len>44 字节),Redis 将会采用 raw 编码,和 embstr 最大的区别就是 RedisObject 和 SDS 不在一起了,内存地址不再连续了.

如图所示:

思考

为什么字符串会有两种格式 embstr 和格式和 raw分界线是 44 个字节?

Redis 默认的内存分配器jemalloc分配内存大小的单位是次方,为了容纳一个完整的 embstr 对象,最少会分配 32 字节的空间,再长些就是 64 字节,再之后就认为这是一个大字符串不适合用 embstr 存储,而改用 raw 编码了.

那么问题来了,64 字节的空间字符串长度是多少呢?答案就是 44 字节.

下图中 content 的长度为 45 字节减去结尾的 0x\0,就剩下 44 字节了.

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我们的支持。

(0)

相关推荐

  • Redis字符串对象实用笔记

    字符串对象 字符串数据类型是Redis里最常用的类型了,它的键和值都是字符串,使用起来非常的方便.虽然字符串数据类型的值都统称为字符串了,但是在实际存储时会根据值的不同自动选择合适的编码.字符串对象的编码一共有三种:int.raw.embstr. Redis对象 Redis用统一的数据结构来表示一个对象,具体定义如下: typedef struct redisObject { unsigned type:4; unsigned encoding:4; // 当内存超限时采用LRU算法清除内存中的

  • Redis字符串类型的常用命令小结

    Redis字符串类型 字符串类型是Redis中最为基础的数据存储类型,它在Redis中是二进制安全的,这便意味着该类型可以接受任何格式的数据,如JPEG图像数据或Json对象描述信息等.在Redis中字符串类型的Value最多可以容纳的数据长度是512M. 一.最简单的命令 1.获得符合规则的键名列表 keys * 这里的*号,是指列出所有的键,同时*号也可以替换成其他支持glob风格通配符格式,具体规则如下: ?:匹配一个字符 *:匹配任意个(包括0个)字符 []:匹配括号间多大任一个字符,可

  • redis命令行查看中文不乱码的方法(十六进制字符串处理)

    redis命令行查看中文不乱码 Redis在使用命令行操作时,如果查看内容中包含中文,会显示16进制的字符串"\xe4\xb8\xad\xe5\x9b\xbd" 127.0.0.1:6379> set k1 '中国' OK 127.0.0.1:6379> get k1 "\xe4\xb8\xad\xe5\x9b\xbd" 如果想要看到的中文不乱码,解决方案有两种: 一.使用echo $ echo -e `redis-cli get k1` 中国 二.re

  • redis内部数据结构之SDS简单动态字符串详解

    前言 reids 没有直接使用C语言传统的字符串表示(以空字符结尾的字符数组)而是构建了一种名为简单动态字符串的抽象类型,并为redis的默认字符串表示,因为C字符串不能满足redis对字符串的安全性.效率以及功能方面的需求 1.SDS 定义 在C语言中,字符串是以'\0'字符结尾(NULL结束符)的字符数组来存储的,通常表达为字符指针的形式(char *).它不允许字节0出现在字符串中间,因此,它不能用来存储任意的二进制数据. sds的类型定义 typedef char *sds; 每个sds

  • Redis中的动态字符串学习教程

    sds 的用途 Sds 在 Redis 中的主要作用有以下两个: 实现字符串对象(StringObject): 在 Redis 程序内部用作 char* 类型的替代品: 以下两个小节分别对这两种用途进行介绍. 实现字符串对象 Redis 是一个键值对数据库(key-value DB), 数据库的值可以是字符串.集合.列表等多种类型的对象, 而数据库的键则总是字符串对象. 对于那些包含字符串值的字符串对象来说, 每个字符串对象都包含一个 sds 值. "包含字符串值的字符串对象",这种说

  • redis字符串类型_动力节点Java学院整理

    我们都知道redis是采用C语言开发,那么在C语言中表示string都是采用char[]数组的,然后你可能会想,那还不简单,当我执行如下命令,肯定是直接塞给char[]数组的. 如果你真的这么想的话,会有几个问题就要过来砍你了,先我们来找一个redis手册,http://doc.redisfans.com/ 第一:如果你每次都执行Append函数,那是不是redis的char[]每次都需要再次扩容,这样是不是每次都是耗时操作呢? 第二:如果你每次执行String中的StrLen,那redis底层

  • Redis字符串原理的深入理解

    前言 来掘进都有两年多了一直当个小透明,今天终于发一次文章了. 最近在看 Redis,感觉收获很多,写篇博客记录一下. Redis 有五种基础数据结构:string,list,set,zset,hash.其中 string是最最最简单的也是最常用的.这个数据类型虽然简单但是内部的结构设计却很是精致. 基本介绍 相比于 Java,在 Redis 中 string 是可以修改的,是动态字符串(Simple Dynamic String 简称 SDS)他的内部结构更像是一个 ArrayList,维护一

  • Redis核心原理与实践之字符串实现原理

    本文分析Redis字符串的实现原理,内容摘自新书<Redis核心原理与实践>.这本书深入地分析了Redis常用特性的内部机制与实现方式,内容源自对Redis源码的分析,并从中总结出设计思路.实现原理.通过阅读本书,读者可以快速.轻松地了解Redis的内部运行机制. Redis是一个键值对数据库(key-value DB),下面是一个简单的Redis的命令: > SET msg "hello wolrd" 该命令将键"msg".值"hell

  • 详解Redis复制原理

    前言 本文主要介绍Redis复制机制 一.配置与实践 配置 Redis实例分为主节点(master)和从节点(slave),默认情况下都是主节点.每一个从节点只能有一个主节点,但是每一个主节点可以有多个从节点(注意数量,多个从节点会导致主节点写命令多次发送从而过度消耗网络带宽,可用树状结构降低主节点负载).复制是单向的,只能从主节点复制到从节点.配置复制的方式由以下3种: 在redis-slave.conf配置文件中加入slaveof {masterHost} {masterPort} 在red

  • 解析Redis Cluster原理

    目录 一.前言 二.为什么需要Redis Cluster 三.Redis Cluster是什么 四.节点负载均衡 五.什么是一致性哈希 六.虚拟节点机制 七.Redis Cluster采用的什么算法 八.Redis Cluster如何做到高可用 8.1.集群如何进行扩容 8.2.高可用及故障转移 九.简单了解gossip协议 十.gossip协议消息类型 十一.使用gossip的优劣 十二.总结 一.前言 Sentinel集群会对Redis的主从架构中的Redis实例进行监控,一旦发现了mast

  • Redis源码阅读:Redis字符串SDS详解

    SDS 基本概念 简单动态字符串(Simple Dynamic String)SDS,用作Redis 的默认字符串. C语言中的字符串:以空字符结尾的字符数组 SDS实现举例 redis > SET msg "hello world" OK 我们通过 SET 在 Redis 数据库中创建了一个数据键对象为 "msg" 和 数据值对象为 "hello world" 的键值对,其中数据键和数据值对象底层的字符串实现都是 SDS .同时, SDS

  • Java 关于String字符串原理上的问题

    目录 1.String 是最基本的数据类型吗? 2.字符型常量和字符串常量的区别? 3.什么是字符串常量池? 4.String str=new String("aaa")创建了几个对象? 5.字符串的 intern 方法的执行过程是怎样的? 6.String,StringBuffer,StringBuilder 的区别是什么? 7.String为什么要设计成不可变的? 1.String 是最基本的数据类型吗? 不是. Java 中的基本数据类型只有 8 个 :byte.short.in

  • Redis内存碎片原理深入分析

    目录 前言 释放的内存去了哪里? 什么是内存碎片? 什么导致内存碎片? 如何解决? 总结 前言 我们先来看一个问题, 假设Redis实例保存了5GB的数据,现在删除了2GB的数据,那么Redis进程占用的内存会不会减少呢? 答案是:它可能仍然占用大约5GB内存,即使Redis数据只占用大约3GB. 如果maxmemory不设置该参数,Redis不会触发内存淘汰策略删除数据. Redis会继续为新写入的数据分配内存.分配失败会导致应用程序报错,当然不会导致宕机. 注:设置maxmemory参数,执

  • java 字符串池的深入理解

    java 字符串池的深入理解 字符串池是Java的一个特性,可以通过一段代码理解. String a = "Hello"; String b = "Hello"; String c = new String("Hello"); String d = new String("Hello"); System.out.println(a == b); System.out.println(b == c); System.out.pr

  • Java String 拼接字符串原理详解

    首先来一道思考题: String str1 = "111111"; String str2 = "222222"; String str = str1 + str2; System.out.println(str); 很明确,上述代码输出的结果是:"111111222222",但是它工作原理是怎样的呢? 由于字符串拼接太常用了,java才支持可以直接用+号对两个字符串进行拼接.**其真正实现的原理是中间通过建立临时的StringBuilder对象

随机推荐