iis服务器助手广告广告
返回顶部
首页 > 资讯 > 数据库 >redis源码学习01:字符串sds
  • 673
分享到

redis源码学习01:字符串sds

redis源码学习01:字符串sds 2019-03-04 09:03:44 673人浏览 才女
摘要

前言 本文是Redis源码关于字符串处理的学习笔记,欢迎指正。 redis版本是5.0.5,redis的功能、用途及性能我就不做赘述了。 正文 进入正题,redis提供了自己的字符串存储及相关操作,源码文件在sds.h和sds.c里。 在学

redis源码学习01:字符串sds

前言

本文是Redis源码关于字符串处理的学习笔记,欢迎指正。 redis版本是5.0.5,redis的功能、用途及性能我就不做赘述了。

正文

进入正题,redis提供了自己的字符串存储及相关操作,源码文件在sds.h和sds.c里。 在学习代码的过程中发现redis使用了一个比较巧妙的设计,redis里存储字符串不是简单的使用C语言里的char *来存储,而是利用C语言指针可以加减运算的特性来封装字符串结构体。从而能够在常用的字符串处理函数里自动扩容;而且这个设计保证你在使用redis的字符串存函数同时也能使用全部的libc里的所有关于字符串的函数。 下面就来说说这个设计,首先看下一个宏定义:

typedef char *sds;

redis里给char *取了个别名sds,所以常用的跟字符串操作相关的函数也都是以sds开头如:

void sdssetlen(sds s, size_t newlen);
​size_t sdslen(const sds s);

接下来看下存储字符串的结构体:


struct __attribute__ ((__packed__)) sdshdr5 {
    unsigned char flags; 
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {
    uint8_t len; 
    uint8_t alloc; 
    unsigned char flags; 
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {
    uint16_t len; 
    uint16_t alloc; 
    unsigned char flags; 
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {
    uint32_t len; 
    uint32_t alloc; 
    unsigned char flags; 
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {
    uint64_t len; 
    uint64_t alloc; 
    unsigned char flags; 
    char buf[];
};

可以看到定义了5个不同的结构体,我们知道redis数据都是缓存在内存中的,所以分别定义不同的结构体来存放不同长度的字符串,尽可能的减少内存的占用。 sdshdr5结构体基本不用,就先不介绍了,其它4个结构里都包含4个字段:

  • len:字符串长度,不同结构体支持最大长度不一样
  • alloc:分配的内存大小
  • flags:低3位存储类型,即结构类型
  • buf:用于存储字符串的内存起始地址,buf都是动态分配的

每个结构体通过__attribute__来告诉编译器不要进行字节对齐。不采用字节对齐一可以节省内存,另一个用途是可以通过指针的减法操作获取flags,能获取到flags后一切都好办了,flags里低3位是存放结构体类型的,在sds.h里定义如下:

#define SDS_TYPE_5  0
#define SDS_TYPE_8  1
#define SDS_TYPE_16 2
#define SDS_TYPE_32 3
#define SDS_TYPE_64 4
#define SDS_TYPE_MASK 7     // 获取类型的掩码
#define SDS_TYPE_BITS 3     // 类型位数

当需要存储字符串时,先根据字符串长度选择一种结构体,然后申请一块空间,大小包括结构体大小及需要存储的字符串的大小,部分代码如下:

sds sdsnewlen(const void *init, size_t initlen) {
    void *sh;
    sds s;
    char type = sdsReQtype(initlen);    // 根据字符串长度选择合适的结构体存储
    
    if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8;
    int hdrlen = sdsHdrSize(type);      // 计算结构体大小,用于申请内存空间使用
    unsigned char *fp; 

    sh = s_malloc(hdrlen+initlen+1);	// 申请的空间包含结束符
    if (init==SDS_NOINIT)
        init = NULL;
    else if (!init)
        memset(sh, 0, hdrlen+initlen+1);
    if (sh == NULL) return NULL;
    s = (char*)sh+hdrlen;       // s为存储字符串的起始位置
    /*
     * 其它代码
     * ......
     *
     * /

    return s;
}

redis会多申请一个字节用于存储结束符,而且自动加上这个结束符,这样libc里所有的字符串处理函数都能适用。函数里申请的空间包括结构体部分,但是返回值却是真正存放字符串的地址。那么redis是怎么将s和结构体关联起来的呢,先看sds.h里的两个宏:

// 根据字符串起始位置定义一个sdshdr**的结构体指针
#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (void*)((s)-(sizeof(struct sdshdr##T)));
// 将字符串起始位置的指针转化为sdshdr**的结构体指针
#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))

因为申请的时候是连续的地址,所以这两个宏直接将地址减去结构体的长度就能获取到结构体的起始地址。 使用这两个宏需要知道结构体的类型,但是只有一个字符串的起始地址,那怎么知道是哪一种结构体存储的呢。 这时候结构体不采用内存对齐的方式就派上用场了,以获取字符串长度的函数为例:

static inline size_t sdslen(const sds s) {
    unsigned char flags = s[-1];
    switch(flags&SDS_TYPE_MASK) {
        case SDS_TYPE_5:
            return SDS_TYPE_5_LEN(flags);
        case SDS_TYPE_8:
            return SDS_HDR(8,s)->len;
        case SDS_TYPE_16:
            return SDS_HDR(16,s)->len;
        case SDS_TYPE_32:
            return SDS_HDR(32,s)->len;
        case SDS_TYPE_64:
            return SDS_HDR(64,s)->len;
    }
    return 0;
}

因为结构体没有内存对齐,而且flags字段是char类型,所以直接s[-1]就能获取到flag,只要取到flag其它就都好办了。其它比如获取容量等操作都是通过这种方式处理的,这里就不再一一介绍了。

收尾

本文主要是介绍存储字符串结构的技巧,其它代码都相对简单。sds.h和sds.c里常用的储如字符串拼接、格式化等操作都是自动扩容的,扩容大小是指数增长的。有时候也有可能进行缩容,不管是扩容还是缩容都会导致原来的指针失效。所以使用的时候要小心指针失效的情况,大部分函数都得这么用:s = sdstrim(s,"Aa. :"); 就先到这吧,欢迎指正~

您可能感兴趣的文档:

--结束END--

本文标题: redis源码学习01:字符串sds

本文链接: https://www.lsjlt.com/news/5591.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Redis源码阅读:Redis字符串SDS详解
    SDS 基本概念 简单动态字符串(Simple Dynamic String)SDS,用作Redis 的默认字符串。 C语言中的字符串:以空字符结尾的字符数组 SDS实现举例 r...
    99+
    2024-04-02
  • Redis中SDS简单动态字符串详解
    Redis 是内存数据库,高效使用内存对 Redis 的实现来说非常重要。 看一下,Redis 中针对字符串结构针对内存使用效率做的设计优化。 一、SDS的结构  C语言没有string类型,本质是char[...
    99+
    2023-04-12
    redis sds 介绍 redis sds原理 Redis的SDS结构
  • Redis中SDS和C字符串的区别有哪些
    这篇文章主要介绍Redis中SDS和C字符串的区别有哪些,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!redis底层没有使用“C字符串”来表示,而是用自己构建的“SDS抽象类型”进行...
    99+
    2024-04-02
  • Python学习—字符串练习
    输入一行字符,统计其中有多少个单词,每两个单词之间以空格隔开。如输入: This is a c++ program. 输出:There are 5 words in the line. 【考核知识点:字符串操作】代码: s=input...
    99+
    2023-01-31
    字符串 Python
  • python学习之字符串
    目录 python学习之字符串 1. python字符串概述 2. Python字符串运算符 3. python字符串格式化 ...
    99+
    2023-01-30
    字符串 python
  • Redis中SDS简单动态字符串问题怎么解决
    这篇文章主要介绍“Redis中SDS简单动态字符串问题怎么解决”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Redis中SDS简单动态字符串问题怎么解决”文章能帮助大家解决问题。一、SDS的结构&n...
    99+
    2023-07-06
  • python3学习之字符串
    s='this is test message's.capitalize()    首字母大写,其它小写               s.find(sub[, start[, end]])      在指定范围内(默认全部字符串),查找su...
    99+
    2023-01-31
    字符串
  • 解析Redis数据结构之简单动态字符串sds
    Redis是用ANSI C语言编写的,它是一个高性能的key-value数据库,它可以作用在数据库、缓存和消息中间件。其中 Redis 键值对中的键都是 string 类型,而键值对...
    99+
    2024-04-02
  • 几分钟教你掌握Redis简单动态字符串SDS
    目录正文redisLog(REDIS_WARNING,"Redis is now ready to exit, bye bye...");与C字符串的区别获取字符...
    99+
    2023-01-28
    Redis动态字符串SDS Redis SDS
  • Redis数据结构的动态字符串sds怎么使用
    本篇内容主要讲解“Redis数据结构的动态字符串sds怎么使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Redis数据结构的动态字符串sds怎么使用”吧!Redis是用ANSI C语言编写的...
    99+
    2023-06-21
  • python学习笔记字符串(二)
    字符串类型(string)字符串是以单引号'或双引号"括起来的任意文本,比如'abc',"123"等等。请注意,''或""本身只是一种表示方式,不是字符串的一部分,因此,字符串'abc'只有a,b,c这3个字符。如果'本身也是一个字符,那就...
    99+
    2023-01-31
    字符串 学习笔记 python
  • python学习------指定字符串补
    python学习------指定字符串补全空格  需求:如果希望字符串的长度固定,给定的字符串又不够长度,我们可以通过rjust,ljust和center三个方法来给字符串补全空格。如果是数字型先将其转为字符,再使用rjust,ljust和...
    99+
    2023-01-31
    字符串 python
  • python学习之字符串转换
    配置环境:python 3.6   python编辑器:pycharm 代码如下: #!/usr/bin/env python #-*- coding: utf-8 -*- def strCase(): "字符串大小写转换" ...
    99+
    2023-01-30
    字符串 python
  • 5. Python3源码—字符串(st
    5.1. 字符串对象 字符串对象是“变长对象”。 5.1.1. Python中的创建 Python中字符串(strs)对象最重要的创建方法为PyUnicode_DecodeUTF8Stateful,如下Python语句最终会调用到PyUn...
    99+
    2023-01-31
    字符串 源码 st
  • python随机生成字符串学习
    所用到的函数学习string模块的三个函数>>> import string >>> string.letters 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefg...
    99+
    2023-01-31
    字符串 python
  • 字符编码学习笔记
    一、常见编码 ASCII:ASCII码即美国标准信息交换码(American Standard Code for Information Interchange)。由于计算机内部所有信息最终都是一个二进制值,而每一个二进制位(bit)有0...
    99+
    2023-01-31
    学习笔记 字符
  • Python学习之字符串格式化
    目录一、这种占位符有很多二、具体的使用方式1、可以一次多插入几个数值2、打印浮点数(小数)3、打印百分数4、调用字典进行拼接5、sep 设置分割符号6、flags 格式符号三、for...
    99+
    2024-04-02
  • Python学习-字符串函数操作3
    字符串函数操作 isprintable():判断一个字符串中所有字符是否都是可打印字符的. 与isspace()函数很相似 如果字符串中的所有字符都是可打印的字符或字符串为空返回 True,否则返回 False str1 = ...
    99+
    2023-01-30
    字符串 函数 操作
  • PHP入门学习之字符串操作
    目录字符串简介单引号和双引号的区别字符串的连接符去除字符串首尾空格和特殊字符1.trim()函数2.Itrim()函数3.rtrim()函数转义、还原字...
    99+
    2024-04-02
  • Python学习-字符串函数操作1
    字符串的函数操作 capitalize():可以将字符串首字母变为大写 返回值:首字符大写后的新字符串 str = "liu" print(str.capitalize()); // Liu print(str); /...
    99+
    2023-01-30
    字符串 函数 操作
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作