`

redis之字符串命令源码解析(一)

阅读更多

形象化设计模式实战             HELLO!架构

 

在redis的使用中,set/get无疑是使用最普遍的命令,我先telnet连接运行看看

 

先看get命令,获取一个key服务器返回了两行内容,是"$3\r\n123\r\n"(\r\n为换行符),不难发现3就是“123”的长度,redis的官方文档get返回值为:

Bulk string reply: the value of key, or nil when key does not exist.

可以点击超链接看里面的解释,发现确实如此,那现在就从源码看看get是如何获取数据的。

 

1、内部数据结构之sds(Simple Dynamic String)

 

在传统C语言中,表示字符串通常是char *,由于char *类型的功能单一,抽象层次低,并且不能高效地支持一些Redis常用的操作(比如追加操作和长度计算操作),所以在Redis程序内部,绝大部分情况下都会使用sds而不是char *来表示字符串

 

sds的结构

现假如运行命令 set test "hello redis"

那么set命令创建并保存"test"到一个sdshdr中:(最终保存到数据库是char *类型,指向sdshdr->buf)

 

struct sdshdr
{
    len  = 4;
    free = 0;
    buf = "test\0";
};

 

将"hello redis"保存到另一个sdshdr中:(最终保存到数据库是robj类型,后序会讲解)

struct sdshdr
{
    len  = 11;
    free = 0;
    buf = "hello redis\0";
};

 那么如果再运行append test " now!",那是不是就会变成

struct sdshdr
{
    len  = 16;
    free = 0;
    buf = "hello redis now!\0";
};

 这样呢?不是!

sdsMakeRoomFor函数描述此场景的内存预分配优化策略

/* Enlarge the free space at the end of the sds string so that the caller
 * is sure that after calling this function can overwrite up to addlen
 * bytes after the end of the string, plus one more byte for nul term.
 * 
 * Note: this does not change the *length* of the sds string as returned
 * by sdslen(), but only the free buffer space we have. */
/*
 * 对 sds 中 buf 的长度进行扩展,确保在函数执行之后,
 * buf 至少会有 addlen + 1 长度的空余空间
 * (额外的 1 字节是为 \0 准备的)
 *
 * 返回值
 *  sds :扩展成功返回扩展后的 sds
 *        扩展失败返回 NULL
 *
 * 复杂度
 *  T = O(N)
 */
sds sdsMakeRoomFor(sds s, size_t addlen) {

    struct sdshdr *sh, *newsh;

    // 获取 s 目前的空余空间长度
    size_t free = sdsavail(s);

    size_t len, newlen;

    // s 目前的空余空间已经足够,无须再进行扩展,直接返回
    if (free >= addlen) return s;

    // 获取 s 目前已占用空间的长度
    len = sdslen(s);
    sh = (void*) (s-(sizeof(struct sdshdr)));

    // s 最少需要的长度
    newlen = (len+addlen);

    // 根据新长度,为 s 分配新空间所需的大小
    if (newlen < SDS_MAX_PREALLOC)
        // 如果新长度小于 SDS_MAX_PREALLOC(1024*1024)
        // 那么为它分配两倍于所需长度的空间
        newlen *= 2;
    else
        // 否则,分配长度为目前长度加上 SDS_MAX_PREALLOC
        newlen += SDS_MAX_PREALLOC;
    // T = O(N)
    newsh = zrealloc(sh, sizeof(struct sdshdr)+newlen+1);

    // 内存不足,分配失败,返回
    if (newsh == NULL) return NULL;

    // 更新 sds 的空余长度
    newsh->free = newlen - len;

    // 返回 sds
    return newsh->buf;
}

很明显在append后,test的长度只有15,远远不够1024*1024的,所以它的新长度应该是16*2+1=31

struct sdshdr
{
    len  = 33;
    free = 16;
    buf = "hello redis now!\0";
};

 

2、字符串编码

 

上面说set命令会将字符串数据保存在sdshdr中,那如果是一个数字也会如此吗?答案是不会!

 object.c的tryObjectEncoding方法

 

/* Check if we can represent this string as a long integer.
     * Note that we are sure that a string larger than 21 chars is not
     * representable as a 32 nor 64 bit integer. */
    // 检查字符串的长度,不对长度小于 21 的字符串进行编码
    // 也不对可以被解释为整数的字符串进行编码
    len = sdslen(s);
    if (len <= 21 && string2l(s,len,&value)) {
        /* This object is encodable as a long. Try to use a shared object.
         * Note that we avoid using shared integers when maxmemory is used
         * because every object needs to have a private LRU field for the LRU
         * algorithm to work well. */
        if (server.maxmemory == 0 &&
            value >= 0 &&
            value < REDIS_SHARED_INTEGERS)
        {
            decrRefCount(o);
            incrRefCount(shared.integers[value]);
            return shared.integers[value];
        } else {
            if (o->encoding == REDIS_ENCODING_RAW) sdsfree(o->ptr);
            //将encoding转为REDIS_ENCODING_INT
            o->encoding = REDIS_ENCODING_INT;
            o->ptr = (void*) value;
            return o;
        }
    }
 

由此可见,字符串类型有两种编码:

1、REDIS_ENCODING_INT使用long类型来保存long类型值

2、REDIS_ENCODING_RAW 使用sdshdr结构来保存sds(也就是char *)、long long double和long double类型值

 

3、get命令的实现

t_string.c中

int getGenericCommand(redisClient *c) {
    robj *o;

    // 尝试从数据库中取出键 c->argv[1] 对应的值对象
    // 如果键不存在时,向客户端发送回复信息,并返回 NULL
    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL)
        return REDIS_OK;

    // 值对象存在,检查它的类型
    if (o->type != REDIS_STRING) {
        // 类型错误
        addReply(c,shared.wrongtypeerr);
        return REDIS_ERR;
    } else {
        // 类型正确,向客户端返回对象的值
        addReplyBulk(c,o);
        return REDIS_OK;
    }
}

 这里说明一点,redis的key/value都是由“字典”数据结构实现,在这里不做深究。

/* Add a Redis Object as a bulk reply 
 *
 * 返回一个 Redis 对象作为回复
 */
void addReplyBulk(redisClient *c, robj *obj) {
    //回复字符的长度
    addReplyBulkLen(c,obj);
    //回复要返回的字符
    addReply(c,obj);
    //回复"\r\n"
    addReply(c,shared.crlf);
}

 这样就出现了开始的“"$3\r\n123\r\n"”。

 

  • 大小: 12.5 KB
5
0
分享到:
评论

相关推荐

    Redis源码解析

    源码解析有助于深入理解其工作原理,提高在实际应用中的优化能力。本篇文章将聚焦Redis的源码,探讨其核心组件、数据结构以及内部运行机制。 1. **Redis的数据结构** Redis的核心数据结构包括字符串(String)、...

    Redis实战中文版及源码下载

    2. **丰富的数据结构**:Redis提供了多种数据结构,如字符串、哈希、列表、集合、有序集合,这些数据结构在处理各种复杂应用场景时非常实用。例如,哈希可用于存储对象,列表可以实现消息队列,有序集合则适用于排行...

    Redis Windows源码

    Redis支持多种数据结构,如字符串、哈希表、列表、集合和有序集合。这些数据结构的实现是Redis高效性的关键。例如,链表、跳跃表(用于有序集合的快速查找)和压缩表(用于节省内存)等都是Redis内部的重要组件。 ...

    redis3源码及解析

    Redis源码解析是一项深入学习数据库系统、内存管理以及并发控制等核心计算机科学概念的宝贵资源。 首先,我们来看Redis中的核心数据结构。Redis支持多种数据类型,包括字符串(String)、哈希表(Hash)、列表...

    redis delphi实施数据库操作源码

    这通常涉及解析响应的类型(简单字符串、错误、整数、多条批量数据等)。例如,获取键的值: ```pascal var Response: string; begin RedisClient.SendCommand('GET', ['mykey']); if RedisClient.GetResponse...

    易语言源码Redis协议客户端易语言模块源码.rar

    它主要包含三种数据类型:简单的字符串(Simple Strings)、错误(Errors)、整数(Integers)、大整数(Bulk Strings)和多条数据(Multi-Bulk)。在易语言源码中,你会看到如何将这些数据类型转化为易语言的数据...

    redis-unstable_Redis数据库源码_

    以上只是Redis源码中的一部分关键知识点,深入研究源码还能了解到更多关于错误处理、内存管理、命令执行流程等方面的内容。对于想要深入了解Redis或者进行定制化开发的开发者来说,阅读和理解源码是非常有价值的。

    Redis安装包X64及源码.rar

    1. 数据结构:Redis的核心在于其高效的数据结构,如字符串、哈希表、链表、集合和有序集合等,这些都是C++实现的。 2. 异步I/O:Redis采用单线程模型处理所有客户端请求,通过事件驱动的非阻塞I/O(epoll、kqueue等...

    redis视频以及代码

    1. 键值对(Key-Value):Redis的核心存储结构,键是唯一的标识,值可以是多种类型,如字符串、哈希、列表、集合和有序集合。 2. 数据类型: - 字符串(String):最基础的类型,可存储整数、浮点数或任意文本。 ...

    redis-5.0.14.源码

    1. **数据结构**:Redis 的核心在于其高效的数据结构实现,如字符串(String)、哈希表(Hash)、列表(List)、集合(Set)和有序集合(Sorted Set)。这些数据结构的实现通常采用内存优化的方式,如 SDS(Simple ...

    redis in action 源码

    源码的阅读可以帮助我们深入理解Redis如何处理数据结构(如字符串、哈希表、列表、集合、有序集合等)、命令执行、持久化机制(RDB和AOF)、网络I/O模型(基于epoll的事件驱动)以及复制和集群功能。 首先,Redis的...

    labview redis通讯源码及实例

    LabVIEW程序需要正确构建这些命令的字符串格式并发送到Redis服务器。 3. **数据序列化与反序列化**:由于LabVIEW是基于图形的,而Redis处理的是字符串,所以数据在两者之间传输时,需要进行序列化和反序列化。例如...

    Redis_redis_源码

    1. **Redis的数据结构**:Redis支持多种数据结构,如字符串(Strings)、哈希表(Hashes)、列表(Lists)、集合(Sets)和有序集合(Sorted Sets)。这些数据结构的设计使得Redis在处理复杂数据操作时非常高效。 2...

    redis源码日志(源码分析)

    Redis是一款高性能的键值存储系统,其开源代码的解析对于深入理解其内部机制至关重要。"redis源码日志(源码分析)"是针对Redis源码进行深入剖析的学习资料,旨在帮助开发者理解如何通过源码实现Redis的高并发处理...

    redis-4.0.14源码压缩包

    - **字符串(Strings)**:简单动态字符串(SDS)是Redis内部实现的字符串,提供预分配空间、长度计算等功能,比C语言的原始字符串更高效。 - **列表(Lists)**:可以使用quicklist或linked list实现,前者是优化...

    redis 学习 基础知识 源码

    本文将深入探讨 Redis 的基础知识,包括其核心概念、数据类型、命令操作以及源码解析。 ### 1. Redis 核心概念 Redis 作为内存数据库,所有数据存储在内存中,通过持久化策略将数据保存到磁盘以防止数据丢失。其...

    redis源码分析

    Redis支持多种数据结构,如字符串(String)、哈希表(Hash)、列表(List)、集合(Set)和有序集合(Sorted Set)。这些数据结构在源码中由不同的C结构体实现,例如`sds`用于表示字符串,`dict`表示哈希表,`list`...

    redis in action源码

    《Redis in Action》是一本深入解析Redis的书籍,其源码是学习和理解Redis工作原理的重要资源。Redis是一个开源的、高性能的键值存储系统,常用于数据缓存、消息队列、数据库等多个场景。通过分析源码,我们可以深入...

    C# Redis秒杀活动系统源码

    它的数据类型包括字符串、哈希、列表、集合、有序集合等,这些丰富的数据结构在秒杀场景中能提供强大的支持。 在秒杀系统中,Redis主要扮演以下几个角色: 1. **库存管理**:利用Redis的原子操作(如INCR命令)...

Global site tag (gtag.js) - Google Analytics