
湖仓一体架构解析数仓架构选择第48天最近在梳理数据架构演进相关的内容正好走到数仓架构选择这个节点想结合手头的一个实际项目聊聊湖仓一体Lakehouse这件事。这不是一个新鲜概念了但真正能把怎么选、怎么落地、踩过什么坑讲清楚的并不多我试着用自己的实际经历把这条线串一遍。先说清楚背景。我之前在的那家公司数据平台经历了从传统数仓到数据湖再到湖仓一体的完整迁移过程我自己完整参与了第三个阶段。今天想写的这篇文章核心就围绕一个决策当数据规模、分析需求、实时性要求都上来了老一套的架构撑不住的时候到底该怎么选新的数仓架构为什么最后选了湖仓一体而不是其他方案以及落地过程中遇到的那些意料之外的坑。这篇文章适合正在面临数仓架构升级、或者刚开始接触湖仓一体的朋友。如果你只是听说过这个概念、想知道它和传统数仓有什么区别、落地要花多大力气那这篇应该能给你一个比较完整的参考。1. 湖仓一体是什么解决了什么问题1.1 从一次真实的架构选型说起事情起因很简单当时业务方提了一个需求说要把近一年的用户行为明细数据全部开放给算法团队做特征工程同时数据产品那边还要求能在这份明细数据上跑一些即席分析查询响应时间要求在秒级。这个需求放在五年前根本不算事无非就是数据量大了点。但问题在于当时的用户行为明细数据每天新增大概是2亿条左右一年下来就是700多亿条存到传统的MPP数仓里存储成本和查询性能都撑不住。而如果放到数据湖里虽然存储便宜但查询性能又达不到产品侧秒级响应的要求。这就是典型的既要又要的需求。再往前一步说其实很多公司都会遇到类似的困境数仓存明细数据太贵数据湖查明细数据太慢。湖仓一体这个架构说白了就是奔着解决这个矛盾来的——用数据湖的低成本存储来接纳所有原始数据再用数仓的查询引擎、治理能力和事务支持来保证分析性能和数据质量。1.2 湖仓一体不是数据湖数仓的简单拼接很多人对湖仓一体有个误解觉得就是把数据湖和数据仓库两套系统对接起来平时数据放数据湖查询的时候通过某种方式映射到数仓里。如果是这么简单那这个问题十年前就能解决根本不需要等湖仓一体这个概念出来。真正的湖仓一体核心在于存储层面只有一个湖存储但是在这个存储之上直接构建了数仓级别的能力。具体来说就是把事务支持、索引、元数据管理、权限控制这些原本属于数仓的能力直接下沉到数据湖存储层。查询引擎直接读取湖上的数据文件而不是先把数据拷贝到另一个存储系统里再查。少了这一道搬运的过程才是湖仓一体和传统湖仓拼接方案最本质的区别。我记得当时调研阶段看到某个开源方案的时候第一反应也是这不就是一个数据库挂在对象存储上吗后来真正理解了它的设计才明白关键是它把原来数仓中最值钱的元数据管理和事务能力搬到了存储层让多个不同引擎可以共享同一份数据同时还能保证读的是一致的数据快照。这才是一体这两个字的含义。2. 数仓架构演进路线与选型逻辑2.1 传统数仓架构的痛点在哪里传统数仓架构和它配套的那套ETL流程、维度建模方法论在很长一段时间里是数据分析的绝对主力。它的优势很明显数据质量高、口径统一、查询性能稳定。但它有两个无法回避的问题。第一个问题是扩展成本。传统MPP数仓在数据量到一定程度之后扩容的成本几乎是线性上升的而且很多MPP数仓的扩容操作伴随一定的业务停写窗口。业务量小的时候无所谓数据量一旦上来每次扩容都是一次伤筋动骨的大手术。第二个问题是数据类型固化。传统数仓天生是为结构化数据设计的往里面灌半结构化、非结构化的数据虽然技术上能实现但成本很高查询性能也不理想。而这些年业务侧对非结构化数据的需求越来越多——日志文件、图片元数据、传感器数据、文档内容都需要进入分析体系。逼着架构往更灵活的存储方向走。2.2 数据湖的兴起和它留下的烂摊子数据湖的出现很大程度上就是为了解决传统数仓不够灵活、扩容太贵的问题。把数据以原始格式扔到廉价的分布式存储上等要用的时候再按需解析。这个思路在存储成本上确实无敌但也带来了新的麻烦。最典型的问题是数据沼泽。存进去的东西越来越多但没人知道里面到底有什么、质量怎么样、能不能信。因为没有统一的事务控制可能出现读到了写到一半的数据这种恶心场景。数据管道跑着跑着失败了留下的半成品文件既不能自动清理也不能保证下一次读取时跳过。搞数据工程的朋友肯定都经历过修补补丁式的数据湖维护——今天修一个文件覆盖问题明天补一个分区目录权限问题后天再处理一个脏数据导致的查询报错。数据湖本身没有对数据质量负责的意识。在实际使用中有时候为了赶进度直接把原始文件往湖里一扔、建个临时表就开始分析了等分析完了发现数据有重复或者缺失再回头排查的时候要花费的时间远远超过当初直接走正规数仓流程的时间。2.3 湖仓一体为什么能同时补上两边的短板湖仓一体这个思路不算复杂存储用数据湖那套廉价、灵活、能存任意格式的数据但在此之上加上一套完整的元数据管理、事务控制、权限管理、索引优化能力。相当于让数据湖长出了数仓的灵魂。我用一个比较粗糙的类比来说明。传统数仓像一个管理严格的图书馆所有书必须按规范登记、编号、上架借阅有记录保管得很好但藏书量受限于馆舍面积增加书架很贵。数据湖像一个堆满货物的仓库什么都能往里放仓储成本便宜但找东西基本靠记忆货物质量没有保障可能一箱货是完整的、隔壁一箱是碎的。湖仓一体则像一个用上了现代管理系统的智能仓库——货物随便放但每一件都贴了电子标签、录入系统、有存取记录、库位自动管理找货和管控都做得到。这个类比能比较好地解释为什么湖仓一体在架构决策中会成为最大公约数方案保留数据湖的灵活性同时把数据治理做起来业务团队既能低成本存数据又能信任数据质量。3. 架构选择的几个核心考量维度3.1 数据规模和处理模式是决定性因素如果数据量不大每天新增几百万行、总量不超过几十亿行那我建议老老实实待在传统MPP数仓里没有必要为了追新概念去搞湖仓一体。湖仓一体的优势要在数据量足够大、需要低成本存储大量原始数据时才能体现出来。小体量场景下引入这套架构只会增加维护成本收益却微乎其微。处理模式也很关键。如果业务主要是跑批处理每天凌晨定时加工数据对时效性要求不高那传统数仓足够。如果业务需要对实时数据做分析而且数据分析还要和全量历史数据做关联湖仓一体的优势才真正放大。因为实时链路的数据可以直接以低成本落入湖存储而查询引擎能从湖上同时读取历史全量数据和实时增量数据不需要设计复杂的实时数仓离线数仓双链路合并逻辑。3.2 团队能力与运维成本要冷思考这是很容易被忽视的一个维度。湖仓一体在技术概念上很美好但落地运维的复杂度比传统数仓高一个级别。传统数仓基本上是一个开箱即用的黑盒运维重点在SQL性能调优。湖仓一体则涉及存储、文件格式、表格式、查询引擎、事务机制多个层面的协同出了问题要排查的环节多很多。我们当时团队的情况是三个人负责平台基础设施两个数据工程师负责业务数据开发。说实话湖仓一体引入初期性能调优和问题排查的压力非常大。比如某个查询引擎对格式A的文件支持得很好对格式B的支持还在实验阶段比如某个表格式版本升级之后老版本文件还可以读取但写入需要转型。这些细节散落在各种文档里靠团队自己去趟。好在几个人都比较能扛花了大概两个月时间把这些隐藏知识摸清了。所以给一个建议如果团队人数少于五个人、且没有专职的数据平台研发那湖仓一体还是要谨慎评估至少需要有一个人能愿意花大量时间吃透底层原理不然上线之后遇到问题会非常被动。3.3 业务场景的匹配度比技术潮流更重要架构选型技术上再怎么分析最后都要落到业务需求上。我总结了一套问题清单每次选型前都会先过一遍业务方需要查询的数据结构化的比例高还是非结构化的比例高数据时效性要求是分钟级、小时级还是天级是否需要对同一份数据同时跑批处理、流处理和即席查询数据的生命周期有多长是否长期保留大量明细数据业务分析是否会经常修正口径需要回刷历史数据这些问题如果答案主要集中在非结构化比例高、需要廉价保留明细、需要多种计算模式那湖仓一体基本是逃不掉的选择。如果答案都是结构化、天级、批量跑批那真的没必要折腾。我见过一个失败的案例某团队拍板上了一套看起来很前沿的湖仓一体方案但实际业务需求就是每天跑一次常规报表数据量也不大。结果不仅没有带来任何改进反而因为链路复杂了数据产出的稳定性下降了。这个事例能给我们的教训是——架构选型永远是为了适配业务而不是为了技术而技术。4. 实操过程与核心环节实现4.1 存储层选型与布局规划当时我们选定湖仓一体方案后第一步是规划存储层的布局。因为底层的分布式存储直接决定了数据的可靠性和扩展能力这一步选型选得好后面能省很多心。存储层选型主要考虑了几点是否支持标准的对象存储接口扩容是否平滑是否有跨区域容灾能力以及单位存储成本。对象存储基本是共识选择但具体选哪个产品不同公司有不同的考量这里不展开品牌对比关键是要注意一点——存储桶的目录规划要在初期就设计好。湖仓一体架构下存储目录的布局直接影响后续的数据管理和权限控制。我们当时的做法是把桶按照层级划分raw层存放原始数据、clean层存放清洗后的数据、app层存放应用模型数据每层之间不允许跨层读写。这个分层方式和传统数仓的ODS、DWD、ADS其实是异曲同工的——本质上都是将数据生命周期分成几个阶段每层各司其职便于追溯和治理。操作层面有一个重要经验文件分区策略要结合数据特征定。比如时间字段分区先想清楚下游查询的过滤习惯是走天级还是小时级分区粒度过细会导致小文件问题过粗又会导致查询扫描数据量过大。我们最开始把用户行为数据按小时分区结果因为每小时的文件太小产生了大量小文件后续不得不做合并操作。后来改成按天分区、再在表结构上用时间字段进行pruning性能反而提升了不少。4.2 表格式选择这块要慎重湖仓一体架构中表格式是核心中的核心。常见的开源表格式主要有三种各有侧重点。一种格式在ACID事务和增量读取上做得比较完善流读流写支持得很顺滑适合构建实时的数据管道。它的优点是并发写控制做得比较好多个任务同时写同一张表不会出问题缺点是学习曲线有点陡各种配置项很多且与其他组件集成时偶尔有兼容性小坑。另一种格式在数据文件管理上做了很多优化比如文件的自动合并、列级统计信息管理查询性能优化做得比较深入。它更偏向大数据量的分析场景在离线批处理场景下表现稳定但实时写入在早期版本中相对弱一些需要依赖外部机制来支持增量处理。还有一种比较轻量主要作为前两者的补充存在在一些特定场景下也有使用。我们当时做了对比测试选型标准和过程大致是这样的先用同样的数据量分别装载到两种表格式下然后跑同样的查询SQL记录查询耗时再进行并发写入压力测试模拟多个数据管道同时写入之后查询数据的正确性。测试结果其实差别不算太大最终决定性的因素是我们对实时数据管道的要求比较高——希望流式写入和批量写入可以同时操作同一张表而不产生冲突。所以选了事务支持更好的那一版。说实话表格式选型没有绝对的对错关键看你的核心场景是偏实时还是偏批量。但如果让我重新选一次我会考虑一个额外的维度生态成熟度。不要只看单个组件的功能要看它和周边生态系统的集成广度、社区活跃度、版本迭代稳定性。这一点的价值在使用过程中才能体现出来初期评估很难感知到但等你在生产环境遇到问题时生态成熟的好处就变得非常明显了。4.3 查询引擎的接入与调优湖仓一体的存储层解决了数据放在哪的问题查询引擎解决的是数据怎么算的问题。在架构中可以做到存储与计算分离一份数据可以由多种不同的引擎来访问适配不同的计算需求。我们在接入查询引擎的时候踩过一些坑这里分享几个值得关注的细节。第一个是文件格式的选择。虽然理论上查询引擎能读取存储上的各种格式但实际性能差异非常明显。经过对比测试对于大多数分析型查询列式存储格式的性能优势极具压倒性。列式存储按列组织数据分析查询只需要读取涉及到的列IO量大幅减少。这一点在几百GB甚至上TB级别的表上体现得非常明显——可能差一个数量级。第二个是统计信息采集。很多刚接触湖仓一体的开发者会忽略这个操作觉得数据文件放上去就能查了。但如果没有统计信息查询引擎的优化器就像蒙着眼睛做计划经常选错执行策略。比如一个本来应该走Broadcast Join的查询因为没有统计信息优化器选择了Sort Merge Join结果就是中间结果巨大、查询时间翻了几倍。采集统计信息是一个需要定期执行的维护操作要像数仓里的Analyze Table一样养成习惯。第三个是缓存策略。虽然存储与计算分离的好处是弹性伸缩但每次查询都从远端存储拉数据延迟和成本都扛不住。我们最后采用了分层缓存策略把高频访问的热数据表缓存到本地节点把中频访问的数据做部分文件缓存冷数据不缓存直接读远端。从配置的JSON参数调整来说其实并不复杂但需要结合业务访问特征做一些反复实验和调优。4.4 数据治理与权限管理的落地湖仓一体解决了能存、能算的问题但能管是另一个层面的挑战。尤其是数据湖原有的野生气质如果不引入严格治理湖仓一体最终还是会退化成一个有事务能力的数据沼泽。我们当时在数据治理上做了三件事可以说是最关键的三个动作。第一件是建立统一的数据目录。所有入库的数据表都要注册到元数据管理组件中包括表名、字段注释、负责人、数据来源、刷新频率、生命周期。强制规定没有注册的表不允许被业务查询。这个规定一开始被数据工程师吐槽流程繁琐但坚持了两个星期之后效果就出来了——新人找数据不再需要到处问人数据字典每个人都看得到。第二件是血缘追踪。每次数据加工任务执行的时候自动记录输入输出表之间的依赖关系。这个血缘信息在排查问题时特别有用——业务报表数据出了问题可以沿着血缘往下游追踪快速定位是源头数据出的异常还是加工逻辑出了问题。血缘数据的维护成本其实不高关键是架构设计时就把这套机制纳入考虑。第三件是细粒度的权限管控。湖仓一体架构下权限管理需要同时管到存储和表两个层级。单纯控制表权限是不够的因为了解存储路径的人可能直接绕过表去读文件。我们当时的方案是存储层做桶级别的权限隔离表级别做行列级别的权限控制两层权限同时校验达到双重保险的效果。而且权限策略要能支持按数据分类自动打标比如敏感数据表自动限制只能由特定角色访问。5. 常见问题与排查技巧实录5.1 小文件问题湖仓一体最大的隐形杀手这是我第一次在生产环境被坑得最惨的问题没有之一。湖仓一体的小文件问题比传统数仓要严重得多因为底层是对象存储文件写入天然是追加式的而且实时任务频繁写入会产生大量的碎片文件。症状很典型查询速度越来越慢但是数据量并没有显著增长。我们用工具分析后发现一张本来应该只有几百个文件的表实际文件数高达二十多万个。查询引擎每次执行查询时光扫描文件元数据就耗时很久更别说真正读数据了。解决办法无非是两种一种是使用有自动合并能力的表格式让系统在后台定期把小文件合并成大文件另一种是手动合并写一个定时任务把小于某个阈值的文件读出来重新写一遍。两条路我们都走过最终依赖表格式原生的优化能力来处理手动合并作为兜底方案。经验总结新建表的时候一定要预估写入频率和文件大小让文件大小控制在合理范围内。如果实在没法控制那就接受定期合并是一等公民这个现实别等到性能下降了再来处理。5.2 流批一体数据打架的问题湖仓一体的卖点之一是流批一体——流式任务和批式任务能同时处理同一份数据。但实际过程中打架的情况时有发生。最典型的一种场景实时任务写入一张表另一个批量任务也在同时清理这张表的过期分区结果实时任务写了一半的数据被清掉了数据对不上账。或者两个任务同时写同一张表各自认为自己是对的了结果产生了数据冲突。这个问题的根源在于很多湖仓表格式在并发控制上并不像传统数据库那样强。我们最后的解法是严格控制写入模式一张表只允许一种写入方式要么全部是流式写入要么全部是批量写入避免混用。如果确实需要两个任务写一张表那就把写入路径分开先各自往中间表写再通过一个整合任务做合并。这里面还有个经验不要过度迷信流批一体这个概念。它在设计上很美好但在生产环境中要落地得平滑需要付出额外的架构成本。对于大多数业务场景我的建议是能分开就分开实在分不开才合。5.3 跨引擎查询的结果不一致我们在实际使用中发现同一个查询在不同引擎下执行结果可能不一样尤其在涉及浮点数运算、时间精度、字段空值处理这些细节时。这种不一致在单引擎的架构中根本不会出现但湖仓一体天然多引擎这个问题就暴露出来了。排查思路很简单就是在遇到结果对不上的时候先对比两边的执行计划看看是不是因为优化器的差异导致运算顺序不同。但治本的办法是对核心指标口径做统一在生成数据的时候就保证口径一致让查询引擎只做简单的计算不做复杂的隐式类型转换。另外一个细节是时间字段的精度。不同引擎读取同一个时间戳字符串时有的解析成秒级精度有的解析成毫秒级精度一对比就差了几个小时看起来像数据错了其实是精度截断的问题。解决方式是在建表规范里强制统一时间字段的格式和精度写入端和读取端严格遵守。6. 个人实操心得与后续规划6.1 踩过坑之后形成的几条选型经验这次湖仓一体架构落地从决策到稳定运行大约花了四个月中间踩过的坑、积累的教训不少。如果让我把核心经验浓缩成几句话大概是这样的第一架构选型不要太浪漫。《数据仓库》那本书里讲了各种理想架构但实际落地时业务、成本、团队、周期四个因素才是真正的约束条件。理想架构只有在这些条件都满足的情况下才是最优解否则就要做妥协。第二数据治理要前置。湖仓一体的架构灵活性很高但如果治理没跟上灵活性就会变成混乱。我们因为初期把治理优先级放得比较低后期补了很多课。现在回过头看如果一开始就把数据目录、血缘、权限这三件事做好后面能省一半的排查时间。第三选型测试不能省。我们花了两周时间做 PoC 测试测试过程中发现的问题帮我们在架构设计阶段就避免了很多坑。比如某个存储库在某类并发场景下会概率性报错遇到这类问题测试就是唯一能提前暴露隐患的途径。第四团队学习曲线要提前安排。湖仓一体的技术栈跨度比较大从存储、表格式到查询引擎每一层都需要时间理解。如果团队之前没有底层的经验建议先做一两个月的技术预研别直接上生产。6.2 湖仓一体的未来演进方向这块是我个人的观察和一些设想供参考。湖仓一体的大方向不会变但它会继续往智能化和服务化演进。智能化方面自动化的数据治理会是一个趋势——比如依靠统计信息和查询日志自动识别伪热表把合并、压缩、缓存策略做成自动化决策减少人工干预。服务化方面湖仓一体会从平台能力走向数据产品能力让业务分析师甚至业务运营人员可以直接通过自然语言交互来获取数据洞察而不再依赖数据工程师写SQL、调管道。我们目前已经在做的方向是把报表开发中一些重复性的ETL逻辑沉淀成可视化组件业务人员可以自助搭建数据管道减少数据团队的日常工作量把更多时间留给数据治理和模型优化。6.3 最后分享一个小技巧做湖仓一体性能调优的时候最容易被忽略的检查项是——你的查询到底扫描了多少数据。很多时候SQL看起来写得没问题但实际扫描的数据量是必要的几十倍。我建议在查询引擎的配置中开启执行计划可视化先看每个查询的扫描分区数、扫描行数再做针对性优化。另外遇到性能问题的时候先看一眼底层文件的数量和大小分布再回头看SQL。我们80%以上的性能问题其实都是存储文件层面的问题而不是SQL写法的问题。处理好文件布局比调SQL更一劳永逸。这篇关于数仓架构选择的记录写到这里基本把湖仓一体的选型思考、落地过程和踩坑经验都覆盖了。里面对架构选型的考量思路、技术细节的具体取舍都是我个人的实践判断如果你们团队的情况不同具体决策可能也会不同。但有一点是一致的——任何技术架构都是为业务服务的把业务需求理解透了架构选型其实就成功了一半。