透过Redis源码探究Hash表的实现

生成式引擎优化(GEO)全流程解析 生成式引擎优化(GEO)已成为AI时代数字营销新趋势,通过优化企业内容以提升在AI生成答案中的引用率。本文系统梳理了GEO优化的完整流程,包括:1)前期准备与市场调研;2)数据治理与知识库构建;3)模型适配与算法优化;4)内容权威性建设;5)本地化营销;6)效果监测与优化。研究显示,实施GEO的企业AI曝光率提升62%,转化率增长2.3倍。未来GEO将向多模态内容、实时个性化、垂直场景深化方向发展。企业需建立动态知识图谱,采用结构化数据标记,持续优化内容策略,以在AI搜索时代获取流量优势。 阅读详情

本文使用的Redis 5.0源码

概述

我们在学习 Redis 的 Hash 表的时候难免脑子里会想起其他 Hash 表的实现,然后进行一番对比。通常我们如果要设计一个 Hash 表,那么我们需要考虑这几个问题:

  1. 有没有并发操作;
  2. Hash冲突如何解决;
  3. 以什么样的方式扩容。

对 Redis 来说,首先它是单线程的工作模式,所以不需要考虑并发问题,这题 pass。

对于 Hash 冲突的解决,通常来说有,开放寻址法、再哈希法、拉链法等。但是大多数的编程语言都用拉链法实现哈希表,它的实现复杂度也不高,并且平均查找的长度也比较短,各个用于存储节点的内存都是动态申请的,可以节省比较多的存储空间。

所以对于 Redis 来说也是使用了拉链法来解决 hash 冲突,如下所示,通过链表的方式把一个个节点串起来:

至于为什么没有向 JDK 的 HashMap 一样红黑树来解决冲突,我觉得其实有两方面,一方面是链表转红黑数其实也是需要时间成本的,会影响链表的操作效率;另一方面就是红黑树其实在节点比较少的情况下效率是不如链表的。

再来看看扩容,对于扩容来说,一般要新起一块内存,然后将旧数据迁移到新的内存块中,这个过程中因为是单线程,所以在扩容的时候,不能阻塞主线程很长时间,在 Redis 中采用的是 渐进式 rehash + 定时 rehash 。

渐进式 rehash会在执行增删查改前,先判断当前字典是否在执行rehash。如果是,则rehash一个节点。这其实是一种分治的思想,通过通过把大任务划分成一个个小任务,每个小任务只执行一小部分数据,最终完成整个大任务。

定时 rehash如果 dict 一直没有操作,无法渐进式迁移数据,那主线程会默认每间隔 100ms 执行一次迁移操作。这里一次会以 100 个桶为基本单位迁移数据,并限制如果一次操作耗时超时 1ms 就结束本次任务,待下次再次触发迁移

Redis 在结构体中设置两个表 ht[0] 和 ht[1] ,如果当前 ht[0] 的容量是 0 ,那么第一次会直接给4个容量;如果不是 0 ,那么容量会直接翻倍,然后将新内存放入到 ht[1] 中返回,并设置标记0表示在扩容中。

迁移 hash 桶的操作会在增删改查哈希表时每次迁移 1 个哈希桶从 ht[0] 迁移到 ht[1] ,在迁移拷贝完所有桶之后会将 ht[0] 空间释放,然后将 ht[1] 赋值给 ht[0] ,并把 ht[1] 大小重置为0 ,并将表示设置标记1表示 rehash 结束了。

对于查找来说,在 rehash 的过程中,因为没有并发问题,所以查找 dict 也会依次先查找 ht[0] 然后再查找 ht[1]

设计与实现

Redis 的 hash 实现主要在 dict.h 和 dict.c 这两个文件中。

hash 表的数据结构大致如下所示,我就不贴出结构体的代码了,字段都标注在图上了:

从上面的图上也可以看到 hash 表中有一个空间为2的 dictht 数组,这个数组就是用来做 rehash 时交替保存数据用的,其中 dict 里面的 rehashidx 用来表示是否在进行 rehash 。

何时触发扩缩容?

很多 hash 表都只有扩容,但是 dict 在 Redis 中是既有扩容,也有缩容。

扩容

扩容其实就是一般是在 add 元素的时候校验一下是否达到某个阈值,然后决定要不要进行扩容。所以经过搜索可以看到添加元素会调用 dictAddRaw 这个函数,我们通过函数的注释也可以知道它是 add 或查找的底层的函数。

Low level add or find:
This function adds the entry but instead of setting a value returns the dictEntry structure to the user, that will make sure to fill the value field as he wishes.

dicAddRaw 函数会调用到 _dictKeyIndex 函数,这个函数会调用 _dictExpandIfNeeded 判断是否需要扩容。

┌─────────────┐     ┌─────────┐      ┌─────────────┐      ┌─────────────────────┐
 │ add or find ├────►│dicAddRaw├─────►│_dictKeyIndex├─────►│ _dictExpandIfNeeded │
 └─────────────┘     └─────────┘      └─────────────┘      └─────────────────────┘

_dictExpandIfNeeded 函数判断了大致有三种情况会进行扩容:

其中哈希表的负载因子可以通过公式:

// load ratio = the number of elements / the buckets
load_ratio = ht[0].used / ht[0].size

比如说, 对于一个大小为 4 , 包含 4 个键值对的哈希表来说, 这个哈希表的负载因子为:

load_ratio = 4 / 4 = 1

又比如说, 对于一个大小为 512 , 包含 256 个键值对的哈希表来说, 这个哈希表的负载因子为:

load_ratio = 256 / 512 = 0.5

为什么要根据 rdb 或者 aof 操作联合负载因子来判断是否应该扩容呢?其实源码的注释中也有提到:

as we use copy-on-write and don't want to move too much memory around when there is a child performing saving operations.

也就是说在 copy-on-write 时提高执行扩展操作所需的负载因子, 可以尽可能地避免在子进程存在期间进行哈希表扩展操作, 这可以避免不必要的内存写入操作, 最大限度地节约内存,提高子进程的操作的性能。

逻辑我们说完了, 下面我们看看源码:

static int _dictExpandIfNeeded(dict *d)
{ 
    // 正在扩容中
    if (dictIsRehashing(d)) return DICT_OK; 
    // 如果 hash 表的size为0,那么创建一个容量为4的hash表
    if (d->ht[0].size == 0) return dictExpand(d, DICT_HT_INITIAL_SIZE);

    // hash表中元素的个数已经大于hash表桶的数量
    if (d->ht[0].used >= d->ht[0].size &&
        //dict_can_resize 表示是否可以扩容
        (dict_can_resize ||
        // hash表中元素的个数已经除以hash表桶的数量是否大于5
         d->ht[0].used/d->ht[0].size > dict_force_resize_ratio))
    {
        return dictExpand(d, d->ht[0].used*2); // 容量扩大两倍
    }
    return DICT_OK;
}

通过上面的源码我们可以知道,如果当前表的已用空间大小为 size,那么就将表扩容到 size*2 的大小。新的 dict hash 表是通过 dictExpand 来进行创建的。

int dictExpand(dict *d, unsigned long size)
{
    //正在扩容,直接返回
    if (dictIsRehashing(d) || d->ht[0].used > size)
        return DICT_ERR;

    dictht n;  
    // _dictNextPower会返回 size 最接近的2的指数值
    // 也就是size是10,那么返回 16,size是20,那么返回32
    unsigned long realsize = _dictNextPower(size); 
 
    // 校验扩容之后的值是否和当前一样
    if (realsize == d->ht[0].size) return DICT_ERR;
 	// 初始化 dictht 成员变量
    n.size = realsize;
    n.sizemask = realsize-1;
    n.table = zcalloc(realsize*sizeof(dictEntry*)); // 申请空间是 size * Entry的大小
    n.used = 0;

    //校验hash 表是否初始化过,没有初始化不应该进行rehash
    if (d->ht[0].table == NULL) {
        d->ht[0] = n;
        return DICT_OK;
    }
    //将新的hash表赋值给 ht[1]
    d->ht[1] = n;
    d->rehashidx = 0;
    return DICT_OK;
}

这一段代码还是比较清晰的,可以跟着上面的注释稍微看一下就好了。

缩容

讲完了扩容,那么来看一下缩容。熟悉 Redis 的同学都知道,在 Redis 里面对于清理过期数据一个是惰性删除,另一个是定期删除,缩容其实也是在定期删除里面做的。

Redis 的定时器会每100ms调用一次 databasesCron 函数,它会调用到 dictResize 函数进行缩容:

┌─────────────┐   ┌──────────────────┐   ┌──────────┐   ┌──────────┐
 │databasesCron├──►│tryResizeHashTable├──►│dictResize├──►│dictExpand│
 └─────────────┘   └──────────────────┘   └──────────┘   └──────────┘

同样的 dictResize 函数中也会判断一下是否正在执行 rehash 以及校验 dict_can_resize 是否在进行 copy on write操作。然后将 hash 表的 bucket 大小缩小为和被键值对同样大小:

int dictResize(dict *d)
{
    int minimal;
	
    if (!dict_can_resize || dictIsRehashing(d)) return DICT_ERR;
    minimal = d->ht[0].used; // 将bucket 缩小为和被键值对同样大小
    if (minimal < DICT_HT_INITIAL_SIZE)
        minimal = DICT_HT_INITIAL_SIZE;
    return dictExpand(d, minimal);
}

最后同样调用 dictExpand 创建新的空间赋值给 ht[1]。

数据迁移如何进行?

上面我们也提到了,无论是扩容还是缩容,创建的新的空间都会赋值给 ht[1] 以便进行数据迁移。然后在两个地方分别执行数据迁移,一个是 增删改查哈希表时触发 ,另一个是 定时触发

增删改查哈希表时触发

增删改查操作的时候都会检查 rehashidx 参数,校验是否正在迁移,如果正在迁移那么会调用 _dictRehashStep 函数,然后会调用到 dictRehash 函数。

但是需要注意的是,这里调用 dictRehash 函数传入的大小是 1 ,也就意味着每次只迁移 1 个 bucket。下面我们来看看 dictRehash 函数,这是整个迁移过程中最重要的函数。这个函数主要做了以下几件事:

  1. 校验当前迁移的bucket数量是否已达上线,并且ht[0]是否还有元素;
  2. 判断当前的迁移的bucket槽位是否为空,最大访问的空槽数量不能超过 n*10,n是本次迁移bucket数量;
  3. 获取到非空槽位里面 entry 链表进行循环迁移;
    1. 首先获取ht[1]新槽位的index;
    2. 一个个节点放置到新bucket的头部;
    3. 直到全部迁移完毕;
  4. 迁移完了将旧的hash表ht[0]对应的bucket置空;
  5. 检查如果已经rehash完了,那么需要free掉内存占用,并将ht[1]赋值给ht[0];

感兴趣的可以看看下面源码,已标注好注释:

int dictRehash(dict *d, int n) {
    // 最大的空bucket访问次数
    int empty_visits = n*10; /* Max number of empty buckets to visit. */
    if (!dictIsRehashing(d)) return 0;
	// 校验当前迁移的bucket数量是否已达上线,并且ht[0]是否还有元素;
    while(n-- && d->ht[0].used != 0) {
        dictEntry *de, *nextde;
 
        assert(d->ht[0].size > (unsigned long)d->rehashidx);
        // 判断当前的迁移的bucket槽位是否为空
        while(d->ht[0].table[d->rehashidx] == NULL) {
            d->rehashidx++;
            if (--empty_visits == 0) return 1;
        }
        // 获取到槽位里面 entry 链表
        de = d->ht[0].table[d->rehashidx]; 
        // 从老的bucket迁移数据到新的bucket中
        while(de) {
            uint64_t h; 
            nextde = de->next; 
            // hash之后获取新hash表的bucket槽位
            h = dictHashKey(d, de->key) & d->ht[1].sizemask;
            // 一个个节点放置到新bucket的头部
            de->next = d->ht[1].table[h];
            d->ht[1].table[h] = de;
            d->ht[0].used--;
            d->ht[1].used++;
            de = nextde;
        }
        // 迁移完了将旧的hash表对应的bucket置空
        d->ht[0].table[d->rehashidx] = NULL;
        d->rehashidx++;
    } 
    // 如果已经rehash完了,那么需要free掉内存占用,并将ht[1]赋值给ht[0]
    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;// 返回0表示迁移已完成
    } 
    return 1; // 返回1表示迁移未完成
}

定时触发

定时触发是由 databasesCron 函数进行定时触发,这个函数会每 100ms 运行一次,最终会通过 dictRehashMilliseconds 函数调用到我们上面提到的 dictRehash 函数。

┌─────────────┐   ┌───────────────────┐   ┌──────────────────────┐   ┌──────────┐
  │databasesCron├──►│incrementallyRehash├──►│dictRehashMilliseconds├──►│dictRehash│
  └─────────────┘   └───────────────────┘   └──────────────────────┘   └──────────┘

dictRehashMilliseconds 函数传入的 ms 参数表示可以运行多长时间,默认传入的是1,也就是运行1ms就会退出这个函数:

int dictRehashMilliseconds(dict *d, int ms) {
    long long start = timeInMilliseconds();
    int rehashes = 0;
	// 每次会迁移 100 个 bucket
    while(dictRehash(d,100)) {
        rehashes += 100;
        if (timeInMilliseconds()-start > ms) break;
    }
    return rehashes;
}

调用 dictRehash 函数的时候每次会迁移 100 个 bucket。

总结

之所有要讲 hash 表的实现是因为 Redis 中凡是需要 O(1) 时间获取 kv 数据的场景,都使用了 dict 这个数据结构,而 Redis 用的最多的也就是这种 kv 获取的场景,所以通过这篇文章我们可以清楚的了解到 Redis 的 kv 存储是怎么存放数据的,何时扩容,以及扩容是如何迁移数据的。

看这篇文章的时候不妨对比一下自己所使用的语言中 hash 表是如何实现的。

手把手教你用STM32的SPI驱动MCP2517FD实现CAN FD通信(附完整代码) 本文详细介绍了如何使用STM32通过SPI驱动MCP2517FD实现CAN FD通信,涵盖硬件连接、SPI驱动编写、寄存器配置及数据收发实战。提供完整代码示例和常见问题排查指南,帮助开发者快速构建高可靠性的CAN FD通信系统,适用于汽车电子和工业控制等领域。 阅读详情

相关推荐

环境配置 |Jupyter lab/Jupyter Notebook 安装与设置

ipynb使用Jupyterlab/Jupyter Notebook 来编写程序时的文件,在使用时,可以现转换为标准的.py的python文件。

weixin_44649780的博客 5774

redis的数据结构小结

目录 1,string类型 2,list类型 3,hash类型 dictEntry dictht dict 4,集合类型 skiplist与平衡树、哈希表的比较 redis可以存储五种数据结构:String(字符串)、List(列表)、Set(集合)、Hash(哈希)、Zset(有序集合)。del、type、rename等命令是通用的,另外,注意一点,这些结构都是一个key-数据...

苏云南雁的博客 866

ROS2机械臂MPC轨迹规划实战:从MoveIt 2插件配置到参数调优全流程

本文详细介绍了ROS2机械臂MPC轨迹规划的全流程,从MoveIt 2插件配置到参数调优。通过模型预测控制(MPC)技术,实现高精度运动控制,特别适合复杂装配任务。文章包含环境搭建、插件部署、参数设计及实战调优方法,帮助开发者快速掌握ROS2机械臂的MPC轨迹规划技术。

i0j1k2l3m的博客 207

Redis学习笔记-哈希桶和底层数据结构

文章目录Redis 学习笔记-哈希桶和底层数据结构1.Redis 基本数据类型2.Redis 底层数据结构类型3.Redis 基本数据类型和底层数据结构关系示意图4.key 是如何查找到哈希桶4.1 哈希表(HashTable)原理图4.2 哈希桶和数据类型对应原理图4.3 渐进式 rehash5.底层数据结构5.1 双向链表示意图5.2 压缩列表5.3 跳表5.4 不同数据结构时间复杂度6.小结 Redis 学习笔记-哈希桶和底层数据结构 前面文章介绍了 Redis 基本数据类型和这些数据类型对应的一些操

qq_38937634的博客 1247

win10安装openvpn客户端

进入C:\Program Files\TAP-Windows\bin目录,然后右键用管理员权限执行addtap.bat文件,此文件执行会创建一个网卡。链接:https://pan.baidu.com/s/1KAifTiP7qp1i5ZkfdnfwkA。4.2 进入安装目录的config下新建一个文件夹,将证书文件全部挪进去(不移动.ovpn文件)3.4 将拿到的ssl认证文件放入安装目录的config目录下。然后修改ovpn文件 “./”代表当前路径,第二个链接重复。3.1 一直下一步到这里打上勾。

qq_36200932的博客 3万+

BugkuCTF-PWN题pwn2-overflow超详细讲解

解题思路 1)计算出get_shell_的地址偏移量 2)算出来之后就直接溢出到后门函数 知识点 x64函数调用规则 解题之前我们先学下linux x64的函数调用规则。 x64机器在调用某个函数前,比如int func(int a, int b, int c, int d, int e, int f, int g, int h),首先他会把前6个参数从左往右存入到寄存器rdi、rsi、rdx、rcx、r8、r9,其余参数存入到栈里,保存的顺序是从右往左入栈。比如abcdef会存入到寄存器里,然后一次入栈h

am_03的博客 3014

redis哈希分桶路由介绍及代码示例

哈希分桶路由是一种数据分片的技术,通过哈希算法将数据分散到多个桶(Bucket)中,从而提高系统的处理能力。在处理大量数据时,可以将数据均匀地分配到多个桶中,使得每个桶中的数据量减少,从而降低单个桶的负载,提高系统的并发处理能力。Redis中的哈希表(Hash)是一种将多个键值对存储在一个Redis键里的数据结构。以上代码示例中,我们首先通过Jedis库连接到本地的Redis服务,然后使用hset命令将黑白名单数据存储到名为blacklist的哈希表中,最后通过hget命令查询指定用户的黑白名单状态。

还在活水泥的未来包工头 1300

ArcGIS | 全球新冠肺炎地图数据下载和处理

ArcGIS

thinkpang的博客 2901

Redis的Hash的实现

Redis的Hash的实现 我们知道,Hash 表是一种非常关键的数据结构,在计算机系统中发挥着重要作用。 比如在 Memcached 中,Hash 表被用来索引数据;在数据库系统中,Hash 表被用来辅助 SQL 查询。而对于 Redis 键值数据库来说,Hash 表既是键值对中的一种值类型,同时,Redis 也使用一个全局 Hash 表来保存所有的键值对,从而既满足应用存取 Hash 结构数据需求,又能提供快速查询功能。 那么,Hash 表应用如此广泛的一个重要原因,就是从理论上来说,它能以 O(1)

子冉冰清的博客 2445

Visual Studio Code ADT Extension 能不能连接 ECC,答案是可以,但真正的门槛在 ABAP 后端版本

Visual Studio Code 版 ABAP Development Tools(ADT)支持连接传统 SAP ECC 系统,关键不在于系统名称,而在于底层 SAP NetWeaver AS ABAP 版本是否达到最低要求(7.31 SP04 及以上)。只要系统已支持 Eclipse ADT,即可无缝使用 VS Code ADT,无需升级后端。SAP 通过复用原有 ADT 技术栈,将兼容性逻辑置于客户端,实现对老系统的持续支持。尽管初期侧重 ABAP Cloud 模型,但 1.1 及后续版本已全面覆盖

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

基于STM32+RFID设计的宿舍检修管理系统

设计一个上位机,用来管理查看维修检修信息,当维修寝室的设备或者检修完成时,通过STM32上的RC522刷一下这个寝室的IC卡,识别这是哪个寝室,识别成功后在软件上弹出一个对话框,填写本次维修或者检修的事件,填写完毕点击提交即可。...

2557

Redis一文全总结

本文详细总结redis基本数据结构、redis对象及使用场景、网络模型、持久化、缓存、集群等知识

woodslay的博客 601

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

透过Redis源码探究Hash表的实现!

我们在学习 Redis 的 Hash 表的时候难免脑子里会想起其他 Hash 表的实现,然后进行一番对比。通常我们如果要设计一个 Hash 表,那么我们需要考虑这几个问题: 有没有并发操作; Hash冲突如何解决; 以什么样的方式扩容。

wdj_yyds的博客 582

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

透过Redis源码探究Hash表的实现,你学废了吗?

我们在学习 Redis 的 Hash 表的时候难免脑子里会想起其他 Hash 表的实现,然后进行一番对比。通常我们如果要设计一个 Hash 表,那么我们需要考虑这几个问题:

wdj_yyds的博客 716

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Java程序员如何透过Redis源码探究Hash表的实现!

我们在学习 Redis 的 Hash 表的时候难免脑子里会想起其他 Hash 表的实现,然后进行一番对比。通常我们如果要设计一个 Hash 表,那么我们需要考虑这几个问题: 有没有并发操作; Hash冲突如何解决; 以什么样的方式扩容。

wdj_yyds的博客 398

简历上还不敢写精通redis?来看看这份阿里强推的redis深度历险成长手册!集群+应用+拓展+原理+源码齐飞

Redis 是互联网技术架构在存储系统中使用得为广泛的中间件,也是中高级后端工程师技术面试中面试官喜欢问的工程技能之一,特别是那些优秀的互联网公司,通常要求面试者不仅仅掌握 Redis 基础用法,还要理解 Redis 内部实现的细节原理。 这份手册分为分为基础和应用篇、原理篇、集群篇、拓展篇、源码篇共 5 大块内容。基础和应用篇讲解对读者来说有价值的内容,可以直接应用到实际工作中;原理篇、集群篇让开发者透过简单的技术表面看到精致的底层世界;拓展篇帮助读者拓展技术视野和夯实基础,便于进阶学习;源码篇让高阶.

JavaJIAMIN的博客 3453

我就不信,redis讲到这个地步你还学不会(从应用讲到源码)

Redis 是互联网技术架构在存储系统中使用得为广泛的中间件,也是中高级后端工程师技术面试中面试官喜欢问的工程技能之一,特别是那些优秀的互联网公司,通常要求面试者不仅仅掌握 Redis 基础用法,还要理解 Redis 内部实现的细节原理。 这份手册分为分为基础和应用篇、原理篇、集群篇、拓展篇、源码篇共 5 大块内容。基础和应用篇讲解对读者来说有价值的内容,可以直接应用到实际工作中;原理篇、集群篇让开发者透过简单的技术表面看到精致的底层世界;拓展篇帮助读者拓展技术视野和夯实基础,便于进阶学习;源码篇让高阶的.

weixin_47908466的博客 158

大厂架构师经验分享!透过根源从而探究红黑树的本质,威力加强版

01 前言 辛苦奋斗两个月,秋招终于圆满收官,拿到了头条、字节、菜鸟、腾讯、网易的offer,这要多亏了意外得到的这份资料文档,这么多面试全都靠它了,哈哈~~有好东西还是要分享出来给大家,一起学习呀 Java核心进阶宝典:JVM,JAVA集合,JAVA多线程并发,JAVA基础,Spring原理,微服务,Netty与RPC,网络,日志,Zookeeper,Kafka,RabbitMQ,Hbase,MongoDB,Cassandra,设计模式,负载均衡,数据库,一致性哈希,JAVA算法,数据结构,加密算法,

m0_57285455的博客 145

细节爆炸!阿里内部Redis成长手册震撼开源!实战+源码齐飞!

Redis 是互联网技术架构在存储系统中使用得为广泛的中间件,也是中高级后端工程师技术面试中面试官喜欢问的工程技能之一,特别是那些优秀的互联网公司,通常要求面试者不仅仅掌握 Redis 基础用法,还要理解 Redis 内部实现的细节原理。 这份手册分为分为基础和应用篇、原理篇、集群篇、拓展篇、源码篇共 5 大块内容。基础和应用篇讲解对读者来说有价值的内容,可以直接应用到实际工作中;原理篇、集群篇让开发者透过简单的技术表面看到精致的底层世界;拓展篇帮助读者拓展技术视野和夯实基础,便于进阶学习;源码篇让高阶.

Java圈全能架构师的博客 542

真香定律!透过根源从而探究红黑树的本质,顺利拿到offer

前言 在学习Java基础的过程中,泛型绝对算得上是一个比较难理解的知识点,尤其对于初学者而言,而且就算是已经有基础的Java程序员,可能对泛型的理解也不是那么透彻,属于那种看了明白,时间长了就忘的那种,究其根本,还是对泛型不够理解。 大部分人对泛型的认识:“基础知识,但是比较模糊” 阿里一面: 介绍一下自己。* 项目参与的核心设计有哪些* java反射原理, 注解原理?* ArrayList和LinkedList的插入和访问的时间复杂度?* 说说一致性 Hash 原理* HashMap

m0_56824583的博客 143

腾讯强推Redis成长手册!原理+应用+集群+拓展+源码五飞

Redis 是互联网技术架构在存储系统中使用得为广泛的中间件,也是中高级后端工程师技术面试中面试官喜欢问的工程技能之一,特别是那些优秀的互联网公司,通常要求面试者不仅仅掌握 Redis 基础用法,还要理解 Redis 内部实现的细节原理。 这份手册分为分为基础和应用篇、原理篇、集群篇、拓展篇、源码篇共 5 大块内容。基础和应用篇讲解对读者来说有价值的内容,可以直接应用到实际工作中;原理篇、集群篇让开发者透过简单的技术表面看到精致的底层世界;拓展篇帮助读者拓展技术视野和夯实基础,便于进阶学习;源码篇让高阶的.

Java架构师联盟 301

周二福利!京东T8纯手码强推Redis成长手册!基础与源码双管齐下

Redis 是互联网技术架构在存储系统中使用得为广泛的中间件,也是中高级后端工程师技术面试中面试官喜欢问的工程技能之一,特别是那些优秀的互联网公司,通常要求面试者不仅仅掌握 Redis 基础用法,还要理解 Redis 内部实现的细节原理。 这份手册分为分为基础和应用篇、原理篇、集群篇、拓展篇、源码篇共 5 大块内容。基础和应用篇讲解对读者来说有价值的内容,可以直接应用到实际工作中;原理篇、集群篇让开发者透过简单的技术表面看到精致的底层世界;拓展篇帮助读者拓展技术视野和夯实基础,便于进阶学习;源码篇让高阶.

bieber007的博客 151

深入浅出通信原理_腾讯强推Redis成长手册!原理+应用+集群+拓展+源码五飞

Redis 是互联网技术架构在存储系统中使用得为广泛的中间件,也是中高级后端工程师技术面试中面试官喜欢问的工程技能之一,特别是那些优秀的互联网公司,通常要求面试者不仅仅掌握 Redis 基础用法,还要理解 Redis 内部实现的细节原理。这份手册分为分为基础和应用篇、原理篇、集群篇、拓展篇、源码篇共 5 大块内容。基础和应用篇讲解对读者来说有价值的内容,可以直接应用到实际工作中;原理篇、集群篇让开...

weixin_39721370的博客 75

大厂面试为何如此偏爱源码阅读?

在开头跟大家分享的时候我就说,面试我是没有做好准备的,全靠平时的积累,确实有点临时抱佛脚了,以至于我自己还是挺懊恼的。(准备好了或许可以拿个40k,没做准备只有30k+,你们懂那种感觉吗)如何准备面试?1、前期铺垫(技术沉积)程序员面试其实是对于技术的一次摸底考试,你的技术牛逼,那你就是大爷。大厂对于技术的要求主要体现在:基础,原理,深入研究源码,广度,实战五个方面,也只有将原理理论结合实战才能把技术点吃透。

2301_82257317的博客 947

2024年Java最全SpringBoot学习笔记(七、Redis),美团java一面二面HR面面经

我还通过一些渠道整理了一些大厂真实面试主要有:蚂蚁金服、拼多多、阿里云、百度、唯品会、携程、丰巢科技、乐信、软通动力、OPPO、银盛支付、中国平安等初,中级,高级Java面试题集合,附带超详细答案,希望能帮助到大家。还有专门针对JVM、SPringBoot、SpringCloud、数据库、Linux、缓存、消息中间件、源码等相关面试题。本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录需要这份系统化的资料的朋友,可以点击这里获取unions。

2401_84692429的博客 868
上一篇: 2022最新Mybatis面试题总结
下一篇: 并发程序的噩梦——数据竞争
JavaMonsterr
博客等级 码龄4年 211粉丝 481原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值