
我上一篇实践笔记写的是环境准备和入门踩坑评论区不少朋友都问后续这篇就算《大数据实践笔记2》。这篇我不打算按课程目录走直接把过去几个月里真正让我“被教育”的几个场景翻出来集群部署的容量规划、离线链路里的小文件与N1问题、实时任务的位点与乱序处理、数据大屏落地还有最近接触的遥感影像数据工程。每个话题都是做项目时实际踩过坑、被面试官追问过、或者被网上各种免费资料带偏过的点理解清楚之后很多大而化之的概念就落地了。1. 集群到底怎么搭被三台机器反复教育之后1.1 搭集群前先算清楚资源账很多人一上来就按“标准架构”规划集群三台NameNode、五个ResourceManager、十几个DataNode觉得组件齐全才叫大数据。实际上绝大多数个人项目和中小型业务三台机器起步就够了关键不是节点数量而是每台机器上跑什么角色、跑几个角色、峰值时资源够不够。我吃过最大的亏是拿到一台16核64G的服务器就当Master然后一股脑把NameNode、ResourceManager、HiveServer2、Spark Thrift Server、MySQL、调度器全部塞进去。看起来没毛病一跑数据就出问题NameNode频繁GCSpark任务提交后一直等资源HiveServer2动不动OOM。后来逐项排查才发现Master的堆内存配置完全没做规划。一个相对实用的估算方法是先拆你的负载再定配置。假设你有三台机器每台64G内存角色可以这样分第一台负责主节点职责NameNode给12G堆内存ResourceManager给8G堆内存剩下的留给操作系统和辅助进程第二台放HiveServer2、调度器、MySQL等上层组件给足16G到20G第三台和第一台都作为Worker跑DataNode和NodeManager把主要内存留给执行容器。如果只有三台机器ZooKeeper、JournalNode这些角色不用单独拆机器三台各跑一个实例做Quorum即可。有个很反直觉的点堆内存不是越大越好。NameNode的堆内存主要是存元数据单机几百万文件块也就需要几G到十几G堆太大反而会导致Full GC时间变长。当时我把NameNode堆调到32G结果一次checkpoint后Young GC飙到几秒任务直接超时。后来调到12G配合调整了GC参数稳定很多。1.2 部署顺序其实暗藏玄机集群部署的顺序不是“想到什么装什么”而是有依赖关系的。我的建议是先装ZooKeeper再装HDFS然后装Yarn接着装Hive最后才碰调度器和查询引擎。为什么因为HDFS是存储底座Yarn是计算底座Hive依赖HDFS的目录和Yarn的队列如果你先把Hive装好再配HDFS很容易遇到路径找不到、权限不对的连锁问题。实际操作中格式化NameNode是个分水岭。格式化前要确认配置文件里的core-site.xml、hdfs-site.xml都改对了尤其是dfs.replication默认副本数、dfs.namenode.name.dir和dfs.datanode.data.dir的实际路径。我之前图省事把数据目录放在/tmp下机器一重启所有DataNode都报“Storage directory not found”NameNode进入安全模式一直出不来。原因很简单/tmp目录被系统清理了。还有一个经常被忽略的点每台机器的主机名和/etc/hosts必须一致并且集群内全部使用主机名通信不要用IP。数据节点注册时如果用IP后面切换网络或DNS解析出问题整个集群的内部通信都会变得极其脆弱。我在配HA时遇到过journalnode无法通信排查到最后发现是hosts里写了一个不可达的内网域名。1.3 我踩过的资源规划坑资源规划里最常见的坑不是算力不够而是端口冲突和内存超卖。HDFS默认端口是9870新版本或50070老版本Yarn的ResourceManager是8088HiveServer2是10000Spark UI是4040。看着都不冲突但Spark提交任务时会动态占用4040之后的端口多个SparkSession同时跑很容易把某个服务的端口挤掉。我的做法是给Spark配置固定的spark.ui.port范围并且在提交脚本里加上随机偏移。内存超卖则是“看起来每个组件都只用了8G加起来却超过物理内存”的问题。组件占用的内存不只是JVM堆还有堆外内存、页缓存、Python或Native进程的内存。Spark执行器的spark.executor.memory和spark.executor.memoryOverhead要一起看后者默认是前者的10%如果你的任务涉及大量shuffle建议调到15%到20%。我当时在64G的Worker上给每个executor分20G堆内存加上overhead两个executor就把机器吃满了NodeManager直接被系统OOM Killer干掉日志还查不到明显报错。磁盘容量也是大头。HDFS默认副本数为3意味着100G的数据实际占用300G磁盘。如果你只有3台机器每台1T磁盘理论上最多可以存333G左右的数据但还要留出日志、临时文件、shuffle中间结果的余量。建议磁盘使用率不要超过70%超过之后要立刻开始考虑扩容或清理。2. 离线数仓链路里那些“反直觉”问题N1、小文件与调度依赖2.1 N1问题在大数据里的真实面目N1这个词最早常见于ORM框架说的是查了1次主表拿到N条记录再逐条访问关联表导致N次查询。大数据环境里也有类似的变形面试官问“N1问题”时想听的不只是SQL层面的认知而是数据链路里“放大了请求次数”的隐患。我遇到过最典型的场景是用Spark读取一张千万级的订单明细表业务逻辑里有一段需要关联用户维度信息结果代码写成了在map算子内部调用外部API查询用户数据。看起来逻辑没错跑起来之后下游接口被瞬间打爆整个ETL任务在连接超时中徘徊。本质上就是把N1问题搬到了分布式计算里主表有N条记录每条记录发起一次外部请求总请求量从1变成了N。正确做法是提前把维表加载成广播变量或者做分布式Join。如果维表本身也比较大超过广播阈值可以用Hive的MapJoin hint、Spark的bucket join或者按关联键做预聚合反正不要在算子内部做外部IO。另一个跟N1相似的问题是“数据倾斜下的TopN”每个key都去遍历全量数据等于每个key都产生一次全表扫描。倾斜处理的核心思路是先局部聚合再全局聚合或者对热点key加随机前缀打散这部分是面试高频也是实际调优的重头戏。2.2 小文件治理平时没人管出事人人找你小文件是离线数仓里最阴魂不散的问题。Spark/Hive写HDFS时如果没有控制分区和任务并行度大批量小文件就会被写出来。每一个文件都要在NameNode里占一条元数据记录几万个小文件可以直接把NameNode内存吃掉下游再读这些文件时每个文件都会被当成一个InputSplit启动大量Map任务调度开销比计算本身还大。我记得有一次跑离线任务源表只有5000万条数据按天分区Spark动态分区写的时候没有设置spark.sql.shuffle.partitions默认200个分区但分区字段的组合有50多个最终每个分区下都散落着几十个几十KB的小文件。第二天早上所有读这张表的任务都慢了一倍查NameNode日志才发现文件数一下多了十几万。治理方案有几种最简单的写入后用Hive的ALTER TABLE ... CONCATENATE对小文件做合并更彻底的办法是从写入端控制。Spark写Hive表时可以把spark.sql.shuffle.partitions调低或者先按分区字段做一次repartition让每个分区只由一个或少数几个task来写Streaming任务则要配合coalesce和周期性的compaction把增量小文件合并成大文件。现在很多数仓方案都会引入Iceberg或Hudi这类表格式目的之一就是自带的compaction机制能自动处理小文件问题省去人工运维的痛。2.3 调度依赖的隐形成本离线任务调度不只是cron表达式那么简单。血缘依赖、重跑策略、阻塞策略这些看似不起眼的配置往往决定了链路能不能稳定。我见过一个很有意思的故障A任务依赖B任务B每天凌晨2点跑完A定的2点05分启动。有一天B因为上游数据迟到跑了2小时A在2点05分照常启动读到的分区是空的任务还成功了。下游报表因此全错最后查了半天才发现是调度时间没有跟数据就绪时间绑定。正确做法是给任务加“数据就绪检查”或者把调度触发方式改成依赖完成触发而不是死板的固定时刻。如果用的是Apache DolphinScheduler或Airflow可以设置上游成功后再触发下游如果本身就是shell脚本就在脚本开头检查目标分区文件存在性和大小阈值不满足就exit非0等重试。依赖还有个隐形成本是“重跑放大”。业务方说“把昨天数据重跑一下”如果你直接重跑最下游任务它会把所有上游重新读一遍计算成本翻几倍不说还有可能因为某层中间结果被覆盖导致数据不一致。重跑要沿着血缘逐层跑并且建议中间层用“插入覆盖分区”而不是“删表重建”的方式这样即使中途失败旧分区数据还在不会留下半截空白。3. 实时链路从0到1不是一上来就上Flink3.1 选型之前先判断需求是否“真实时”做实时项目最容易犯的错是为了用Flink而用Flink。其实很多业务场景分钟级甚至小时级的准实时就能满足需求用Spark Structured Streaming或者Kafka Streams就够了没必要引入一套完整的流处理框架。我接手过一个实时大屏项目业务方说“要每秒刷新”细聊之后发现真正关注的指标是“过去5分钟的订单量”数据延迟在1分钟以内就能接受。最终方案是Kafka接入业务日志Spark Structured Streaming做5秒微批聚合结果写入Doris前端每10秒轮询一次。整条链路简单出问题也好排查。如果当初直接上Flink Checkpoint CEP复杂度至少翻一倍运维成本也高很多。什么时候才需要Flink需要精确一次语义、复杂的Event Time窗口、状态管理、或者跨事件模式匹配的时候。比如风控规则引擎、实时对账、双十一大屏这种“数据晚到几秒会造成明显业务损失”的场景。选型要基于业务容忍度不是基于技术热度。3.2 Kafka消费位点设计与幂等实时链路里Kafka消费位点是第一个要搞清楚的概念。auto.offset.reset有latest和earliest两个常见取值。如果任务刚上线希望从当前时刻开始消费选latest如果需要回放历史数据选earliest或者手动指定offset。我记得有一次上线消费任务默认配置了latest结果Kafka里积压了两天的数据新任务启动后全部跳过业务数据对不上最后只能重建一个新的group从头消费。更麻烦的是位点提交和数据处理不是原子操作。如果先提交offset再处理数据处理失败时位点已经往前走了会丢数据如果先处理数据再提交offset处理成功后还没来得及提交就宕机重启后会重复消费。分布式系统里没有完美的“不丢不重”只能在“at least once”和“exactly once”之间取舍。Kafka Streams或Flink里可以实现真正的端到端精确一次但需要事务性写入配合传统Consumer 外部存储的做法更实际的方案是让写入目标幂等比如写入MySQL/ClickHouse时用唯一键做upsert或者写入HBase时直接按rowkey覆盖这样即使重复消费结果也是幂等的。我们当时是写Doris用标签列做replace重复数据进来覆盖旧值从结果上看业务无损。3.3 窗口乱序与迟到数据实战处理流处理最烦的不是速度是乱序。比如用户下单的事件和支付的事件可能因为网络原因支付先到、下单后到手机端离线缓存也会导致一批事件延迟很久才上报。如果窗口按Processing Time来算这些事件会被划到错误的窗口里。Flink的Event Time配合Watermark就是解决这个问题的。Watermark表示“时间戳小于它的数据我都已经收到了”没收到的是迟到数据。Watermark线设置得太低窗口会被提前触发漏掉迟到数据设置得太高窗口结果迟迟不出。实际项目里我一般根据延迟分布来调比如99%的数据在30秒内到达就把Watermark延迟设成30秒。窗口触发后迟到的数据默认会被丢弃要想处理需要把allowedLateness设大一点并且给窗口结果加一个“迟到的更新逻辑”。比如在线统计每小时成交额允许5分钟的迟到数据那么窗口触发后5分钟内还有数据进来结果需要重新计算并更新到存储里而不是简单忽略。还有更复杂的场景要用侧输出流side output单独收集迟到数据做补偿更新。这些都是面试里常问的细节点但真正要在项目里遇到乱序问题才会意识到原理和代码是两回事。4. 数据可视化大屏ReactTS项目让我重新理解了“前端也是大数据的半条命”4.1 从数据到视觉大屏组件怎么拆很多人觉得数据可视化大屏只是“画图表”直到自己动手做才发现难点在数据和组件设计。我做过一个基于React TypeScript的大屏项目也许部分人是拿免费模板改出来的但模板最大的问题是数据是造死的组件是拼出来的一旦换成真实接口布局和结构全乱。所以我的经验是组件按“图表单元”和“面板容器”分层。图表单元只负责接收一个类型明确的data props内部不关心数据是从哪里来的面板容器负责布局、标题、背景和刷新逻辑。用TypeScript写的时候把指标项定义成interface OrderMetric { orderId: string; amount: number; timestamp: number; }这样接口返回的数据能提前被类型系统校验比裸写JavaScript少了很多低级错误。大屏的设计还要考虑信息层级。第一屏放核心指标总金额、订单量、转化率做醒目的大数字卡片第二层级放趋势折线和排行列表再往下才是地图、词云这类偏展示性的内容。不要把所有图表塞进一屏用户盯着看30秒就会迷失重点。4.2 渲染性能与轮询调度大屏页面最怕的就是每秒钟重新渲染整个图表帧率掉到个位数风扇呼呼转。实际项目里常见的做法是10秒或30秒轮询一次接口前端拿到新数据后用setOption增量更新图表而不是重建图表实例。还有一个很关键的优化点数据变化时才刷新数据没变化就不要触发React重新渲染。可以用React.memo包裹图表组件配合shallow compare判断data引用是否变化。如果使用ECharts要手动管理实例不要每次render都初始化。多次实测下来性能瓶颈基本不在图表绘制本身而是组件生命周期没控制好导致一堆未销毁的定时器和监听器在后台抢CPU。轮询也要讲究策略。高峰期和低峰期可以设置不同频率多个图表可以共用一个轮询周期避免同时发十几条请求把接口打挂。我的做法是做一个统一的数据订阅层所有组件都往订阅层注册自己需要的指标订阅层维护一个定时器拿到数据后按指标分发这样无论图表数量多少对外只保持一个轮询请求链路。4.3 免费数据可视化大屏模板的坑与改造思路网上一搜“免费数据可视化大屏”能搜出一堆炫酷模板。不是不能用但改造之前要清楚风险第一是模板里的“3D地球”“飞线图”大多依赖大量动画和粒子效果数据量大或者电脑性能一般时会非常卡第二是模板的配色和字体往往是为演示而设计真实业务里可读性不够第三是模板的数据结构是写死的换成真实接口时字段名、层级、列表结构很可能对不上。我改造模板时的做法是先剥离所有假数据把所有fetch和mock替换成统一接口层再把动画效果逐个排查凡是会遮挡数据的全部关闭最后重新设置自适应方案一般用rem或scale缩放来适配不同分辨率的大屏而不是每个像素都写死。这样一套流程下来模板只留下了视觉风格逻辑完全变成自己的后续维护才不痛苦。5. 遥感卫星大数据另一个维度的数据工程5.1 一张卫星影像不是“一张图”遥感卫星大数据是最近接触的方向初看以为是“图像处理”深入之后发现本质是“空间数据工程”。卫星影像不是简单的一张JPEG而是带地理坐标的栅格数据以波段Band的形式组织可见光、红边、近红外、热红外等。一张高分影像可能有好几个波段每个波段又是一个矩阵处理不当数据量会爆炸式增长。存储上常见的是GeoTIFF格式它比普通图片多了地理配准信息每个像素对应地面的实际经纬度范围。处理链路通常包括原始影像下载、辐射定标、大气校正、正射校正、裁剪、镶嵌、生成影像金字塔等步骤。每一步都会对文件格式和坐标系有严格要求比如WGS84、GCJ02、UTM投影如果坐标系定义错了后面的空间分析全部没有意义。我在做轨道可视化覆盖分析时最耗时的不是逻辑本身而是把轨道动态计算的坐标点集合理组织成矢量数据再和栅格影像叠加。这个场景非常依赖空间索引直接遍历所有影像块根本跑不动需要先按空间范围建立网格索引再只检索目标范围内的瓦片。5.2 高效精细处理的取舍遥感数据处理里有一对核心矛盾效率和精细度。高精细的辐射校正很慢但如果你只是做地表覆盖分类的初步展示可能不需要完整预处理。我的经验是先明确下游分析需要什么精度的数据再倒推处理流程不要每个任务都跑全套。实际工程里大数据技术主要体现在分块并行把一幅大影像切分成若干个512x512或1024x1024的瓦片用Spark或者分布式计算框架分片处理最后再拼接。切片大小很讲究太小会引入大量调度开销太大又会导致单个任务内存不足。对于常见的16位深、多波段影像我一般先把每个波段的数据类型和范围摸清楚再决定分块策略。另外对象存储在这里几乎是必备的。影像文件动辄几个GBHDFS的块大小和副本策略也能扛但对象存储在这类只读、大文件、顺序读的场景下性价比更高。做实时可视化时后端先生成多级金字塔overview前端按缩放级别请求对应瓦片而不是把原始影像全部拉到浏览器里。6. 大数据面试题背后的“反推学习法”以及毕设选题建议6.1 面试官真正想听到的回答逻辑很多人在准备大数据面试时刷了一堆题却不知道每道题背后在考什么。我复盘过多次面试经历发现高频问题有清晰的考察意图面试官问“你搭过集群吗节点怎么规划”不是想知道你会不会敲安装命令而是考察系统设计和容量估算能力。这时候如果能说出“如何预估数据量、如何分配内存、如果扩展会做哪些调整”比背部署步骤强得多。问“你的ETL任务为什么慢”考察的是排查链路先看存储还是先看计算怎么判断是数据倾斜还是小文件问题回答时要呈现一个完整的排查过程而不是只给一个“加资源”的结论。问“实时和离线的区别”也不只是背概念而是确认真实项目里有没有踩过“迟到数据”“位点提交”“重复消费”的坑。你要能举出一个具体的业务场景说明当时如何折中选型这个能力比任何官方定义都加分。所以我的建议是学习时不要按“Hadoop三部曲、Spark四要素”这样去死记而是选一条真实业务链路比如从数据采集到报表展示把链路里每个环节的问题都亲手做一遍。这样做出来的知识结构是网状的记忆也牢固。6.2 数据科学与大数据技术就业方向复盘数据科学与大数据技术这个专业听起来范围很广实际就业方向大致分几类数据开发工程师做离线/实时数据管道、数仓建设偏工程要求熟悉Hadoop、Spark、Flink、SQL和数据建模大数据平台工程师维护集群、调度、权限、监控偏运维要求熟悉Linux、容器化、组件原理数据分析师/BI工程师偏业务SQL取数、指标口径、报表可视化对业务敏感度要求高算法工程师机器学习模型训练与部署要求数理基础和编程能力对大数据栈的要求相对少一些。很多人一上来就想做算法但如果连数据清洗和特征存储都没接触过直接上模型会非常痛苦。我个人的建议是第一份工作可以优先选数据开发或数仓方向先把数据从采集到应用的完整链路跑通后面再往算法或平台架构方向发展基础会更扎实。6.3 毕设选题怎么选才能不卷又落地每年都有很多人问“大数据毕业论文选题方向”我的看法是与其追热门不如找一个你确实能拿到真实数据、能完整走完“采集-存储-计算-可视化”的题目。几个比较稳的方向供参考基于用户行为日志的离线数仓分析采集埋点数据用Hive/Spark做ETL输出用户路径和转化漏斗最后用大屏展示简单且完整。基于云平台的流式数据处理用Kafka Spark/Flink做实时指标计算适合想展示实时能力的同学。遥感卫星轨道可视化与覆盖分析偏空间数据选题新颖能结合数据可视化适合有地理数据背景的同学。基于Python的招聘/电商数据分析用爬虫获取数据清洗后做探索性分析和简单预测模型适合学校环境里没有专业大数据集群的情况。毕设的评分标准通常不是“技术多高级”而是“问题定义是否清晰、数据链路是否完整、结论是否可信”。哪怕只是一个简单的用户画像系统只要你把每个环节做得扎实、踩过的坑写得清楚答辩时比那些堆了十个框架却没有落地细节的项目要稳得多。写到这里我发现这篇笔记已经覆盖了不少实际项目中反复出现的主题。如果只能给一个建议我觉得应该是大数据不是一套固定的技术组合而是面对海量数据时做出合理权衡的能力。每一次集群资源分配、每一条实时链路选型、每一块大屏渲染优化背后都在逼你想清楚“什么才是当前最重要的事”。这种判断力靠看文章学不来必须自己在任务失败、数据对不上、页面卡顿的现场里慢慢磨出来。下一篇如果有机会我再聊聊数据治理和成本优化这两个话题在真实生产环境里比想象中还要复杂。