数据模型

set hello word 为例,因为Redis 是KV 的数据库,它是通过hashtable 实现的(我们把这个叫做外层的哈希)。所以每个键值对都会有一个dictEntry(源码位置:dict.h),里面指向了key 和value 的指针。next 指向下一个dictEntry。

typedef struct dictEntry {void *key; /* key 关键字定义*/union {void *val; uint64_t u64; /* value 定义*/int64_t s64; double d;} v;struct dictEntry *next; /* 指向下一个键值对节点*/
} dictEntry;

key 是字符串,但是Redis 没有直接使用C 的字符数组,而是存储在自定义的SDS中。

value 既不是直接作为字符串存储,也不是直接存储在SDS 中,而是存储在redisObject 中。实际上五种常用的数据类型的任何一种,都是通过redisObject 来存储的。

redisObject

redisObject 定义在src/server.h 文件中。

typedef struct redisObject {unsigned type:4; /* 对象的类型,包括:OBJ_STRING、OBJ_LIST、OBJ_HASH、OBJ_SET、OBJ_ZSET */unsigned encoding:4; /* 具体的数据结构*/unsigned lru:LRU_BITS; /* 24 位,对象最后一次被命令程序访问的时间,与内存回收有关*/int refcount; /* 引用计数。当refcount 为0 的时候,表示该对象已经不被任何对象引用,则可以进行垃圾回收了*/void *ptr; /* 指向对象实际的数据结构*/
} robj;

可以使用type 命令来查看对外的类型。

127.0.0.1:6379> type qs
string
127.0.0.1:6379> set number 1
OK
127.0.0.1:6379> set qs "is a good teacher in gupao, have crossed mountains and sea "
OK
127.0.0.1:6379> set jack bighead
OK
127.0.0.1:6379> object encoding number
"int"
127.0.0.1:6379> object encoding jack
"embstr"
127.0.0.1:6379> object encoding qs
"raw"

字符串类型的内部编码有三种:

1、int,存储8 个字节的长整型(long,2^63-1)。

2、embstr, 代表embstr 格式的SDS(Simple Dynamic String 简单动态字符串),存储小于44 个字节的字符串。

3、raw,存储大于44 个字节的字符串(3.2 版本之前是39 字节)。为什么是39?

/* object.c */
#define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 44

问题1、什么是SDS?

Redis 中字符串的实现。

在3.2 以后的版本中,SDS 又有多种结构(sds.h):sdshdr5、sdshdr8、sdshdr16、sdshdr32、sdshdr64,用于存储不同的长度的字符串,分别代表2^5=32byte,2^8=256byte,2^16=65536byte=64KB,2^32byte=4GB。

/* sds.h */
struct __attribute__ ((__packed__)) sdshdr8 {
uint8_t len; /* 当前字符数组的长度*/
uint8_t alloc; /*当前字符数组总共分配的内存大小*/
unsigned char flags; /* 当前字符数组的属性、用来标识到底是sdshdr8 还是sdshdr16 等*/
char buf[]; /* 字符串真正的值*/
};

问题2、为什么Redis 要用SDS 实现字符串?

我们知道,C 语言本身没有字符串类型(只能用字符数组char[]实现)。

1、使用字符数组必须先给目标变量分配足够的空间,否则可能会溢出。

2、如果要获取字符长度,必须遍历字符数组,时间复杂度是O(n)。

3、C 字符串长度的变更会对字符数组做内存重分配。

4、通过从字符串开始到结尾碰到的第一个'\0'来标记字符串的结束,因此不能保存图片、音频、视频、压缩文件等二进制(bytes)保存的内容,二进制不安全。

SDS 的特点:

1、不用担心内存溢出问题,如果需要会对SDS 进行扩容。

2、获取字符串长度时间复杂度为O(1),因为定义了len 属性。

3、通过“空间预分配”( sdsMakeRoomFor)和“惰性空间释放”,防止多次重分配内存。

4、判断是否结束的标志是len 属性(它同样以'\0'结尾是因为这样就可以使用C语言中函数库操作字符串的函数了),可以包含'\0'。

问题3、embstr 和raw 的区别?

embstr 的使用只分配一次内存空间(因为RedisObject 和SDS 是连续的),而raw需要分配两次内存空间(分别为RedisObject 和SDS 分配空间)。

因此与raw 相比,embstr 的好处在于创建时少分配一次空间,删除时少释放一次空间,以及对象的所有数据连在一起,寻找方便。

而embstr 的坏处也很明显,如果字符串的长度增加需要重新分配内存时,整个RedisObject 和SDS 都需要重新分配空间,因此Redis 中的embstr 实现为只读。

问题4:int 和embstr 什么时候转化为raw?

当int 数据不再是整数, 或大小超过了long 的范围(2^63-1=9223372036854775807)时,自动转化为embstr。

127.0.0.1:6379> set k1 1
OK
127.0.0.1:6379> append k1 a
(integer) 2
127.0.0.1:6379> object encoding k1
"raw"

问题5:明明没有超过阈值,为什么变成raw 了?

127.0.0.1:6379> set k2 a
OK
127.0.0.1:6379> object encoding k2
"embstr"
127.0.0.1:6379> append k2 b
(integer) 2
127.0.0.1:6379> object encoding k2
"raw"

对于embstr,由于其实现是只读的,因此在对embstr 对象进行修改时,都会先转化为raw 再进行修改。

因此,只要是修改embstr 对象,修改后的对象一定是raw 的,无论是否达到了44个字节。

问题6:当长度小于阈值时,会还原吗?

关于Redis 内部编码的转换,都符合以下规律:编码转换在Redis 写入数据时完成,且转换过程不可逆,只能从小内存编码向大内存编码转换(但是不包括重新set)。

问题7:为什么要对底层的数据结构进行一层包装呢?

通过封装,可以根据对象的类型动态地选择存储结构和可以使用的命令,实现节省空间和优化查询速度。

Redis存储(实现)原理相关推荐

  1. redis 存储结构原理 2

    咱们接着上一部分来进行分享,我们可以在如下地址下载 redis 的源码: https://redis.io/download [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(im ...

  2. Redis如何实现分布式锁延时队列以及限流应用丨Redis源码原理|跳表|B+树|分布式锁|中间件|主从同步|存储原理

    Redis如何实现分布式锁延时队列以及限流应用 视频讲解如下,点击观看: Redis如何实现分布式锁延时队列以及限流应用丨Redis源码原理|跳表|B+树|分布式锁|中间件|主从同步|存储原理|数据模 ...

  3. 后端底层开发人员必须要彻底搞懂的redis存储原理丨redis跳表与B+树详细对比

    后端底层开发人员必须要彻底搞懂的redis存储原理 1. redis存储原理分析 2. redis源码学习分享 3. redis跳表与B+树详细对比分析 视频讲解如下,点击观看: 后端底层开发人员必须 ...

  4. 10年大厂程序员是如何高效学习使用redis的丨redis源码分析丨redis存储原理

    10年大厂程序员是怎么学习使用redis的 1. redis存储原理分析 2. redis源码学习分享 3. redis跳表和B+树详细对比分析 视频讲解如下,点击观看: 10年大厂程序员是如何高效学 ...

  5. 解析redis存储结构丨 redis与mysql存储对比丨redis存储原理分析

    90分钟视频讲解搞懂redis存储原理 1. redis与mysql存储对比 2. redis存储原理分析 3. redis存储与持久化的关系 [后端开发系列]解析redis存储结构丨 redis与m ...

  6. Redis存储总是心里没底?你大概漏了这些数据结构原理

    上一篇文章<Redis存储总用String?你大概错过了更优的使用方法>我们了解了Redis的数据类型特点与适用场景,本期内容则会着重讲讲Redis内存数据结构与编码,弄清Redis内部到 ...

  7. 大容量类 Redis 存储的场景补充-pika

    2019独角兽企业重金招聘Python工程师标准>>> 导读 我们在<大容量类 Redis 存储 - 有关 pika 的一切>里介绍过pika的诞生.pika的特点.pi ...

  8. redis高并发原理_Java中的42行代码中的URL缩短服务— Java(?!)Spring Boot + Redis...

    redis高并发原理 显然,编写URL缩短服务是新的"世界,您好! "在物联网/微服务/时代的世界中. 一切始于在45行Scala中的URL缩短服务-整洁的Scala,以Spray ...

  9. Redis 主从复制的原理及演进

    本文作者:百度基础架构部工程师,王钰 Redis 的主从复制经历了多次演进,本文将从最基本的原理和实现讲起,并层层递进,逐步呈现 Redis 主从复制的演进历史.大家将了解到 Redis 主从复制的原 ...

  10. Redis压缩列表原理与应用分析

    摘要 Redis是一款著名的key-value内存数据库软件,同时也是一款卓越的数据结构服务软件.它支持字符串.列表.哈希表.集合.有序集合五种数据结构类型,同时每种数据结构类型针对不同的应用场景又支 ...

最新文章

  1. 继承和多态 2.0 -- 继承的六个默认成员函数
  2. MongoDB分片实战(三):性能和优化
  3. 【STM32】FreeRTOS 任务切换
  4. am335x linux内核烧写_实时 Linux 抖动分析 Step by step
  5. 前端能读取压缩包内容吗?_解决前端多环境部署的痛点
  6. 信息系统项目管理师:第6章:项目进度管理(2)-章节重点汇总
  7. 使用 ortp 发送原始 H.264 码流
  8. 计算机组装维修期末考试题,2014计算机组装维修期末考试题(1)
  9. javaBean【02】javaBean与表单应用
  10. C# 10 新特性 —— 补充篇
  11. python中log1p用法_python中logging模块的基本用法
  12. apache-commons 常用工具类
  13. pascal voc数据集_【资源分享】数据集搜索神器BIFROST
  14. 中低频量化交易策略研发05_推进分析
  15. SQL Server插入binary类型的数据
  16. 设计大师Donald Norman和Bill Buxton签书会在南京举行
  17. maven常用插件: 打包源码 / 跳过测试 / 单独打包依赖项
  18. Java常用设计模式及应用场景介绍
  19. installshield使用教程
  20. Subsonic简单的语法整理

热门文章

  1. lua IDE all
  2. 在Linux服务器上配置phpMyAdmin--允许空密码
  3. jQuery修改页面元素的属性
  4. mysql bin log日志
  5. [你必须知道的css系列]第一回:丰富的利器:CSS选择符之通配符、类选择符、包含选择符、ID选择符...
  6. Google Chrome等浏览器不允许关闭点击跟踪??
  7. 关于 UTXO 的思考
  8. sass部分知识小结
  9. 自然语言交流系统 phxnet团队 创新实训 个人博客 (五)
  10. 让人难过的 openssl_pkcs7_encrypt