程序猿成长之路之Redis(4)-- redis数据结构之string类型介绍

序言
学过java的同学应该都知道,string在java语言中主要是指字符串类型,并且这不属于java基本数据类型之中。而在redis中,string类型是什么呢?它的常见使用场景有哪些?常见的命令有哪些?

什么是redis的string类型?

String 类型在redis 中由两部分组成,一部分是key也就是键,另一部分是value,也就是值,redis将键和值进行绑定,通过key查找value 的值。

String类型的使用场景

  1. 用于共享session 、验证码等需设置超时时间数据的存储
    Setex session_key 7200 session_value // 设置两小时后过期(2 * 60 * 60),以秒为单位
  2. 用于存储json字符串(序列化对象)如商品信息、用户信息等
    Mset key1 userinfo1 key2 userinfo2
  3. 用于设置分布式锁
    setnx lockname lockvalue
  4. 计数
    set counter 1
    incr counter

string 常用的命令:(redis的)

  1. append key value // 如果该Key已经存在,APPEND命令将参数Value的数据追加到已存在Value的末尾。如果该Key不存在,APPEND命令将会创建一个新的Key/Value,返回值为新的value的长度
  2. incr/decr key //原子性数据加一/减一
  3. incrby/decrby key nb // 原子性数据加nb/减nb
  4. get key //获取数据
  5. set key value //存储数据,如果该Key已经存在,则覆盖其原有值
  6. strlen key // value 的长度
  7. setex key seconds value // 原子性完成两个操作,一是设置该Key的值为指定字符串,同时设置该Key在Redis服务器中的存活时间(秒数)。
  8. setnx key value // 如果key不存在就存储value
  9. mget key1 [key2 …] // 获取key1,key2的value值
  10. mset key1 value1 [key2 value2 …] //设置value1 、value2

Redis string 类型的动态内存分配机制?(扩展)

redis中的字符串会对内存进行动态分配。内部结构实现与java的ArrayList类似(根据装载因子和实际最大长度动态扩充数组长度)。还有一条规定: 字符串长度小于1M使用加倍扩容方式,如果大小超过1M则扩容时以1M大小扩容。但是如果字符串总长度不能超过512M。
再由于redis是基于c语言开发的,因此我们可以用c语言中的结构体(struct) 进行描述,该结构体被命名为sds。
Struct SDS {
T len; // 已有数据长度
T alloc; // 数组容量
Char flags; // 特殊标识位
Char[] buff; //存储字符串
};

为何这么设置?

  1. alloc — 降低数组扩容复杂度
    对于java而言,我们在数组扩容的时候可以使用length 获取数组长度,但是c语言中就不行,每次分配内存都会用void *malloc(size) 或者void *calloc(size) 方法进行分配,但是很明显我们要传入需要分配的地址长度。因此这么设计可以更快的进行扩容。时间复杂度为o(1)。
  2. Len — 确保数据完整不会截断。
    如果字符串保存图片、视频等二进制文件很可能中间会有‘0’字符,大家都知道一旦读取到‘0’字符时就会停止读取。所以这里加上已有数据长度已保证数据截断的完整性。
    此外和capacity一起使用可以减少频繁的内存分配。对于SDS来说,当进行字符串追加(append 命令)时,程序会用 alloc-len 比较下剩下的空余内存是否足够分配追加的内容,如果不够自然触发内存重分配,而如果剩余未使用内存空间足够放下,那么将直接进行分配,无需内存重分配。其扩容策略为,当字符串占用大小小于1M时,每次分配为len * 2,也就是保留100%的冗余;大于1M后,为了避免浪费,只多分配1M的空间,最大不超过512M。
  3. Flags — 判断采用什么sds 结构体来存储数据
    flag的低三位标识当前sds的类型(高5位为无用位),分别如下所示:
    #define SDS_TYPE_5 0 // flag的前三位为000表示使用sdshdr5 结构体来存储数据
    #define SDS_TYPE_8 1 // flag的前三位为001表示使用sdshdr8 结构体来存储数据
    #define SDS_TYPE_16 2 // flag的前三位为010表示使用sdshdr16 结构体来存储数据
    #define SDS_TYPE_32 3 // flag的前三位为011表示使用sdshdr32结构体来存储数据
    #define SDS_TYPE_64 4 // flag的前三位为100表示使用sdshdr64 结构体来存储数据
    这么操作可以选择最适合的结构体来存放数据,提高内存的优化。
    在Redis中的sds.h源码中存在着五种sdshdr(上面描述的结构体),分别如下(以下均为源代码):
struct __attribute__ ((__packed__)) sdshdr5 {
    unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {
    uint8_t len; /* used */
    uint8_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {
    uint16_t len; /* used */
    uint16_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {
    uint32_t len; /* used */
    uint32_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {
    uint64_t len; /* used */
    uint64_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};

内存模型如下:
程序猿成长之路之Redis(4)-- redis数据结构之string类型介绍_第1张图片

注:其中len表示数组实际使用长度而alloc表示数组分配长度,第一种由于不存在len和alloc,也就是无法进行数组长度的自动扩充,一旦预分配地址使用完就必须重新分配内存空间并完成数据的复制,消耗性能较大,因此很少使用。

更多链接

redis安装 https://blog.csdn.net/qq_31236027/article/details/121879741
redis介绍 https://blog.csdn.net/qq_31236027/article/details/121879604
redis 数据结构简介https://blog.csdn.net/qq_31236027/article/details/121894713

你可能感兴趣的:(笔记,redis,数据结构,缓存)