Redis底层数据结构之字典

Redis系列之底层数据结构字典Dict Redis系列之底层数据结构字典Dict。Dict是Redis数据结构中使用最为频繁的复合型数据结构,本质上是一个哈希表。在Redis中,Dict数据结构应该是使用最为频繁的复合型数据结构,除了在hash数据的会使用字典外,整个Redis的key和value也组成一个全局字典,Zset集合中存储value和score值的映射关系也是通过字典结构实现的 阅读详情

Redis底层数据结构之字典

说起字典我们应该都比较熟悉,在C++ STL或者Java集合框架中的HashMap就是一种典型的字典结构,用于保存一个键-值对,将一个键与一个值进行关联起来,其中键是不能重复的,也就是说是唯一的。字典作为一种重要的数据结构在Redis数据库中被广泛应用,底层的很多操作基本都是基于字典的。

一、字典的基本数据结构

一张图描述map的基本数据结构

字典的数据结构定义在dict.h文件中,首先我们看dict结构体构成

   /* 字典结构体 */
    typedef struct dict {
        //字典类型
        dictType *type;
        //字典中私有数据指针
        void *privdata;
        //字典中的哈希表,这里设置了两张哈希表,主要是为了给后面的rehash用
        dictht ht[2];
        //rehash时记录位置下标,当不在进行rehash时候,等于-1
        long rehashidx; /* rehashing not in progress if rehashidx == -1 */
        //当前迭代器数量
        int iterators; /* number of iterators currently running */
    } dict;

从下至下,一个个子结构说明,dictType结构保存了一系列用于操作特定类型键值对的函数,Redis会根据字典的不同用途实现不同的函数

    /* 字典类型 */
    typedef struct dictType {
        //哈希计算方法,返回整形变量
        unsigned int (*hashFunction)(const void *key);
        //复制key方法
        void *(*keyDup)(void *privdata, const void *key);
        //复制val方法
        void *(*valDup)(void *privdata, const void *obj);
        //key值比较方法
        int (*keyCompare)(void *privdata, const void *key1, const void *key2);
        //key的析构函数
        void (*keyDestructor)(void *privdata, void *key);
        //val的析构函数
        void (*valDestructor)(void *privdata, void *obj);
    } dictType;

dictht是一个哈希表结构,这是真正用于存储键和值的数据结构

    /* This is our hash table structure. Every dictionary has two of this as we
     * implement incremental rehashing, for the old to the new table. */
    /* 哈希表结构体 */
    typedef struct dictht {
        //指向dictEntry的二位指针,dictEntry表示键值对实体
        dictEntry **table;
        //表格可容纳键值对数量
        unsigned long size;
        //哈希表掩码,用于计算索引值
        unsigned long sizemask;
        //哈希表已有节点的数量
        unsigned long used;
    } dictht;

在看下dictEntry的结构:

    /* 键值对实体构体,保存K-V值的结构体 */
    typedef struct dictEntry {
        //指向key的指针
        void *key;
        union {
          //指向value的指针
            void *val;
            //无符号整型值
            uint64_t u64;
            //有符号整型值
            int64_t s64;
            double d;
        } v;
        //下一字典结点,形成链表
        struct dictEntry *next;
    } dictEntry;

字典迭代器

    /* If safe is set to 1 this is a safe iterator, that means, you can call
     * dictAdd, dictFind, and other functions against the dictionary even while
     * iterating. Otherwise it is a non safe iterator, and only dictNext()
     * should be called while iterating. */
    /* 字典迭代器,如果是安全迭代器,这safe设置为1,可以调用dicAdd,dictFind */
    /* 如果是不安全的,则只能调用dicNext方法*/
    typedef struct dictIterator {
       //当前字典
        dict *d;
        //下标
        long index;
        //哈希表
        int table, safe;
        //字典实体
        dictEntry *entry, *nextEntry;
        /* unsafe iterator fingerprint for misuse detection. */
        /* 指纹标记,避免不安全的迭代器滥用现象 */
        long long fingerprint;
    } dictIterator;

什么叫安全与不安全的迭代器?

  • 安全迭代器:在迭代进行过程中,可以对字典进行修改。

  • 不安全迭代器:在迭代进行过程中,不对字典进行修改。

下面分别从几个方面介绍字典的具体实现过程,包括字典是如何进行哈希索引的,增删改查,以及rehash策略等

二、字典的初始化

    /* Create a new hash table */
    /* 创建dict操作类 ,type可以是string,list等*/
    dict *dictCreate(dictType *type,
            void *privDataPtr)
    {
        dict *d = zmalloc(sizeof(*d));

        //创建好空间之后调用初始化方法
        _dictInit(d,type,privDataPtr);
        return d;
    }

    /* Initialize the hash table */
    /* 初始化dict类中的type,ht等变量 */
    int _dictInit(dict *d, dictType *type,
            void *privDataPtr)
    {
        //重置2个ht哈希表
        _dictReset(&d->ht[0]);
        _dictReset(&d->ht[1]);
        //赋值dictType
        d->type = type;
        d->privdata = privDataPtr;
        //-1代表还没有rehash过,
        d->rehashidx = -1;
        //当前使用中的迭代器为0
        d->iterators = 0;

        //返回DICT_OK,代表初始化成功
        return DICT_OK;
    }

三、键值的添加过程

我们首先来看键值的添加过程,这这个过程中会涉及很多的操作,首先是根据计算hash值,然后找到相应的哈希表索引位置,如果添加过程中遇到哈希表容量不够,我们该如何扩容,等等一系列问题。下面通过一副流程图来直观的表现该过程的运行机制,然后我们再一一根据代码详细探讨每一个步骤的执行过程。

3.1 键值添加的处理过程

下面从具体代码层面说明整个添加的过程:

    /* 添加一个dicEntry */
    int dictAdd(dict *d, void *key, void *val)
    {
        dictEntry *entry = dictAddRaw(d,key);

        if (!entry) return DICT_ERR;
       // 把值赋给新加的entry
        dictSetVal(d, entry, val);
        return DICT_OK;
    }

    /* 添加一个指定key值的Entry */
    dictEntry *dictAddRaw(dict *d, void *key)
    {
        int index;
        dictEntry *entry;
        dictht *ht;
        //判断是否正处于Rehashing状态
        if (dictIsRehashing(d)) _dictRehashStep(d);

        /* Get the index of the new element, or -1 if
         * the element already exists. */
        /* 如果指定的key已经存在,则直接返回NULL说明添加失败,这里有个疑问?为什么不覆盖掉旧的value,而是直接返回NULL? */
        if ((index = _dictKeyIndex(d, key)) == -1)
            return NULL;

        /* Allocate the memory and store the new entry */
        ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0];
        entry = zmalloc(sizeof(*entry));
        entry->next = ht->table[index];
        ht->table[index] = entry;
        ht->used++;

        /* Set the hash entry fields. */
        dictSetKey(d, entry, key);
        return entry;
    }
3.2 计算哈希索引

将一个新的键值添加到字典里面时候,程序首先要根据键值计算索引,具体步骤如下:

    //使用字典设置的哈希函数,计算键的哈希值
    hash = dict->type->hashFunction(key)

    //使用哈希表的sizemark属性和哈希值,计算出索引
    idx = hash & dict->ht[x].sizemask

Redis使用MurmurHash算法来计算哈希值,该算法的好处在于能够给出一个很好的伪随机分布,并且计算的速度也非常快。

3.3 哈希冲突解决

当有多个键映射到哈希表数组的同一个索引位置时,我们称发生了冲突,Redis使用链地址法解决哈希冲突

四、rehash过程

当哈希表的容量不在一个合理的范围之内,这个范围通常由负载因子来决定,哈希表的键值实体个数太多或者太少,则需要对哈希表的空间进行扩展或者收缩,这个过程称为rehash过程

具体的步骤:

  • 首先是为字典的哈希表ht[1]分配空间,这个空间的大小取决于要进行的操作:

    (1)如果进行的是扩展操作,则ht[1]的空间大小应为第一个2^N >= 2 * ht[0].used 的N值

    (2)如果执行的是收缩操作,则ht[1]的空间大小应为第一个2^N >= ht[0].used 的N值

  • 将保存在ht[0]上的所有键值对从新搬运到ht[1]上

  • 当ht[0]上的所有实体搬完之后,释放ht[0]的空间,将ht[1]设置为ht[0],让ht[1]指向一张空闲的哈希表,供下次rehash使用

在 rehash 的最后阶段,程序会执行以下工作:

  • 释放 ht[0] 的空间;

  • 用 ht[1] 来代替 ht[0] ,使原来的 ht[1] 成为新的 ht[0] ;

  • 创建一个新的空哈希表,并将它设置为 ht[1] ;

  • 将字典的 rehashidx 属性设置为 -1 ,标识 rehash 已停止;

具体的代码实现如下:

    /* rehash过程
     * 如果返回1说明旧的表中还存在key迁移到新表中,0代表没有 */
    int dictRehash(dict *d, int n) {
        if (!dictIsRehashing(d)) return 0;

        /* 根据参数分n步多次循环操作 */
        while(n--) {
            dictEntry *de, *nextde;

            /* Check if we already rehashed the whole table... */
            if (d->ht[0].used == 0) {
                zfree(d->ht[0].table);
                d->ht[0] = d->ht[1];
                _dictReset(&d->ht[1]);
                d->rehashidx = -1;
                return 0;
            }

            /* Note that rehashidx can't overflow as we are sure there are more
             * elements because ht[0].used != 0 */
             // 确保哈希表的大小要比rehashidx大,避免缓冲区溢出
            assert(d->ht[0].size > (unsigned long)d->rehashidx);
            // 跳到要进行rehash的表项
            while(d->ht[0].table[d->rehashidx] == NULL) d->rehashidx++;
            de = d->ht[0].table[d->rehashidx];
            /* Move all the keys in this bucket from the old to the new hash HT */
            /* 循环将ht[0]上的键值对实体移到ht[1]上 */
            while(de) {
                unsigned int h;

                nextde = de->next;
                /* Get the index in the new hash table */
                h = dictHashKey(d, de->key) & d->ht[1].sizemask;
                de->next = d->ht[1].table[h];
                d->ht[1].table[h] = de;
                d->ht[0].used--;
                d->ht[1].used++;
                de = nextde;
            }
            d->ht[0].table[d->rehashidx] = NULL;
            d->rehashidx++;
        }
        return 1;
    }

五、渐进式rehash过程

在上一节,我们了解了字典的 rehash 过程, 需要特别指出的是, rehash 程序并不是在激活之后,就马上执行直到完成的, 而是分多次、渐进式地完成的。

这样做的原因,也很简单,因为当哈希表中记录很少时,服务器rehash过程几乎是瞬间就能完成,但是当服务器中保存的键值对成千上万时候,服务器需要大量的时间进行rehash操作,造成服务响应迟缓,系统吞吐量急剧下降,因此Redis采用渐近式rehash策略来完成rehash操作

具体的步骤:

  • 为ht[1]分配空间

  • 将rehash索引计数器设置为0

  • 每次对字典进行CUAD操作时,顺便将ht[0]的索引为rehashidx上的所有键值对,搬运到ht[1]上,完成后将索引计数器加一

  • 当全部rehash完成时,设置rehashidx的值为-1,表示rehash操作完成

渐进式rehash将rehash的工作量平摊到每个CURD操作上,避免一次性rehash代来较高的时间开销

rehash的进行过程代码:

渐进式rehash的具体执行过程其实就是上面一个小节的int dictRehash(dict *d, int n) 函数,只不过渐进式rehash的参数n为1,也就是每次只对哈希表中的一项进行rehash操作

参考书籍

黄健宏的《Redis设计与实现》

参考博文

http://redisbook.readthedocs.io/en/latest/internal-datastruct/dict.html

http://blog.youkuaiyun.com/xiejingfa/article/details/51018337

https://www.oschina.net/question/28_44909

SAP 中租赁资产的实现需区分经营租赁与融资租赁,核心围绕资产主数据配置、折旧与利息设置、科目分配及业务操作展开 摘要:SAP系统租赁资产管理需区分经营租赁与融资租赁,通过SPRO配置资产主数据、折旧范围、利息计算及科目分配。关键步骤包括:激活折旧范围利息计算(OABZ)、设置租赁资产类别(OAYZ)、维护总账科目(AO90)及创建租赁资产主数据(AS01)。业务操作涵盖资产资本化(ABZON)、折旧利息过账(AFAB)、租金支付(F-53)及终止处理(ABUMN)。核心配置要点在于准确设置租赁类型、利率参数和科目联动,确保系统自动计算折旧与利息并生成正确凭证。 阅读详情

相关推荐

工厂自动化布线实战:从功率计算到线槽布局的完整避坑指南

本文详细介绍了工厂自动化布线的实战经验,从功率计算到线槽布局的完整避坑指南。通过三步计算法确定线径需求,强调电压降控制的重要性,并分享线槽布局的50%法则和强弱电分离技巧。特别针对移动设备和多芯信号线提供专业解决方案,帮助工程师避免常见错误,提升布线效率和质量。

weixin_29266007的博客 352

【Redis】数据结构与对象(六种底层数据结构)——字典

字典 字典,又称为符号表(symbol table)、关联数组(associative array)或映射(map),是一种用于保存键值对(key-value pair)的抽象数据结构。字典中的每一个键都是唯一的,可以通过键查找与之关联的值,并对其修改或删除 Redis的键值对存储就是用字典实现的,散列(Hash)的底层实现之一也是字典 (1)字典的定义实现 Redis的字典底层是使用哈希表实现的,一个哈希表里面可以有多个哈希表节点,每个哈希表节点中保存了字典中的一个键值对 哈希表结构定义: typedef

your_棒棒糖的博客 399

10、网络安全工具:Bettercap 与 Web 应用测试工具全解析

本文详细解析了网络安全工具Bettercap及其在中间人攻击和无线攻击中的应用,同时介绍了多种Web应用测试工具,如Amass、Cadaver、Dirb等,并探讨了工具的使用场景、流程、注意事项以及未来发展趋势,为网络安全测试提供了全面的指导。

u0v1w2x3的博客 227

Redis5种底层数据结构之字典

Redis的字典使用哈希表作为底层实现,一个哈希表里可以有多个哈希表节点,而每个哈希表节点就保存了字典中的一个键值对。

qq_40853117的博客 858

ANPC三电平拓扑双脉冲试验的原理与实施

ANPC三电平拓扑因损耗低、谐波小、应力均衡而广泛应用,但其存在短、长两类换流通道,寄生电感和开关应力差异显著。短通道尖峰小、可靠性高;长通道应力严苛。因此需分别制定两套独立双脉冲测试方案,针对不同时段和器件(如T1、T5、T6、T4及对应二极管)全面考核开关特性。

嵌入式老牛的博客 349

ABAP Cloud 开发教程的文章目录

本教程涵盖 ABAP 云端 Client 处理、多租户架构、ABAP Cloud开发、动态编程优化、SQL语法改进等技术主题。最新版本1.0.0(更新至2025-12-12),包含197篇文章,重点文章包括《ABAP Client Handling与隔离机制》《ABAP Platform 2021泛型编程》《SAP BTP ABAP多租户应用开发》等,为开发者提供从基础到进阶的实战指南。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1804

Python字典的底层原理

一文搞懂Python字典的底层原理。

qq_48441158的博客 2625

使用W5500与外网通信故障解决(MAC地址问题)

在蒲城项目的调试时,AMS500发现一个致命的问题,就是:在局域网跨网段通信时候和中心服务器不能通信,但是在一个HUB下是没有问题的,以前的串口服务器有过类似的问题出现,解决方法说是芯片的问题,结果也不了了之,换了芯片正常。 我们的网络通信芯片选择W5500,其中的MAC地址采用的是CPU的序列号作为Mac地址的问题就出在这,我们在使用的时候MAC地址的第一个必须是偶数,我们忽视了。 现象描...

zxm8513优快云博客 7755

python字典的底层原理

python字典定义底层实现查找对比python字典与redis 定义 无序,可变的容器数据结构,key-value数据存储 key可以为数值,字符串,元组,但是唯一,value则支持多种数据类型 读取速度快,时间复杂度O(1)O(1)O(1) 底层实现 python字典的底层实现是哈希表,即带有索引和存储空间的表 如d={},创建一个空字典,初始化一个长度为8 的C数组arr 1. 插入数据 d["name"]="jack" 首先对键- -“name”计算哈希值,然后对当前C数组arr的长度求余, 得

weixin_45228198的博客 734

华为od 面试八股文_测试/测试开发_02_含答案

华为od 面试八股文_测试/测试开发_02_含答案

MISAYAONE的博客 2160

【AIGC】大模型面试高频考点07-注意力(Attention)篇

AIGC面试高频考点

一个内容严谨、方向前沿、极具实践价值的AI大模型技术分享站 3363

python 底层原理_Python字典底层实现原理详解

在Python中,字典是通过散列表或说哈希表实现的。字典也被称为关联数组,还称为哈希数组等。也就是说,字典也是一个数组,但数组的索引是键经过哈希函数处理后得到的散列值。哈希函数的目的是使键均匀地分布在数组中,并且可以在内存中以O(1)的时间复杂度进行寻址,从而实现快速查找和修改。哈希表中哈希函数的设计困难在于将数据均匀分布在哈希表中,从而尽量减少哈希碰撞和冲突。由于不同的键可能具有相同的哈希值,即...

weixin_39887925的博客 309

把浮点数这件小事讲清楚:ABAP 7.51 里 Open SQL 与 ABAP CDS 的 FLTP 新玩法与避坑指南

ABAP浮点数计算的风险与应对 摘要:ABAP项目中的浮点数(FLTP)在跨平台计算时存在误差风险,同一逻辑在不同数据库平台可能产生细微差异。ABAP 7.51新增了两项关键能力:1)Open SQL和ABAP CDS中可为AVG函数指定结果类型,避免默认返回FLTP;2)ABAP CDS提供FLTP_TO_DEC转换函数。但需注意这些转换仍可能存在平台相关差异,且FLTP_TO_DEC会截断而非四舍五入小数位。开发者在涉及金额、数量等关键业务计算时,应明确数值语义并设计容错机制。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 214

redis底层数据结构——字典

redis底层数据结构——字典

SO_zxn的博客 1055

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

Redis之字典底层数据结构

Redis之字典底层数据结构 table 属性是一个数组, 数组中的每个元素都是一个指向 dict.h/dictEntry 结构的指针, 每个 dictEntry 结构保存着一个键值对。 size 属性记录了哈希表的大小, 也即是 table 数组的大小, 而 used 属性则记录了哈希表目前已有节点(键值对)的数量。 sizemask 属性的值总是等于 size - 1 , 这个属性和哈希值一起决定一个键应该被放到 table 数组的哪个索引上面。 key 属性保存着键值对中的键, 而 v 属性则保存着

jiohfgj的博客 292

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

详解Redis对象底层数据结构 — 字典

因为,在执行命令时,redis需要创建当前服务进程的子进程,而大多数操作系统都采用写时复制技术来优化子进程的使用效率,所以在子进程存在期间,服务器会提高所需的负载因子,从而尽可能的避免子进程存在期间进行哈希表的扩展操作,可以避免非必要的内存写入操作,最大限度的节约内存。1、为字典2(字段数组,第一个我称为字典1,第二个称为字典2)分配更大的空间,空间分配规则:如果执行的是扩展操作则分配第一个大于等于目前使用量*2的2的n次方幂,如果执行的是缩容操作则为字段2分配第一个大于等于目前使用量的2的n次方幂。

qq_64481183的博客 417

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

[Redis]-[底层数据结构]-字典

Redis 底层数据结构之 字典

Pacifica_的博客 180

Redis底层数据结构之字典(Dict)

Redis的Dict数据结构采用哈希表实现,通过两个dictht结构(ht[0]和ht[1])支持渐进式扩容机制。

zErO__N的博客 1652

Redis 底层数据结构之 Dict(字典)

c运行// 字典类型函数} dictType;// 哈希表节点void *key;// 键union { // 值(联合体)void *val;double d;} v;// 链表指针,处理哈希冲突// 哈希表// 哈希表数组// 哈希表大小// 掩码,用于计算索引(size-1)// 已使用节点数量} dictht;// 字典// 类型函数// 私有数据// 两个哈希表,用于渐进式 rehash。

Wyc72409的博客 1119

Redis底层数据结构——字典

在字典中,一个键 key 和一个值 value 进行关联,通过这样的关系,称他们为键值对。而字典的内部数据结构基本是由多个键值对组成的。 Redsi 使用 C 语言作为实现,但 C 语言不像其他大多语言把字典的数据结构内置,所以 Redis 实现了自己的专属字典结构。 字典应用场景 字典基本在 Redis 中是应用最广泛的,比如 Redis 的数据库就使用了字典作为了自己的底层实现,我们平时对数据库的增删改查都是建立在对字典的操作之上的。 还有 Redis 经常使用的数据类型:Hash , Zset 也都使

红橙呀 502

Redis系列(六)底层数据结构之字典

前言 定义 字典 哈希表 哈希算法 哈希冲突 扩容与缩容 渐进式 hash 原理 小应用 思考问题:为什么缩容不用考虑 bgsave? 总结 参考文章 联系我 前言 Redis 已经是大家耳熟能详的东西了,日常工作也都在使用,面试中也是高频的会涉及到,那么我们对它究竟了解有多深刻呢? 我读了几本 Redis 相关的书籍,尝试去了解它的具体实现,将一些底层的数据结构及实现原理记录下来...

呼延十 675

Redis 底层数据结构之字典

文章参考 《Redis 设计与实现》黄建宏 字典 在字典中,每个键都是独一无二的,程序可以在字典中根据键查找与之相关联的值,或者通过键来更新和删除值。 字典在 Redis 中的应用相当广泛,比如 Redis 的数据库就是使用字典来作为底层实现的,例如: redis> SET msg "hello world" OK 在数据库中创建一个键为 “msg” 值为 “hello world” 的键值对, 这个键值对就保存在数据库的字典里面。 哈希键的底层实现之一也是字典 哈希表 Redis 字典所使用的.

weixin_44046117的博客 244

Redis底层数据结构四之字典

Redis的底层数据结构RedisObject对象,其中对应type的7种对象类型,本文主要写字典、压缩列表、整数集合和快速列表

liumeina_study的博客 581

【Redis技术进阶之路】「底层源码解析」揭秘高效存储模型与数据结构底层实现(字典)

随着数据需求的迅猛增长,持久化和数据查询技术的重要性日益凸显。关系型数据库已不再是唯一选择,数据的处理方式正变得日益多样化。在众多新兴的解决方案与工具中,Redis凭借其独特的优势脱颖而出。

世界上并没有完美的程序,但是我们并不因此而沮丧,因为写程序就是一个不断追求完美的过程。 1387

Redis底层数据结构(三)--字典

字典,又称符号表,关联数组或者映射,是一种用于保存键值对(key-value)的抽象数据结构。 字典在Redis里的应用:Redis数据库就是使用字典来作为底层实现的,对数据库的增,删,改,查操作也是建立在对字典的操作之上的。除了表示数据库之外,字典还是哈希键的底层实现之一。 字典的实现 Redis的字典使用哈希表作为底层实现,一个哈希表里面可以有多个哈希表节点,每个哈希表节点就保存了字典中的一个键值对。 哈希表 结构定义: table属性是一个数组,数组中每个元素都是指向dictEntry

Untara的博客 206

Redis-底层数据结构-字典

字典 目录 字典的实现 哈希算法 解决键冲突 rehash 渐进式rehash 字典又称为符号表(symbol table)、关联数组(associative array)、或映射(map),是一种用于保存键值对(key-value pair)的抽象数据结构。 在字典中,一个键(key)可以和一个值(value)进行关联,这些关联的键和值被称为键值对。 字典的实现 Redis的字典使用哈希表作为底层实现,一个哈希表里可以有多个哈希表节点,每个哈希表节点保存了字典中的一个键值对。 哈希表 typedef

shiwentong的博客 311
上一篇: Analysis of Code Heterogeneity for High-Precision Classification of Repackaged Malware
下一篇: Android的进程间通信机制之Binder初探
code&poetry
博客等级 码龄14年 134粉丝 34原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值