ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

OLAP实战:从数据仓库建模到BI报表性能优化

OLAP实战:从数据仓库建模到BI报表性能优化 前阵子跟一个做供应链的同行聊天他说公司BI里最核心那张订单分析报表查询要跑十几分钟业务部门催了半年数据组天天加班优化SQL最后搞不定。我问他数据量多大他说订单明细表大概8亿行。我说你需要的不是优化SQL是把这套分析逻辑换一种存储和查询方式——也就是OLAP。今天这篇文章就把我这些年在大数据领域落地OLAP的完整思路和实操经验一次性讲透包括OLAP到底解决什么问题、主流引擎怎么选、数据模型怎么建、查询性能怎么调以及最终怎么用它驱动业务决策。适合正在做数据仓库、BI报表、数据中台或者正准备入门大数据分析方向的开发、数仓工程师和团队技术负责人。1. OLAP到底是什么——先纠正常见认知偏差很多人一听到OLAP第一反应是某个数据库产品。实际上OLAP不是某个具体软件而是一整套面向分析场景的数据处理范式。联机分析处理的英文全称是On-Line Analytical Processing跟它对应的是OLTP联机事务处理。这两个词的区别直接决定了大数据领域一堆技术选型的方向所以必须聊透。1.1 OLAP和OLTP的本质差别OLTP系统解决的是一笔业务能不能快速完成的问题。你去超市结账收银台扫一下条码库存减一账户扣款这些操作都是事务型的每次影响的数据量很小但并发极高强调数据一致性。典型的OLTP系统是MySQL、PostgreSQL部署在业务库后面支撑的是交易、订单、支付这类在线服务。OLAP系统解决的是另一个问题八千多万笔订单按省份、按月份、按商品类目汇总毛利率是多少这种查询的特征是一次扫全表或大范围数据、按多个维度分组、做聚合计算、返回结果集很小但计算量很大。拿传统关系型数据库硬扛不是不能跑而是数据量一旦上来索引基本失效全表扫描加聚合计算会让CPU和IO双双打满一条报表SQL就能把业务库拖垮。我之前接手过一个项目早期报表直接查业务库运营人员点一下昨日销售汇总生产库CPU直接飙到90%。后来把分析查询全部迁移到OLAP引擎业务库压力立刻降下来报表查询从分钟级变成秒级。这就是OLAP存在的核心价值把分析负载和事务负载分开用专门的存储和计算结构服务分析场景。1.2 OLAP领域的核心术语维度、度量、粒度想入门OLAP必须先厘清三个基础概念维度、度量、粒度。这三个词在你后面设计任何一张分析表时都会反复用到。维度是描述业务的视角比如时间、地区、渠道、商品品类。度量是你要衡量的数值比如销售额、订单量、利润额。粒度则是一行数据代表什么比如每个订单一行和每个订单项一行数据含义完全不同。用生活化类比维度就是相机拍摄的角度度量是照片里记录的数据粒度是照片的分辨率。同一座城市从高空拍是省级粒度从街道拍是门店粒度。分析时如果你用错了粒度得到的结论可能完全失真。这三个概念直接决定了OLAP建模的第一步——确定分析粒度。粒度确定之后后续的维度、度量、汇总逻辑全部围绕它展开。我在第四节会详细讲模型设计的具体步骤这里先不做展开。2. 为什么传统报表架构撑不住分析需求——从SQL到多维模型的思维转换很多团队的起点是一样的把线上业务库的表同步到数仓然后用SQL直接做汇总分析。这种架构在千万级数据量时还能撑住一旦数据规模上亿就会全面崩溃。我总结过这类架构的三个典型症状遇到了基本可以判定需要切换到OLAP体系。第一个症状是查询越来越慢且优化困难。加索引、调参、换硬件能用的手段用了收效甚微。因为分析型查询的模式跟事务型查询完全不同索引在范围扫描和聚合面前基本无效。第二个症状是报表跑批时间过长每天凌晨ETL跑三四个小时早上业务方拿到的还是昨天的数据。第三个症状是业务方开始绕过报表系统自己写SQL导数据到Excel分析导致口径混乱、数据口径对不上管理层问起来各部门各说各话。2.1 分析型查询的本质多维聚合为什么传统SQL在分析场景力不从心回答这个问题要从分析型查询的本质说起。任何分析查询本质上都是在做同一件事对某一数据范围内的记录按若干维度分组计算若干度量的汇总值。翻译成SQL就是SELECT province, category, SUM(amount) AS sales_amount, COUNT(DISTINCT user_id) AS user_cnt FROM orders WHERE dt BETWEEN 2024-01-01 AND 2024-01-31 GROUP BY province, category;这条SQL背后的计算过程是扫描一个月的数据按照省份和品类重新组织然后做聚合。数据量小时这个操作没什么压力数据量达到几亿行每次查询都重复做一次全量扫描和分组计算ICost非常惊人。OLAP的思路完全不一样。它不把每次查询当作一次全新的计算任务而是想尽办法把数据预先组织好让查询尽量只读需要的那部分。这种思路就是经典的空间换时间在数据写入时就做预处理在查询时只做轻量聚合。2.2 星型模型与雪花模型分析思维的落地OLAP建模的核心是维度建模最常见的是星型模型和雪花模型。星型模型由一个事实表和一组维度表组成形状像星星。事实表存储业务过程的度量值维度表存储描述业务的属性。比如电商订单分析事实表是订单事实表维度表有日期维度、用户维度、商品维度、门店维度。事实表通过外键关联维度表。雪花模型是星型模型的规范化扩展把维度表进一步拆分。比如商品维度拆成商品表、类目表、品牌表减少数据冗余但增加Join复杂度。我个人的实践感受是多数场景用星型模型就够了。雪花模型的规范化优势在OLAP场景中体现不出来反而每次查询多几个Join性能会有损耗。只有在维度属性极度庞杂、且需要严格控制存储成本时才考虑雪花模型。这块设计得好不好直接决定了后面几十个报表的性能。一个合理的星型模型能把复杂的分析需求统一收敛到事实表Join维度表然后聚合这条固定路径上查询优化器也容易做优化。3. 主流OLAP引擎怎么选——Doris、ClickHouse、StarRocks实测对比与取舍聊完理论进入实战。第一步就是选引擎。市面上的OLAP引擎非常多各有侧重。我在实际项目中深度用过Apache Doris、ClickHouse、StarRocks也调研过Hive和Presto/Trino这里直接给一份基于实测经验的选型对照表帮不同场景的朋友快速缩小范围。维度Apache DorisClickHouseStarRocksHivePresto/Trino存储模型列式存储明细聚合模型列式存储MergeTree家族列式存储明细聚合更新模型列式存储ORC/Parquet无存储靠外部表数据更新支持适合实时离线混合弱更新成本高支持性能好适合批量覆盖依赖底层存储并发查询高适合多用户BI单查询极强并发一般高适合多用户BI低跑批为主中虚拟数仓Join性能较好有Colocate Join较弱大Join需优化较好优化器成熟极慢尽量规避中实时写入支持Stream Load秒级可见支持Kafka实时写入支持Stream Load主键更新强不支持不支持运维复杂度中组件较少低单机即可跑中FE/BE架构高需要HDFSYARN高需要Hadoop全家桶典型场景企业级BI、数据中台、实时报表日志分析、可观测性、单表聚合企业级BI、实时数仓、统一分析离线大批量计算数据湖联邦查询3.1 一个案例日增10亿行日志查询要求秒级返回讲一个我做过的具体项目某IoT平台每台设备每5秒上报一条状态数据每天新增约10亿行。业务方的需求是按设备类型、地域、时间维度实时查看在线率、故障率、消息量等指标还要支持任意时间范围的历史回溯。一开始团队用Hive做离线计算T1跑批报表刷新一次要等到第二天早上数据出来后再查一次又要几十秒。后来业务方提出要看实时数据Hive这条路直接堵死。当时的选型过程是这样的ClickHouse先入场。它的单表聚合性能确实强按时间范围分组统计10亿行扫描加聚合大概在几百毫秒到一两秒第一批实时报表很快上线。但做了一段时间问题暴露出来了多表Join场景越来越复杂ClickHouse的Join性能和语法限制开始拖后腿另外有十几个运营同事同时在线拖拽报表ClickHouse的并发能力不够查询开始排队。后来把核心分析负载迁到Doris小事表Join、高并发简单查询、实时写入都明显更稳。ClickHouse保留下来做日志检索和单表深度聚合。这算是一个比较典型的混合架构没有哪一款引擎是万能的组合使用往往能覆盖更多场景。3.2 引擎选型决策树如果你正在选型可以直接按下面这个路径过滤如果只有几台机器不想维护复杂组件业务以单表聚合、日志分析为主选ClickHouse。如果要做企业级数仓建设有较多维度建模、多表Join、实时离线混合场景面向几十上百个BI用户提供报表选Doris或StarRocks。如果已经有完整的大数据生态HDFS、Spark主要是离线批量计算跑T1报表可以继续用Hive但建议在上面加一层Presto/Trino提升交互式查询速度。如果已经在用数据湖Iceberg/Hudi想直接查湖里的数据Presto/Trino是更合理的选择它不是一个存储引擎而是联邦查询层。从我踩过的坑来看很多团队选型失败不是因为引擎不够好而是选错了参照系。网红引擎热度再高不适合你的数据规模和查询模式就是白搭。选型前最好把业务方最核心的20条查询样例收集起来用真实数据和真实查询做一轮压测再决定。不要凭感觉拍板也不要在PPT里做决定。4. 从业务问题到OLAP模型——模型设计的核心步骤与坑引擎选好了接下来是硬仗中的硬仗模型设计。同一个数据场景模型设计得好不好查询性能可以差几十倍。这一节我以一个典型的电商订单分析场景为例带你完整走一遍模型设计的核心步骤并点出每一步容易踩的坑。4.1 需求调研先对齐口径再画模型在设计模型之前最重要的不是画表结构而是跟业务方对齐口径。我见过不少工程团队上来就根据业务系统的表结构开始建模结果模型建好业务方一看指标数值跟运营手里的Excel对不上直接推倒重来。对齐口径核心要问清楚三件事度量定义销售额是含税还是不含税算不算退款订单是按下单时间还是支付时间统计同一个指标定义不同结果完全不同。维度层级地区维度是省、市、区县三级都要有还是只需要省级商品类目分几级时间口径自然日还是工作日自然周从周一开始还是周日开始时间粒度最小到天还是小时实践中我习惯把这些问题整理成一份指标口径文档让业务负责人签字确认。这个过程看似耗时实际上能避免后面大量返工。口径没有对齐建模、ETL、可视化做得再好都是废的。4.2 事实表与维度表的拆分原则口径确认后开始设计表结构。我强烈建议按星型模型来组织事实表放度量维度表放属性。以电商订单为例我的设计思路是这样的事实表orders_fact每行对应一个订单项级别记录字段说明order_id订单IDuser_key用户维度外键product_key商品维度外键store_key门店维度外键date_key日期维度外键quantity数量amount金额cost成本order_status订单状态维度表分别是date_dim、user_dim、product_dim、store_dim每张维度表存描述性属性。比如product_dim会有product_id、product_name、category_l1、category_l2、brand等字段。这个结构的好处是显而易见的业务属性的变化通过修改维度表就能完成不需要改动事实表。比如商品从A类目调整到B类目只需要更新product_dim里的类目字段所有历史分析都会按新类目重新汇总。有些基础不太好的同学喜欢把维度属性直接冗余在事实表里比如在orders_fact里直接放category和brand字段。这在极少数查询场景下确实能省掉Join但代价是事实表膨胀明显更新维度属性时要重刷历史数据。除非你非常清楚自己在做什么否则不建议这么做。4.3 ETL加工中的常见坑时区、缓慢变化维、粒度漂移模型设计完成后ETL加工是另一个事故高发区。分享三个几乎每个团队都会踩的坑时区坑是最隐蔽的。业务库存的时间是北京时间你的数仓服务器时区如果是UTCETL清洗时如果直接按服务器时间截取日期数据就会整体偏移8小时。我的习惯是在ETL的最上层统一约定所有时间字段使用中国时区并在字段命名里显式标注time_zone防止后人接手时误用。缓慢变化维是维度表更新的经典问题。用户修改了手机号订单历史分析里到底该显示修改前的手机号还是修改后的手机号常见做法是采用拉链表记录每个版本的生效时间需要历史回溯时按日期匹配对应版本。如果不需要精细历史回溯可以直接采用覆盖更新但代价是历史分析口径会随着维度更新而变化这一点必须在口径文档里跟业务方说清楚。粒度漂移是指两张事实表在汇总时因为口径不一致导致重复计算。比如订单表和订单退款表关联如果一张订单有多笔退款left join就会产生重复行汇总金额直接翻倍。处理这种问题的通用手段是先按订单粒度把度量聚合好保证一行一单再和订单表关联或者提前在字段设计阶段就把退款金额冗余进入订单事实表避免事后Join带来的重复。这几个坑没有任何一个引擎能帮你自动规避全是建模和ETL阶段的设计责任。做数仓的人常说的数据质量是靠设计保障的不是靠查错保障的就是这个意思。5. 查询性能优化的三板斧——分区裁剪、预聚合与Join消除模型设计得好只能说地基打牢了。上线一段时间后随着数据量增长和查询模式复杂化性能问题一定会出现。这里分享我在OLAP日常调优中最常用的三板斧基本能解决80%以上的性能问题。5.1 先看执行计划还是先看表结构遇到慢查询时很多新人第一反应是去优化SQL写法。老手的第一反应是先看执行计划再看表结构设计。因为OLAP场景下查询慢的根本原因往往在存储层和计划层SQL写法反而在其次。以Doris和StarRocks为例EXPLAIN命令会展示查询的执行计划你要重点看三个信息扫描的分区数量理想状态下一个按天分区的表查一天的数据应该只扫描一个分区。聚合下推情况聚合操作是下推到存储层完成还是把所有数据拉上来再聚合。Join的执行方式用的是Broadcast Join还是Shuffle Join数据量较大的表是否正确选择Colocate Join。我见过一个典型问题一张按月分区的表实际查询按天过滤但因为过滤条件写的是dt 2024-01-01而不是dt 2024-01-01分区裁剪失效引擎把整个1月份的数据全扫了一遍。执行计划一眼就能看出问题但通过SQL检查半天也不一定发现问题因为SQL语法完全没问题。5.2 分区、分桶与排序键摸清引擎的脾气OLAP引擎的存储结构直接决定了查询性能这个一定要花时间摸清。分区是粗粒度的物理隔离。按照日期分区是最常见的做法查询时会通过分区裁剪跳过不需要的分区。分区粒度要跟查询粒度匹配经常查7天数据就按天分区经常查月度数据按月分区省得管理太多分区目录。分桶是更细粒度的切分它影响数据分布和Join效率。分桶字段的选择很重要一定要选查询中高频使用的等值过滤字段。比如订单表经常按user_id查询就按user_id分桶经常按store_id关联门店表就按store_id分桶。排序键决定了数据在文件内部的排列顺序。列式存储中排序键跟查询过滤条件的匹配程度决定了扫描时能跳过多少数据块。比如一张订单表最频繁的查询条件是时间和用户ID那排序键顺序可以设为(dt, user_id)。注意排序键的字段顺序有讲究最常作为过滤条件的字段放在最前面后面字段的过滤效果会逐级下降。这块没有银弹最靠谱的方法是用真实数据做实验构造一个覆盖典型查询的基准集分别测试不同分区/分桶/排序键方案下的查询耗时用数据说话。5.3 物化视图和预聚合把查询费用前置任何OLAP引擎面对海量数据频繁查询最终都会回到相同的问题如果结果集变化不频繁为什么不在写入阶段就先算好物化视图和预聚合表就是干这个事的。典型的落地场景一张订单事实表有几亿行业务方每天要看按省份按小时的销售汇总。每次实时聚合都不是不能跑但并发一高就会卡顿。建一张小时级聚合表CREATE TABLE order_hourly_agg ( dt DATE, hour INT, province_id INT, category_id INT, order_cnt BIGINT, sales_amount DECIMAL(18, 2) ) ENGINE SumMergeTree PARTITION BY dt ORDER BY (dt, hour, province_id, category_id);然后用定时任务或物化视图机制每整点把上一小时的数据预聚合写入。查询省份小时报表时直接查这张预聚合表数据量从亿级降到几十万行再重的并发也能扛住。要注意的是预聚合不是万能的。维度组合一变预聚合表就失效。我的做法是只对最高频、最核心的查询口径做预聚合长尾查询继续走明细表。过度预聚合会让存储膨胀ETL调度复杂化维护成本大大增加。6. 让OLAP真正驱动决策——从数据到行动的关键链路技术层面跑通后OLAP项目的最终评判标准只有一条业务方是否基于这些数据做出了更好的决策。如果报表做出来没人看或者看的人不信任数据那整个项目就是建了一座漂亮的空中楼阁。这一节聊技术之外同样重要的事情。6.1 指标口径统一数据驱动决策的第一道坎我常跟团队说一句话技术解决了数据能不能算出来的问题口径统一解决了数据算出来是否可信的问题。数据驱动决策的基础是管理层看到的每一个指标跟业务部门自己看到的指标含义完全一致。举一个非常常见的场景——用户数。市场部定义用户数可能是注册用户总数运营部可能是当月有登录行为的用户数产品部可能是当月有支付行为的用户数。同一个名词三个数字开会时各说各的数据再多也驱动不了决策反而是扯皮的素材。解决这个问题需要用指标管理的方法把核心指标标准化。我的落地经验是建立一张指标字典对每个核心指标明确五要素指标名称、业务定义、口径描述、计算公式、来源表。这张表放到数据平台上作为一个独立模块公示。业务方查数时必须从这个字典里选指标不允许各自临时定义。OLAP模型和指标字典是契合的维度、度量在设计阶段就是按统一口径建的一旦模型通过评审指标字典就自动跟模型绑定天然实现一处定义处处引用。这也是为什么我强调建模前必须先对齐口径。6.2 从OLAP到可视化层大屏和BI系统的技术选型有了OLAP引擎还需要一个可视化层把数据变成人看得懂的东西。市面上常见的方案有这么几类BI工具类如帆软FineBI、Tableau、Power BI、Superset。它们与OLAP引擎通过JDBC/ODBC连接支持拖拽式报表开发。适合业务团队自助取数和报表开发。优势是开发效率高劣势是重度复杂报表需要专门培训且大并发访问时需要做好缓存策略。开源可视化组件库如ECharts、AntV。这种方案适合你所在团队有自己的前端开发资源需要高度定制化界面。我之前做的几个数据大屏就是用ECharts加Vue写的数据接口直接查OLAP引擎秒级刷新。嵌入式分析平台则是把OLAP能力封装进业务系统。比如在供应链管理平台里嵌入库存分析页面用户一边看库存一边看周转率趋势不需要跳转到独立BI系统。这种项目技术上是OLAP引擎统一权限体系可视化组件的组合。从实际落地看一个中型公司最合理的组合常常是BI工具覆盖日常报表开源可视化组件定制管理驾驶舱和大屏两者共用一个OLAP引擎。注意避免每个部门各搞一套报表工具时间长了数据口径不一致的问题又会复发。6.3 如何让业务团队真正用起来最后说一个很多人忽视的问题系统上线了业务方不用。数据系统最怕的不是性能问题而是沦为摆设。很多团队花大精力搭好平台结果运营人员还是习惯去Excel里处理数据报表系统访问量惨淡。复盘一下原因不外乎这几个查询太慢、数据不全、口径不透明、UI不友好。让业务团队愿意用我有三个实操心得第一个心得是从最高频、最痛的一个场景切入。不要一开始就追求大而全的指标体系先选业务方天天要看的那三五个核心指标做好做透让业务方第一次用就感觉比原来更快、更准。把口碑建立起来后续推广就顺了。第二个心得是给业务方开自助分析窗口。好的OLAP平台不能只会出固定报表还要支持业务方按自己的思路拖拽维度、筛选条件、下钻到明细。很多业务分析的灵感是在数据探索中产生的而不是在固定报表里看到的。第三个心得是用数据质量反馈闭环反向推动建设。每个指标旁边加一个数据反馈入口业务方发现数据异常可以一键提交工单数据团队通过工单快速定位是ETL调度问题、数据延迟问题还是口径理解问题。这个机制看上去简单但对提升数据可信度帮助很大业务方感觉自己参与建设而不是被动接受IT交付的东西。最后再分享一个小技巧全篇聊了OLAP的核心概念、引擎选型、模型设计、性能调优和业务落地最后再分享一个我个人的实操习惯。在给OLAP引擎做性能基准测试时不要只看平均查询耗时。你要同时记录P50、P90、P99耗时。很多OLAP引擎的查询耗时分布很微妙平均耗时才几百毫秒但P99已经到了10秒这意味着总有少量查询卡顿得让业务方无法接受。按P99做优化能更准确地定位问题是出在少数极端大查询上还是整体性能都不行。另外生产环境的OLAP日例巡检我只看三个指标查询排队数、扫描行数分布、写入延迟。查询排队数上涨说明并发容量不够扫描行数异常说明分区裁剪失效写入延迟上涨说明ETL链路出现瓶颈。这三个指标能覆盖日常运维中大部分的性能问题比看一堆花花绿绿的监控大屏管用得多。
返回列表