小菜鸟

java菜鸟号正在起航

MapReduce InputFormat 深度解析:数据怎么从文件变成键值对

MapReduce 处理数据的第一步,是把原始文件读进来、切成块、转成键值对,交给 Map 函数去处理。

这个过程的”总指挥”就是 InputFormat。它干三件事:

  1. 切分:把输入数据切成多个逻辑分片(InputSplit),每个分片交给一个 Map 任务
  2. 读取:每个分片通过 RecordReader 逐条读数据,转成 <key, value>
  3. 校验:检查输入路径对不对

InputSplit:逻辑分片,不是物理切块

InputSplit 是 MapReduce 的逻辑分片,记录了”读哪些数据”。

它不存储数据本身,只存三个信息:

  • 文件路径:读哪个文件
  • 起始位置:从文件的哪个字节开始读
  • 长度:读多少字节
  • 位置信息:数据在哪个 DataNode 上(用于任务本地化调度)
public abstract class InputSplit {  
    // 获取分片大小(字节)  
    public abstract long getLength() throws IOException, InterruptedException;  

    // 获取分片所在的节点位置(DataNode 主机名)  
    public abstract String[] getLocations() throws IOException, InterruptedException;  

    // 获取更详细的位置信息(如机架信息),用于高级调度  
    public SplitLocationInfo[] getLocationInfo() throws IOException {  
        return null;  
    }  
}

InputSplit vs HDFS Block:

InputSplit HDFS Block
是什么 逻辑分片,MapReduce 的概念 物理存储块,HDFS 的概念
存什么 不存数据,只存”位置+长度” 存实际数据
关系 一个 Split 通常对应一个 Block(默认) Block 是 Split 的数据来源

默认情况下,Split 大小 = Block 大小(128MB)。所以一个文件有几个 Block,就有几个 Map 任务。

FileInputFormat:分片大小的计算公式

FileInputFormat 是所有文件类 InputFormat 的父类,定义了分片的核心逻辑:

splitSize = max(minSize, min(maxSize, blockSize))

三个参数控制分片大小:

参数 默认值 作用
blockSize 128MB HDFS 块大小
minSize 1B 最小分片,设大了可以合并小文件
maxSize 无限大 最大分片,设小了可以拆分大文件

举个例子:

  • maxSize=64MB,分片=64MB → 一个 128MB Block 会被切成 2 个 Split,产生 2 个 Map 任务
  • minSize=256MB,分片=256MB → 两个 128MB Block 合并成 1 个 Split,产生 1 个 Map 任务
阅读全文 »

MapReduce深度解析:分而治之,把大数据拆成小任务

处理 TB 级的数据,单机跑不动。MapReduce 的思路很简单:把大任务拆成小任务,分到多台机器上并行跑,最后把结果汇总。

这个思路来自函数式编程的 Map 和 Reduce——Map 负责”拆”和”算”,Reduce 负责”合”。这套模型成了 Hadoop 时代的计算标准。

MapReduce 在干什么?分三阶段

一个 MapReduce 作业,数据走三个阶段:

输入 → Map(拆分+计算)→ Shuffle(排序+传输)→ Reduce(汇总)→ 输出

Map 阶段: 把输入数据拆成多个分片,每个分片交给一个 Map 任务处理,输出一堆键值对 (key, value)

Shuffle 阶段: 把 Map 输出的键值对按 key 分组、排序,然后传给 Reduce。

Reduce 阶段: 每个 Reduce 任务拿到一组相同 key 的所有 value,聚合计算,输出最终结果。

完整流程:一条数据怎么走

第 1 步:输入分片(Input Split)

输入文件被切成多个逻辑分片(InputSplit),每个分片对应一个 Map 任务。默认按块大小切(128MB),所以一个 1GB 文件大约产生 8 个 Map 任务。

第 2 步:Map 处理

每个 Map 任务读取自己的分片,逐行处理,调用 map() 函数输出键值对。

比如 WordCount,输入 "hello world" → 输出 <"hello", 1><"world", 1>

第 3 步:Shuffle——最复杂的环节

Shuffle 是数据从 Map 到 Reduce 的传输过程,包含三个关键动作:

  • 分区(Partition):决定每个键值对去哪个 Reduce。默认按 key 的哈希值取模。
  • 排序(Sort):Map 端输出按 key 排序,Reduce 端拉取后再次合并排序。
  • 传输(Transfer):Reduce 从所有 Map 任务拉取属于自己分区的数据。

第 4 步:Reduce 处理

每个 Reduce 任务收到一组 (key, values),调用 reduce() 函数做聚合,输出结果到 HDFS。

阅读全文 »

Hibernate 缓存机制深度解析:一级缓存、二级缓存与查询缓存的实践

Hibernate 缓存是提升查询性能的核心机制,通过减少数据库访问次数,显著降低系统 IO 开销。Hibernate 提供一级缓存(Session 缓存)二级缓存(SessionFactory 缓存)查询缓存三级缓存体系,各级缓存的作用范围、管理方式和适用场景差异显著。本文系统解析各级缓存的原理、配置、使用方式及最佳实践,帮助开发者合理利用缓存优化系统性能。

Hibernate 缓存体系概述

Hibernate 缓存按 “作用范围” 和 “管理粒度” 分为三级,各级缓存的核心定位如下:

缓存级别 作用范围 管理主体 启用方式 核心作用
一级缓存 Session(事务级) Hibernate 自动 强制启用,无法关闭 确保同一事务内重复查询同一对象时无需访问数据库
二级缓存 SessionFactory(进程级) 第三方缓存插件 手动配置启用 共享多事务 / 多 Session 的查询结果,减少重复查询
查询缓存 SessionFactory(进程级) 第三方缓存插件 手动配置 + 代码标记 缓存 HQL/QBC 查询结果,避免重复执行相同查询

一级缓存(Session 缓存)

一级缓存是 Hibernate 的内置缓存,与 Session 生命周期绑定,属于 “事务级缓存”,是 Hibernate 确保事务一致性和减少数据库访问的基础。

核心原理

  • 存储内容:当前 Session 加载的持久化对象(包含 OID 和属性值);
  • 生命周期:随 Session 创建而初始化,随 Session 关闭 / 清理(clear())而销毁;
  • 强制启用:无需配置,Hibernate 自动管理,无法手动关闭;
  • 核心价值:同一 Session 内多次查询同一 OID 的对象时,仅第一次访问数据库,后续直接从缓存获取,避免重复 SQL 执行。

一级缓存的关键操作

一级缓存通过 Session 的核心方法实现缓存管理,常见操作如下:

操作方法 作用描述
get()/load() 加载对象时,先检查缓存:存在则直接返回,不存在则查询数据库并放入缓存
save()/update() 执行保存 / 更新时,先更新缓存中的对象状态,事务提交时同步到数据库
flush() 同步缓存中的对象状态到数据库(不清空缓存),确保缓存与数据库一致
clear() 清空缓存中所有对象,所有持久化对象变为游离状态
evict(Object obj) 从缓存中移除指定对象,该对象变为游离状态
contains(Object obj) 判断对象是否在缓存中

一级缓存实践示例

阅读全文 »

Hibernate 批量操作全解析:四种实现方式的原理、实践与优化

在处理大量数据(如批量插入 10 万条记录、批量更新订单状态)时,常规的单条操作(如循环调用session.save())会因频繁的数据库交互和内存占用导致性能瓶颈。Hibernate 提供四种批量操作方式,覆盖从简单到高性能的不同场景,本文将逐一解析每种方式的核心原理、代码实现、注意事项及性能对比,帮助开发者选择最优方案。

批量操作的核心挑战与优化目标

核心挑战

  • 频繁数据库交互:单条操作每次执行 1 条 SQL,10 万条记录需 10 万次数据库调用,网络 IO 开销大;
  • 内存溢出:Session 一级缓存会保存所有处理过的对象,大量对象堆积导致 OutOfMemoryError
  • ORM overhead:Hibernate 的脏检查、关联级联、缓存同步等机制会增加额外性能消耗。

优化目标

  • 减少 SQL 执行次数:通过批量 SQL(如 INSERT INTO ... VALUES (?), (?), (?))减少数据库调用;
  • 控制内存占用:及时清理缓存,避免对象堆积;
  • 绕过不必要的 ORM 机制:高性能场景下跳过缓存、脏检查等环节,直接操作 JDBC。

四种批量操作方式详解

方式一:通过 Session 实现批量操作

核心原理

利用 Session 的批量处理能力,结合 flush()(同步缓存到数据库)和 clear()(清空缓存)控制内存,通过配置 hibernate.jdbc.batch_size 实现 SQL 批量发送。

关键配置(必须)

hibernate.cfg.xml 中配置批量大小,控制每次向数据库发送的 SQL 条数:

阅读全文 »

Hibernate 检索方式全解析:从导航查询到本地 SQL 的实战指南

Hibernate 提供多种灵活的对象检索方式,覆盖从简单的单对象查询到复杂的多条件筛选场景。这些方式可分为对象导航检索、OID 检索、HQL 检索、QBC 检索、本地 SQL 检索五类,每类适用于不同业务需求。本文结合代码示例,详细解析每种检索方式的实现逻辑、核心 API 及最佳实践,帮助开发者高效选择查询方案。

检索方式分类与核心场景

Hibernate 检索方式的设计遵循 “从简单到复杂、从面向对象到原生 SQL” 的梯度,各类方式的核心定位如下:

检索方式 核心原理 适用场景 优势
导航对象图检索 通过已加载对象的关联属性获取关联对象 已知主对象,需访问其关联对象(如订单→客户) 无需手动写查询,依赖对象关系自动加载
OID 检索 通过主键(OID)直接获取对象 已知主键,需快速查询单个对象 最简单高效,直接定位记录
HQL 检索 面向对象的查询语言(类似 SQL,操作对象而非表) 复杂多条件查询(如多表联查、聚合统计) 跨数据库兼容,支持动态参数绑定
QBC 检索 基于 API 的无字符串查询(链式调用构建条件) 动态条件查询(如条件数量不确定的筛选) 类型安全,避免 SQL 注入,无需拼接字符串
本地 SQL 检索 使用数据库原生 SQL 查询 复杂 SQL 场景(如存储过程、特殊函数调用) 充分利用数据库特性,性能最优

详细检索方式解析

1. 导航对象图检索方式

核心逻辑

基于对象间的关联关系(如一对多、多对一),通过已加载对象的关联属性直接获取关联对象,无需手动编写查询语句,依赖 Hibernate 的关联检索策略(延迟 / 立即加载)。

代码示例(一对多关联:客户→订单)
阅读全文 »
0%