Redis 原理 - String

火影  金牌会员 | 2022-8-20 09:34:59 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 870|帖子 870|积分 2610

String 数据结构

首先我来看下, Redis 中 String 的数据结构:
我们称之为 SDS (Simple Dynamic String) 简单动态字符串
  1. struct sdshdr {
  2.     //记录buf数组中已经使用的字节数(等价于字符串的长度strlen)
  3.     int len;
  4.     //记录buf数组中未使用的字节数(用于动态扩容)
  5.     int free;
  6.     //字节数组,用于保存字符串
  7.     char buf[];
  8. }
复制代码
对比C语言字符串:

  • 优化了获取字符串长度的时间复杂度为: O(1)
    因为 SDS 维护了一个 len 字段,这个字段的设置和更新是由 SDS 的 API 在执行时自动完成的。

  • 杜绝缓冲区溢出
    在使用 c 语言函数 strcat(s1, " Cluster") (在 s1 字符串后面拼接另一个字符串) 时,如果忘记提前为 s1 分配足够的空间,那么 strcat 函数执行之后, 将会导致s1后面的空间内容被意外修改。
    而 Redis 在对 SDS 修改时,会提前检查长度时候足够,才执行相关操作, 如果长度不够,会先扩展 s 的空间, 再执行

  • 减少内存重分配
    在 SDS 维护了 free 字段,可以知道未使用的字节数,这样在动态扩充字符串时可以根据这个值判断要不要重新分配内存

  • 二进制安全
    C语言是依靠 \0 作为字符串的结束标志的,这意味着字符串中间不能再次包含 \0 字符, 而 SDS 记录了字符串的实际长度, 所以它可以做更多的事情, 比如保存图片

  • 兼容部分 C字符串函数
    虽然 SDS 是 二进制安全的,但他们都遵守在字符串末尾额外分配一个字节容纳 \0 当结尾,这就是为了让那些保存文本数据的 SDS 可以重用  库

RedisObject 数据结构

在redis中存储的每个对象都表示为一个redisObject,它记录了这个对象的数据类型,编码方式,指向实际内容的指针等
  1. typedef struct redisObject {
  2.     // 类型 4bits (String,Hash,Set,List等)
  3.     unsigned type:4;
  4.     // 编码方式 4bits (int, embstr, raw 等)
  5.     unsigned encoding:4;
  6.     // LRU 时间(相对于 server.lruclock) 24bits
  7.     unsigned lru:22;
  8.     // 引用计数 Redis里面的数据可以通过引用计数进行共享 32bits
  9.     int refcount;
  10.     // 指向实际存储的对象 (比如:指向一个SDS字符串对象) 64bits
  11.     void *ptr;
  12. } robj;
复制代码
String的三种编码方式


  • int
    当保存的值为整数且值的大小不超过long的范围,使用整数存储

  • embstr
    当字符串长度不超过44个字节时,使用embstr编码
    (只实现一次分配内存空间,只允许读,若修改数据,就会转成raw编码)

  • raw
    大于44字节时,用raw编码

int编码不必多说,为了数值计算方便
为什么要分为 embstr 和 raw 呢?

首先来张图,直观感受下,这2种存储方式的内存布局:


  • embstr 是连续存储的,也就是说 RedisObject 对象头 和 SDS 字符串的实际内容 是连续在一起存储的,也就是 RedisObject 中的ptr 指向的内容,就紧跟着在后面 (只要分配1次内存)
  • raw 是不连续存储的, ptr 指向的内容,是单独分配的(要分配2次内存)
这么做的原因当然是为了性能考虑,Redis考虑到多数字符串,可能都不会很长, 这样使用 embstr 只需分配一次内存(也很小),而且还是连续的存储,性能很高,也有利于减少内存碎片
String编码演示


  • 使用 TYPE key 查看对象的数据类型
  • 使用 OBJECT ENCODING key 查看对象的编码方式
演示 int 编码:
  1. 127.0.0.1:6379> set age 100
  2. OK
  3. 127.0.0.1:6379> type age
  4. string
  5. 127.0.0.1:6379> object encoding age
  6. "int"
复制代码
演示 embstr 编码:
  1. 127.0.0.1:6379> SET name tom
  2. OK
  3. 127.0.0.1:6379> TYPE name
  4. string
  5. 127.0.0.1:6379> OBJECT ENCODING name
  6. "embstr"
复制代码
演示 raw 编码:
  1. 127.0.0.1:6379> set name sdsdnjjkasjdnnjasjdnasldklaksdkkansndmasdulasndnkadashdahsdhkpasduasdybasbdvcfaffafkgsaas
  2. OK
  3. 127.0.0.1:6379> type name
  4. string
  5. 127.0.0.1:6379> object encoding name
  6. "raw"
复制代码
String的常用命令


  • SET: 添加或修改一个已经存在的String类型的键值对
  • GET: 根据key获取String类型的value
  • INCR: 让一个整形存储的String类型的value自增1
  • INCRBY: 让一个整形存储的String类型的value自增指定的步长, 例如 incrby num 2, 让num的值自增2
  • INCRBYFLOAT: 让一个浮点类型的数值自增指定的步长
  • SETNX: 添加一个String类型的键值对,前提是这个key不存在,否则不执行(可用于实现分布式锁)
  • SETEX: 添加一个String类型的键值对,并且指定超时时间
  • 更多 String 命令,请查阅 官方文档

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

火影

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表