侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

Redis字符串对象实用笔记

Redis  /  管理员 发布于 5年前   281

字符串对象

字符串数据类型是Redis里最常用的类型了,它的键和值都是字符串,使用起来非常的方便。虽然字符串数据类型的值都统称为字符串了,但是在实际存储时会根据值的不同自动选择合适的编码。字符串对象的编码一共有三种:int、raw、embstr。

Redis对象

Redis用统一的数据结构来表示一个对象,具体定义如下:

typedef struct redisObject { unsigned type:4; unsigned encoding:4; // 当内存超限时采用LRU算法清除内存中的对象 unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or       * LFU data (least significant 8 bits frequency       * and most significant 16 bits access time). */ // 该对象被引用数 int refcount; // 对象的值指针 void *ptr;} robj;

其中type字段代表对象的类型,取值一共有7种:

/* A redis object, that is a type able to hold a string / list / set *//* The actual Redis Object */#define OBJ_STRING 0 /* 字符串对象. */#define OBJ_LIST 1  /* 列表对象. */#define OBJ_SET 2  /* 集合对象. */#define OBJ_ZSET 3  /* 有序集合对象. */#define OBJ_HASH 4  /* 哈希对象. *//* The "module" object type is a special one that signals that the object * is one directly managed by a Redis module. In this case the value points * to a moduleValue struct, which contains the object value (which is only * handled by the module itself) and the RedisModuleType struct which lists * function pointers in order to serialize, deserialize, AOF-rewrite and * free the object. * * Inside the RDB file, module types are encoded as OBJ_MODULE followed * by a 64 bit module type ID, which has a 54 bits module-specific signature * in order to dispatch the loading to the right module, plus a 10 bits * encoding version. */#define OBJ_MODULE 5 /* 模块对象. */#define OBJ_STREAM 6 /* 流对象. */

然后是encoding字段,代表着对象值的实际编码类型,取值一共有11种:

/* Objects encoding. Some kind of objects like Strings and Hashes can be * internally represented in multiple ways. The 'encoding' field of the object * is set to one of this fields for this object. */#define OBJ_ENCODING_RAW 0  /* 简单动态字符串 */#define OBJ_ENCODING_INT 1  /* long类型的整数 */#define OBJ_ENCODING_HT 2  /* 字典 */#define OBJ_ENCODING_ZIPMAP 3 /* 压缩字典 */#define OBJ_ENCODING_LINKEDLIST 4 /* 不再使用的旧列表,使用双端链表. */#define OBJ_ENCODING_ZIPLIST 5 /* 压缩列表 */#define OBJ_ENCODING_INTSET 6 /* 整数集合 */#define OBJ_ENCODING_SKIPLIST 7 /* 跳跃表和字典 */#define OBJ_ENCODING_EMBSTR 8 /* embstr编码的简单动态字符串 */#define OBJ_ENCODING_QUICKLIST 9 /* 编码为ziplist的列表 */#define OBJ_ENCODING_STREAM 10 /* 编码为listpacks的基数树 */

前面已经提到字符串对象只用到了long类型的整数、简单动态字符串、embstr编码的简单动态字符串这三种编码。

OBJ_ENCODING_INT

当字符串对象的值是一个整数且可以用long来表示时,字符串对象的编码就会是OBJ_ENCODING_INT编码。

可以看到,当值非常大的时候还是用OBJ_ENCODING_RAW来存储的。

OBJ_ENCODING_RAW

当字符串对象的值是一个字符串且长度大于44字节时,字符串对象的编码就会是OBJ_ENCODING_RAW编码。具体结构在下文。

OBJ_ENCODING_EMBSTR

当字符串对象的值是一个字符串且长度小于等于44字节时,字符串对象的编码就会是OBJ_ENCODING_EMBSTR编码。OBJ_ENCODING_EMBSTR编码和OBJ_ENCODING_RAW编码的区别主要有以下几点:

  • OBJ_ENCODING_RAW编码的对象在分配内存时会分配两次,分别创建redisObject对象和SDS对象。而OBJ_ENCODING_EMBSTR编码则是一次就分配好。
  • 同样的,OBJ_ENCODING_RAW编码的对象释放内存也需要两次,OBJ_ENCODING_EMBSTR编码则是一次。
  • OBJ_ENCODING_EMBSTR编码的数据都存储在连续的内存上,OBJ_ENCODING_RAW编码则不是。
/* Create a string object with EMBSTR encoding if it is smaller than * OBJ_ENCODING_EMBSTR_SIZE_LIMIT, otherwise the RAW encoding is * used. * * The current limit of 44 is chosen so that the biggest string object * we allocate as EMBSTR will still fit into the 64 byte arena of jemalloc. */#define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 44robj *createStringObject(const char *ptr, size_t len) { if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT)  return createEmbeddedStringObject(ptr,len); else  return createRawStringObject(ptr,len);}

SDS

字符串是Redis里非常常见的类型,而用C实现的Redis和Java不一样。在C里字符串是用长度为N+1的字符数组实现的,且使用空字符串'\0'作为结束符号。获取字符串的长度需要遍历一遍,找到空字符串'\0'才知道字符串的长度,复杂度是O(N)。

如果有一个长度非常大的字符串,单线程的Redis获取它的长度就可能会阻塞很久,这是不能接受的,所以Redis需要一种更高效的字符串类型。

Redis实现了一个叫SDS(simple dynamic string)的字符串类型,其中有两个变量来分别代表字符串的长度和字符数组未使用的字符数量,这样就可以用O(1)的复杂度来获取字符串的长度了,而且同样也是使用空字符串'\0'作为结束符号。

struct sdshdr { // 字符串长度 int len; // 字符数组未使用的字符数量 int free; // 保存字符串的字符数组 char buf[];}

扩容机制

SDS在字符数组空间不足于容纳新字符串的时候会自动扩容。

如果把一个C字符串拼接到一个SDS后面,当字符数组空间不足时,SDS会先扩容到刚好可以容纳新字符串的长度,然后再扩充新字符串的空字符长度,最终SDS的字符数组长度等于 2 * 新字符串 + 1(结束符号'\0')。不过当新字符串的大小超过1MB后,扩充的空字符长度大小会固定为1MB。

之所以会有这个机制,是因为Redis作为一个NoSQL数据库,会频繁的修改字符串,扩容机制相当于给SDS做了一个缓冲池。把SDS连续增长N次字符串需要内存重分配N次优化成了SDS连续增长N次字符串最多需要内存重分配N次,这其实和Java里的StringBuilder实现思想是一样的。

后记

我看过两本关于Redis的书,里面都是讲Redis如何实战的,并没有讲Redis的设计和实现。这也就导致了面试很尴尬,因为面试官最喜欢问原理相关的东西了,所以以后学习技术的时候不要从实战类的书籍开始了,还是先看懂原理比较好。

参考资料

这是《Redis设计与实现》里字符串一节的总结。


总结

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


  • 上一条:
    Redis字符串原理的深入理解
    下一条:
    redis字符串类型
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 在Redis中能实现的功能、常见应用介绍(0个评论)
    • 2024年Redis面试题之一(0个评论)
    • 在redis缓存常见出错及解决方案(0个评论)
    • 在redis中三种特殊数据类型:地理位置、基数(cardinality)估计、位图(Bitmap)使用场景介绍浅析(2个评论)
    • Redis 删除 key用 del 和 unlink 有啥区别?(1个评论)
    • 近期文章
    • 在go中实现一个常用的先进先出的缓存淘汰算法示例代码(0个评论)
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • 在go + gin中gorm实现指定搜索/区间搜索分页列表功能接口实例(0个评论)
    • 在go语言中实现IP/CIDR的ip和netmask互转及IP段形式互转及ip是否存在IP/CIDR(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2017-12
    • 2020-03
    • 2020-05
    • 2021-04
    • 2022-03
    • 2022-05
    • 2022-08
    • 2023-02
    • 2023-04
    • 2023-07
    • 2024-01
    • 2024-02
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客