Redis 设计与实现 6:五大数据类型之字符串
前文 Redis 设计与实现 2:Redis 对象 说到,五大数据类型都会封装成 RedisObject。
typedef struct redisObject {
unsigned type:4; // 类型
unsigned encoding:4; // 编码
// ...
void *ptr; // 指向具体底层数据的指针
} robj;
不同数据类型的主要区别就是 type 和 encoding 属性的差异,同一种数据类型,有不同的编码。
一、编码类型
字符串的编码有raw、embstr、int三种。
raw用于长字符串。embstr用于短字符串。int用于整数类型。
定义在 server.h 中,这里只列出 string 类型的编码
#define OBJ_ENCODING_RAW 0
#define OBJ_ENCODING_INT 1
#define OBJ_ENCODING_EMBSTR 8
编码 1:raw
raw 编码主要用来保存长度超过 44 的字符串。其真实数据,由 sdshdr 结构来表示存储,外层还是由 redisObject 包装。
sdshdr 的结构在前文 Redis 设计与实现 3:字符串 SDS 中有讲到。
sdshdr 结构大致如下:

redisObject 中的 ptr 指针,就是指向 sds。

编码 2:embstr
embstr 编码是专门用于保存短字符串的一种优化编码方式。当字符串的长度小于等于 44 的时候,将采用 embstr 编码。
创建字符串对象的代码如下(object.c):
#define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 44
robj *createStringObject(const char *ptr, size_t len) {
if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT)
return createEmbeddedStringObject(ptr,len);
else
return createRawStringObject(ptr,len);
}
embstr 有个显著的特点,就是 redisObject 跟 sds 的内存是挨在一起的。挨在一起的好处:
- 分配内存的时候,只需要分配一次。而
raw编码的sds跟redisObject分离,就要分配两次内存。 - 同样,释放内存也只需要释放一次。
- 连续内存能更好利用内存带来的优势。
embstr 问题一:那么为什么 embstr 跟 raw 的界限是 44 呢?
embstr的sds使用了sdshdr8,sdshdr8头占用了 3 个字节:
struct __attribute__ ((__packed__)) sdshdr8 {
uint8_t len; /* 1 字节 */
uint8_t alloc; /* 1 字节 */
unsigned char flags; /* 1 字节 */
char buf[];
};
- 另外还有
redisObject占用 16 个字节 (4 + 4 + 24 + 32 + 64 = 128位):
typedef struct redisObject {
unsigned type:4;
unsigned encoding:4;
unsigned lru:LRU_BITS; // #define LRU_BITS 24
int refcount; // 32 位
void *ptr; // 64 位
} robj;
redisObject + sdshdr8 至少需要 3 + 16 = 19 字节。
redis 认为如果超过 64 字节就是大字符串,所以在 redisObject+ sdshdr8 的总长度是 64 字节的情况下,留给 buf 的长度就只剩下 45 字节,由于字符串结尾需要一个 \0 占用一个字节,所以留个字符串的长度就只有 44 字节了。
公式:64 - 3(sdshdr8 ) - 16(redisObject) - 1(\0) = 44
embstr 问题二:为什么网上有的博文说 embstr 跟 raw 的界限是 39
在 redis 3.2 版本之前,这个界限的确是 39,为什么后面改成 44 了呢?
那是因为 sdshdr 的结构在 3.2 版本的时候修改了。3.2 之前的 sdshdr 结构是:
struct sdshdr {
unsigned int len; // 4 字节
unsigned int free; // 4 字节
char buf[];
};
旧版本的 sdshdr 的头占用了 8 个字节,比新版本的多了 5 个字节,所以界限就是 44 - 5 = 39 啦!
编码 3:int
如果一个字符串对象保存的是整数值,并且这个整数值可以用 long 类型来表示,那么这个整数值将会保存在字符串对象结构的 ptr 属性里面(将 void* 转换成 long),并将字符串对象的编码设置为 int。
相对于用 raw 编码,int 编码既节省了指针占用的内存,也节省了sds结构的内存。
redis> SET int_key 12345
OK
redis> OBJECT ENCODING int_key
"int"
下图为存着 12345 的 string 示例结构:

二、编码的转换
1. int 转 raw
- 当字符串传的不是整数的时候,int 就会转成 raw 编码。
- 如果执行了一些修改的命令,如
append等(set不算),都会转成raw编码。因为这些操作只有字符串才支持。 - 一旦编码变为
raw之后,将不会再转成embstr
127.0.0.1:6379> SET num 1
OK
127.0.0.1:6379> OBJECT ENCODING num
"int"
127.0.0.1:6379> APPEND num 2
(integer) 2
127.0.0.1:6379> OBJECT ENCODING num
"raw"
127.0.0.1:6379> SET num 12
OK
127.0.0.1:6379> OBJECT ENCODING num
"int"
2. embstr 转 raw
- 如果执行了一些修改的命令,如
append等,都会转成raw编码,不管修改后字符串的长度。因为没有给embstr编码实现修改接口,所以实际上embsr是只读的。 - 一旦编码变为
raw之后,将不会再转成embstr
三、重点回顾
- 字符串对象有三种编码,
raw、embstr、int raw负责保存长字符串;embstr负责保存短字符串;int负责保存整数。int和embstr在修改的时候,会转成raw编码,并且不再转回
本文的分析没有特殊说明都是基于 Redis 6.0 版本源码
redis 6.0 源码:https://github.com/redis/redis/tree/6.0
Redis 设计与实现 6:五大数据类型之字符串的更多相关文章
- redis学习(七)——五大数据类型总结:字符串、散列、列表、集合和有序集合
目录 字符串类型(String) 散列类型(Hash) 列表类型(List) 集合类型(Set) 有序集合类型(SortedSet) 其它命令 一.字符串类型(String) 1.介绍: 字符串类型是 ...
- Redis设计与实现读书笔记——简单动态字符串
前言 项目里用到了redis数据结构,不想只是简单的调用api,这里对我的读书笔记做一下记录.原文地址: http://www.redisbook.com/en/latest/internal-dat ...
- 前端总结·基础篇·JS(一)五大数据类型之字符串(String)
前端总结系列 前端总结·基础篇·CSS(一)布局 前端总结·基础篇·CSS(二)视觉 前端总结·基础篇·CSS(二)补充 前端总结·基础篇·JS(一)五大数据类型之字符串(String) 目录 这是& ...
- 【笔记】《Redis设计与实现》chapter2 简单动态字符串
------------恢复内容开始------------ 2.1 SDS的定义 struct sdshdr{ // 记录buf数组中已使用字节的数量 // 等于SDS所保存字符串的长度(不含'\0 ...
- Redis详解(五)------ redis的五大数据类型实现原理
前面两篇博客,第一篇介绍了五大数据类型的基本用法,第二篇介绍了Redis底层的六种数据结构.在Redis中,并没有直接使用这些数据结构来实现键值对数据库,而是基于这些数据结构创建了一个对象系统,这些对 ...
- Redis 详解 (五) redis的五大数据类型实现原理
目录 1.对象的类型与编码 ①.type属性 ②.encoding 属性和 *prt 指针 2.字符串对象 3.列表对象 4.哈希对象 5.集合对象 6.有序集合对象 7.五大数据类型的应用场景 8. ...
- redis五大数据类型以及常用操作命令
Redis的五大数据类型 String(字符串) string是redis最基本的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value.string类型是二进制安全的.意 ...
- redis 五大数据类型使用
redis 五大数据类型使用 字符串str 单个值 127.0.0.1:6379> set name pp # 设置键值[O(1)] OK 127.0.0.1:6379> setex na ...
- redis的五大数据类型实现原理
1.对象的类型与编码 Redis使用前面说的五大数据类型来表示键和值,每次在Redis数据库中创建一个键值对时,至少会创建两个对象,一个是键对象,一个是值对象,而Redis中的每个对象都是由 redi ...
随机推荐
- 朴素的模式匹配算法BF
1 #include <iostream> 2 using namespace std; 3 int BF(char S[], char T[]); 4 int main() 5 { 6 ...
- SpringCloud 源码系列(1)—— 注册中心 Eureka(上)
Eureka 是 Netflix 公司开源的一个服务注册与发现的组件,和其他 Netflix 公司的服务组件(例如负载均衡.熔断器.网关等)一起,被 Spring Cloud 整合为 Spring C ...
- 【SDOI2013】JZOJ8月3日提高组T4 直径
题目 题目描述 小 Q 最近学习了一些图论知识.根据课本,有如下定义. 树:无回路且连通的无向图,每条边都有正整数的权值来表示其长度.如果一棵树有 N 个节点,可以证明其有且仅有 N-1 条边. 路径 ...
- paddleocr安装笔记
下载解压安装 wget http://npm.taobao.org/mirrors/python/3.7.6/Python-3.7.6.tgztar xvf Python-3.7.6.tgzcd Py ...
- day6(celery原理与组件)
1.Celery介绍 1.1 celery应用举例 Celery 是一个 基于python开发的分布式异步消息任务队列,通过它可以轻松的实现任务的异步处理,如果你的业务场景中需要用到异步任务,就可以考 ...
- Java数据结构(十四)—— 平衡二叉树(AVL树)
平衡二叉树(AVL树) 二叉排序树问题分析 左子树全部为空,从形式上看更像一个单链表 插入速度没有影响 查询速度明显降低 解决方案:平衡二叉树 基本介绍 平衡二叉树也叫二叉搜索树,保证查询效率较高 它 ...
- Go语言基础--1.1 变量的声明
1.标准格式: var name type (var 关键字 name 变量名 type 类型) 命名规则:建议使用驼峰命名法 例如:var userName string var ...
- DVWA SQL Injection Medium
Mdeium 基本的步骤及知识点在上节已经提到过这里不再赘述:https://blog.csdn.net/WQ_BCJ/article/details/84554777 1)与low级别不同,本次采用 ...
- 3、pytorch实现最基础的MLP网络
%matplotlib inline import numpy as np import torch from torch import nn import matplotlib.pyplot as ...
- Scrum 冲刺 第五篇
Scrum 冲刺 第五篇 每日会议照片 昨天已完成工作 队员 昨日完成任务 黄梓浩 初步完成app项目架构搭建 黄清山 完成部分个人界面模块数据库的接口 邓富荣 完成后台首页模块数据库的接口 钟俊豪 ...