做Hive项目,前面辛辛苦苦搭数仓、写ETL、调调度,其实都是在给最后这一步铺路——业务分析。项目做到这个阶段,业务方真正关心的问题才浮出水面:用户到底是谁、哪些人值得运营、活动转化行不行、GMV涨跌背后到底是什么原因。这一篇Hive项目实战,我就围绕“业务分析”这个主题,把我们项目里从分析框架设计到HiveQL落地的完整过程拆开讲,包括指标体系怎么建、口径怎么定、RFM和留存怎么用SQL实现,以及那些跑数时踩过的坑。
这套内容适合两类读者:一类是刚接触Hive,想知道业务分析到底分析什么的同学;另一类是已经会写SQL,但面对一堆表不知道怎么组织分析逻辑、不知道怎么给业务方交差的朋友。我尽量少讲理论,多给可以直接拿去用的SQL和思路。
1. 业务分析先想清楚再写SQL:从问题到指标
很多人拿到数仓表之后的第一反应是“先跑个数看看”,这是大忌。Hive里跑数不贵,但跑错方向重来一遍很贵,尤其当业务方等着数据决策的时候。我在项目里总结出的经验是:写SQL之前,先花一整个下午把分析目标、指标口径、输出形式全部定死,后面才能顺。
1.1 分析目标决定分析维度和深度
业务分析的起点永远是“要回答什么问题”,而不是“有什么数据”。同样是用户分析,“运营要做用户分群发放优惠券”和“产品要判断功能粘性”,分析维度和深度完全不同。前者需要RFM、消费偏好、活跃频次;后者需要留存率、使用时长、核心路径转化。
以我们当时的模拟项目X为例,业务方给了两个模糊的需求:一是“想看看用户质量怎么样”,二是“想搞清楚上周做的那场活动到底有没有用”。这两个问题合在一起,分析目标就明确了:先给用户分层,看大盘结构和质量;再做活动前后对比,评估转化和拉新效果。
这个环节一定要拉上业务方一起过一遍口径和预期结果。比如“新用户”到底按首次支付时间算,还是按首次访问时间算?如果没有业务方确认,后面所有SQL都可能是废的。数据团队最容易犯的错就是埋头写数,写完了才发现业务方问的不是这个数。
1.2 指标体系怎么拆:北极星指标到二级三级指标
目标定好之后,指标就要拆解。我习惯把所有分析需求拆成三层:核心指标、维度指标、诊断指标。
核心指标是业务方最终要看的那个数,比如GMV、DAU、付费转化率;维度指标是按业务维度拆开的核心指标,比如按城市、渠道、品类、新老客拆GMV;诊断指标是解释核心指标为什么会变的中间指标,比如客单价、购买频次、退款率、复购间隔。
举我们项目的例子,核心指标是30天GMV,维度指标按用户分层维度拆成头部、中腰部、尾部用户的GMV贡献,诊断指标则包括各层级的转化率、客单价、购买频次。这样拆完之后,分析框架就很清晰了:每一层分析对应哪些表、哪些SQL、输出什么结果,都是可预期的。
指标拆解还有一个容易被忽略的点:所有指标必须可溯源。每一个计算口径都要落到具体的字段和表上,比如GMV是订单支付金额减去退款金额,还是未退款的支付金额;DAU是活跃设备还是活跃用户。口径不统一,后续分析对不上账,业务方会直接质疑数据的可信度。
1.3 梳理分析周期和输出形式
分析周期也很关键,是按天、按周还是按月,直接决定了SQL里日期条件怎么控制、维度是否要有时间粒度。我们项目里,用户分层是月度任务,因为RFM基于月累计消费行为才有意义;活动分析则是按活动周期拉取,活动前N天和活动后N天对比。
输出形式同样要提前考虑。业务方要的是Excel报表,还是要看数据看板,还是只要一封带结论的邮件?不同输出形式决定了结果表怎么设计——宽表、长表还是报表样式。我们当时为了满足运营自助取数,把分析结果固化成了几张结果宽表,后面接可视化工具时省了大量时间。
2. 核心分析模型落地:RFM、留存、漏斗
分析框架定了,具体模型就要落地。这一节讲我们项目里真正跑到生产环境的三个经典分析:RFM用户价值分层、留存分析和漏斗转化分析。这三个模型覆盖了“看存量”“看粘性”“看路径”三个层面,也是业务分析里最常用的一套组合拳。
2.1 RFM用户价值分层:最实用的用户分群手段
RFM是Recency(最近一次消费时间)、Frequency(消费频次)、Monetary(消费金额)三个维度的缩写。它把用户按最近一次购买、购买次数、购买金额分成不同层级,是运营做精细化运营最常用的用户分群方法。
很多人觉得RFM是个老概念,但在Hive里实现它其实有讲究。最关键的步骤是确定阈值。阈值一般可以用全体用户的平均值或者中位数,更精细的做法是分渠道、分新老客去确定。我们项目里用的是全体用户近90天的平均值作为阈值,原因很简单:业务方需要的是可解释、可复现的规则,平均值口径最好传。
RFM三个维度组合后通常得到8类用户,但实际业务中不需要全部输出。我们只输出四类核心用户:高价值用户(高R高频次高金额)、潜力用户(高R但频次或金额偏低)、预警用户(最近购买距今较远且历史贡献高)、普通用户(各项指标都偏低)。这样运营拿到的用户包清晰、可执行,不会看到一堆无意义的组合。
2.2 留存分析:算清楚用户到底是“借过”还是“留下”
留存分析是判断产品长期价值的核心指标。Hive里计算留存的核心思路是:先定义一个初始动作和回访动作,然后看初始动作发生后第N天、第N周是否发生了回访动作。
项目里我们算的是新增用户次日、7日、30日留存,初始动作是“注册”,回访动作是“登录”。这里有两个容易翻车的细节:一是初始动作发生当天到底是第0天还是第1天,不同团队口径可能不同;二是回访动作必须限定在注册用户本身,不能把整个用户的全部登录行为都算进去,否则留存率会虚高。
SQL实现上,我推荐用日期差函数datediff来算回访日期偏移,然后用条件聚合一次性输出多日留存率,避免一条SQL只算一个留存日期的低效写法。具体SQL下一节展开。
2.3 漏斗转化分析:定位业务路径上的断点
漏斗分析的典型场景是用户从进入到下单的各个环节,比如曝光->点击->加购->下单->支付。Hive做漏斗分析最大的难点在于用户行为日志是事件型的,同一个用户会产生大量事件记录,需要先做“路径重组”,才能算准每一步的人数。
我们项目里处理的方式是:找到每个用户在分析周期内的第一条和最后一条满足条件的动作,然后逐步加条件过滤。比如要算“加购后是否支付”,就先取加购用户作为基础人群,再关联支付事件表,最后看支付人数占加购人数的比例。每一步人数就是漏斗的层级。
漏斗分析的结果最怕一步到位写一个大SQL,中间环节出错了很难排查。我更建议分层拆SQL:第一层出每个环节的总人数,第二层出相邻环节的转化率,第三层再按渠道、设备维度拆分。每层结果都落一张表,这样后面做归因分析也能复用。
2.4 GMV构成与品类交叉分析
除了用户类分析,GMV构成分析也是业务方最常要的东西。我们项目里做的是品类交叉分析:看每个一级品类下的GMV占比、同比环比变化,以及品类之间的连带购买关系。
连带购买关系在Hive里用自关联实现,思路是找出同一个用户同一个订单里同时购买的两个品类组合,统计组合出现的次数。这个数据对运营做捆绑销售、品类规划非常有价值。SQL不算复杂,但要控制组合爆炸的问题——如果品类太多,两两组合会出现大量低价值记录,需要预设一个最小支持度阈值去过滤。
3. 实操过程:HiveQL实现核心业务分析
这一节我把项目里真正跑过的SQL核心片段放出来,顺序按照分析落地流程展开:先说数据准备,再分别展示RFM、留存、漏斗的具体实现,最后讲结果表怎么设计。所有SQL基于Hive 3.x语法,兼容Hive 2.x也没问题,只是部分函数名可能要做微调。
3.1 数据准备:把明细数据汇总成分析基础表
分析前一定要有规范的基础数据表。我们项目里业务分析直接依赖三张表:用户维表(user_profile)、订单事实表(order_fact)、用户行为日志表(behavior_log)。其中用户维表每天更新,订单事实表按支付时间分区,行为日志表按日期分区。
跑分析之前,我习惯先执行一个数据质量检查脚本,确认三个关键事项:订单表里没有重复记录、日期字段没有NULL值、用户维表和订单表的关联字段能对得上。这一步看起来多余,但实际项目里八成以上的分析返工都是因为源头数据有问题,与其分析完了发现问题,不如提前堵住入口。
数据质量检查无误后,先建一个分析基础表,把用户维表和订单表关联后的结果固化下来。这样后续所有RFM和留存SQL都从这张基础表取数,逻辑统一,也方便排查。
-- 创建分析基础表:每个用户每天的下单聚合 CREATE TABLE IF NOT EXISTS analysis_base_daily AS SELECT u.user_id, u.user_reg_date, u.channel_id, u.city_level, o.order_date, COUNT(DISTINCT o.order_id) AS order_cnt, SUM(o.order_amount) AS order_amount, SUM(o.item_cnt) AS item_cnt FROM user_profile u JOIN order_fact o ON u.user_id = o.user_id WHERE o.order_date BETWEEN '2024-01-01' AND '2024-03-31' AND u.is_valid = 1 GROUP BY u.user_id, u.user_reg_date, u.channel_id, u.city_level, o.order_date;这个基础表的设计有几个细节要说明。第一,用了DISTINCT订单ID去重,是因为订单表偶尔会出现同订单多行的情况(部分业务场景下订单行和商品行是一张表),提前去重能防止后续分析翻倍。第二,用户维表的is_valid字段用于过滤测试账号和异常账号,这个逻辑在分析中必须保持一致。第三,时间范围在基础表就限定好,后面每一层分析都不会再动这个条件,保证数出同源。
3.2 RFM用户价值分层SQL实现
RFM的SQL实现分两步走。第一步先算每个用户的三个R/F/M值。R是最近一次消费距离分析截止日期的天数,F是分析周期内的下单次数,M是分析周期内的累计消费金额。这里的分析周期我们取的是近90天。
-- 第一步:计算每个用户的RFM三值 CREATE TABLE IF NOT EXISTS user_rfm_raw AS SELECT user_id, DATEDIFF('2024-03-31', MAX(order_date)) AS recency, SUM(order_cnt) AS frequency, SUM(order_amount) AS monetary FROM analysis_base_daily WHERE order_date >= DATE_SUB('2024-03-31', 89) GROUP BY user_id;这段SQL有三个细节值得注意。第一,为什么在基础表上WHERE只取近90天而不是基础表生成时就限制?因为analysis_base_daily还要给其他分析复用,RFM只是它的一种消费方式,所以在业务SQL中过滤而不污染基础表,这是数仓分层的核心思想。第二,recency用了datediff,以固定的截止日期作为锚点,而不是用当前日期,这样保证了不同天跑出来的结果具有可比性,报表不会因为跑的时间不同而数据漂移。第三,frequency这里直接SUM(order_cnt),因为基础表里已经是每天每人的订单数聚合,这边再GET就只会重复累加天数,逻辑不对。
第二步就是把三值和阈值比对,输出用户分层标签。阈值我们存了一张配置表thresh_rfm,便于运营以后调整,不用改代码。
-- 第二步:用户分层打标签 CREATE TABLE IF NOT EXISTS user_rfm_tag AS SELECT r.user_id, r.recency, r.frequency, r.monetary, CASE WHEN r.recency <= t.recency_thresh AND r.frequency >= t.frequency_thresh AND r.monetary >= t.monetary_thresh THEN 'high_value' WHEN r.recency <= t.recency_thresh AND (r.frequency < t.frequency_thresh OR r.monetary < t.monetary_thresh) THEN 'potential' WHEN r.recency > t.recency_thresh AND (r.frequency >= t.frequency_thresh OR r.monetary >= t.monetary_thresh) THEN 'warning' ELSE 'common' END AS user_level FROM user_rfm_raw r CROSS JOIN thresh_rfm t;实际跑数时,这一步经常因为CROSS JOIN配置表而出现数据膨胀,因为每个用户都会和所有配置项关联。好在这里只有一个配置行,CROSS JOIN没有性能问题;但如果以后想按不同渠道用不同阈值,就得改成JOIN条件是渠道匹配,不能再用CROSS JOIN了,这一点提前留意。
3.3 留存分析SQL实现:多日留存一次算清
留存计算不复杂,但SQL写法决定效率和可维护性。我们的留存口径是“注册后第N天有登录行为”,N分别为1、7、30。实现方式是把注册用户集合和登录行为集合做一个日期偏移匹配,再用条件聚合一次算出来。
-- 注册用户首日集合 CREATE TABLE IF NOT EXISTS reg_user_day AS SELECT user_id, user_reg_date FROM user_profile WHERE user_reg_date BETWEEN '2023-12-01' AND '2024-03-01' AND is_valid = 1; -- 登录行为集合 CREATE TABLE IF NOT EXISTS login_act AS SELECT user_id, login_date FROM behavior_log WHERE behavior_type = 'login' AND login_date BETWEEN '2023-12-01' AND '2024-03-30'; -- 计算留存率 SELECT r.user_reg_date, COUNT(DISTINCT r.user_id) AS reg_cnt, COUNT(DISTINCT CASE WHEN DATEDIFF(l.login_date, r.user_reg_date) = 1 THEN r.user_id END) AS retain_day1, COUNT(DISTINCT CASE WHEN DATEDIFF(l.login_date, r.user_reg_date) = 7 THEN r.user_id END) AS retain_day7, COUNT(DISTINCT CASE WHEN DATEDIFF(l.login_date, r.user_reg_date) = 30 THEN r.user_id END) AS retain_day30 FROM reg_user_day r LEFT JOIN login_act l ON r.user_id = l.user_id AND l.login_date >= r.user_reg_date GROUP BY r.user_reg_date;这段SQL有两个容易踩的坑。第一个是COUNT(DISTINCT CASE WHEN)这种写法,在数据量大时会有性能压力,因为每个用户的去重匹配次数会被放大。如果留存分析是每日都要跑的全量任务,建议改为先按(user_id, offset)聚合去重,再计算留存,性能会好很多。第二个坑是LEFT JOIN时关联条件里已经带了l.login_date >= r.user_reg_date,这个条件只能写在JOIN ON里,不能写在WHERE里,否则会退化成INNER JOIN,丢掉未登录用户,留存率会虚高。
给你看一个我刚才说的优化版写法,在大表场景下推荐这个:
-- 优化版:先JOIN后聚合 CREATE TABLE IF NOT EXISTS retention_mid AS SELECT r.user_id, r.user_reg_date, DATEDIFF(l.login_date, r.user_reg_date) AS day_offset FROM reg_user_day r LEFT JOIN login_act l ON r.user_id = l.user_id AND l.login_date >= r.user_reg_date AND DATEDIFF(l.login_date, r.user_reg_date) IN (1, 7, 30); SELECT user_reg_date, COUNT(DISTINCT user_id) AS reg_cnt, COUNT(DISTINCT CASE WHEN day_offset = 1 THEN user_id END) AS retain_day1, COUNT(DISTINCT CASE WHEN day_offset = 7 THEN user_id END) AS retain_day7, COUNT(DISTINCT CASE WHEN day_offset = 30 THEN user_id END) AS retain_day30 FROM retention_mid GROUP BY user_reg_date;注意JOIN的关联条件里直接限定了day_offset只能是1、7、30,这样中间表的记录数会大幅压缩,后面聚合就快很多。
3.4 漏斗转化SQL实现:路径重组的核心写法
漏斗分析我们做的是“首页曝光->详情页浏览->加购->提交订单->支付成功”这五步。行为日志是事件流,第一步要先把用户在某会话内的路径按时间排好序。
-- 第一步:给行为日志加时间排序 CREATE TABLE IF NOT EXISTS behavior_sorted AS SELECT user_id, behavior_type, event_time, ROW_NUMBER() OVER ( PARTITION BY user_id ORDER BY event_time ) AS rn FROM behavior_log WHERE event_date = '2024-03-15' AND behavior_type IN ('expose', 'detail', 'add_cart', 'submit_order', 'pay_success');这里用ROW_NUMBER()按用户分区做全路径排序,是为了后续能判断事件发生的先后关系。实际生产环境里我会再按session_id分区,但简化场景下按用户也行。
漏斗每一步的计算逻辑是“满足前置条件后再看下一步”。比如要算“浏览详情页后是否加购”,就是先找出所有浏览过详情页的用户,再关联加购事件,并且加购时间要晚于详情页浏览时间。
-- 第二步:计算每层漏斗人数 CREATE TABLE IF NOT EXISTS funnel_step AS SELECT COUNT(DISTINCT CASE WHEN step >= 1 THEN user_id END) AS step_expose, COUNT(DISTINCT CASE WHEN step >= 2 THEN user_id END) AS step_detail, COUNT(DISTINCT CASE WHEN step >= 3 THEN user_id END) AS step_cart, COUNT(DISTINCT CASE WHEN step >= 4 THEN user_id END) AS step_submit, COUNT(DISTINCT CASE WHEN step >= 5 THEN user_id END) AS step_pay FROM ( SELECT user_id, MAX(CASE WHEN behavior_type = 'expose' THEN 1 WHEN behavior_type = 'detail' THEN 2 WHEN behavior_type = 'add_cart' THEN 3 WHEN behavior_type = 'submit_order' THEN 4 WHEN behavior_type = 'pay_success' THEN 5 END) AS step FROM behavior_sorted GROUP BY user_id ) t;注意这段SQL里我给的行为路径编号,MAX()取的是该用户在整个分析周期里达到的最深层级。这样做的前提假设是行为日志完整且事件有序,如果用户先加购后曝光(比如从分享链接直接进入详情页),那这个用户的step会被记为2而不是3,漏斗人数会比真实情况偏少。这个口径问题需要在分析结论里注明,否则业务方会对数据产生误解。
更严谨的做法是只保留每条路径中事件按时间递增的序列,并对跳变情况做修正,但这种写法工程量较大。在项目节奏紧的情况下,用MAX()+事件埋点顺序来近似漏斗,是行业里常见的折中方案。
3.5 结果表设计与存储:分析结果怎么给业务方
分析SQL跑完之后,结果不能散落在临时表里。我们项目里会按分析主题设计固定的结果表,RFM分层结果存入ads层用户分群表,留存和漏斗结果存入ads层运营分析日报表,每张结果表都带上分析周期字段,方便回溯。
结果表的存储格式统一为ORC,开启hive的矢量化查询和谓词下推。业务方如果直接用Hue或JDBC查询这些表,速度会明显比文本格式快。分区方面,我建议按分析日期做单分区,因为每一次跑批都是覆盖当天全量结果,不需要按维度再拆多层分区,过度分区反而会导致小文件过多。
-- 结果表示例结构 CREATE TABLE IF NOT EXISTS ads_user_level_daily ( stat_date STRING COMMENT '统计日期', user_level STRING COMMENT '用户层级', user_cnt BIGINT COMMENT '用户数', gmv_total DECIMAL(16,2) COMMENT 'GMV合计', avg_order_price DECIMAL(16,2) COMMENT '人均客单价' ) COMMENT '用户分群日报' PARTITIONED BY (part_date STRING) STORED AS ORC;这里插一句踩过的坑:结果表字段类型一定要用DECIMAL,不要用DOUBLE。DOUBLE在展示端会出现1.2000000000000002这种浮点误差,业务方看到会直接找人投诉。DECIMAL在Hive里算金额类型是绝对正确的选择。
4. 常见问题与排查技巧实录
业务分析SQL写多了,遇到的坑也自然多。这一节我把项目实战里遇到频率最高的几个问题拎出来,每个问题都配上排查思路和解决方案,希望能帮大家少走弯路。
4.1 数据倾斜:GROUP BY维度导致的红黑不均
做RFM和漏斗时最怕遇到数据倾斜。当我们按user_id做GROUP BY时,如果存在少量超大用户(比如平台头部商家的聚合账号),reduce端会集中在少数几个节点上,任务跑几十分钟不动,日志里看到大量shuffle。
排查方法很简单:先跑一个探查SQL,看每个KEY的数据量分布。
SELECT user_id, COUNT(*) AS cnt FROM analysis_base_daily GROUP BY user_id ORDER BY cnt DESC LIMIT 20;如果头部用户的数据量比尾部用户高出几个数量级,基本就实锤了数据倾斜。解决办法有几种:一是过滤掉明显异常的测试账号;二是给JOIN时的大KEY加随机前缀打散,但这种方法只适合JOIN场景;三是对GROUP BY倾斜可以先做两阶段聚合,即先按user_id+随机后缀分组聚合一次,再按user_id汇总。两阶段聚合是最通用的方案,但SQL写起来偏长,如果倾斜不严重,也可以先接受,靠调整map和reduce内存来缓解。
4.2 去重口径不一致导致数据翻倍
项目里最容易引发争议的问题就是去重。用户表JOIN订单表时,如果用户维表每个用户只有一行,JOIN没问题;但行为日志表可能一个用户对应多条记录,直接JOIN订单表后,订单金额会被放大N倍。
比如在算用户GMV时,用了LEFT JOIN行为表,再SUM(order_amount),结果GMV比实际大了好几倍。排查手段是在写SQL时先验证明细表粒度。你可以先分别COUNT(*)两张表的粒度,再用EXPLAIN看JOIN后行数变化。我常用的技巧是拿一个已知用户的订单金额手工验算,比如某用户实际消费100元,SQL出来是300元,就说明JOIN膨胀了。
针对这个问题,我的习惯是所有事实表先做粒度量确认,在注释里写明每个表的粒度,然后在JOIN之前把维度表的粒度降到最低。比如行为日志先按(user_id, event_date, behavior_type)聚合,再JOIN订单表,膨胀率会大大降低。
4.3 时间维度处理:日期函数坑和时区问题
业务分析里用日期函数是常态,但日期处理有几个暗坑。一个是Hive的DATEDIFF返回的是两个日期之间的天数差,如果日期字段是STRING类型且格式不标准,会直接返回NULL。比如'2024/03/15'这种格式,DATEDIFF就算不出来,必须先用regexp_replace统一成yyyy-MM-dd格式。
另一个是时区问题。如果行为日志的时间戳是UTC,而订单时间用的东八区,直接比较日期会出现差8小时的问题。我们处理方式是数据ETL阶段统一转换成东八区并落成日期字符串,不允许在分析SQL里做临时时区换算,这样所有的报表口径才一致。
还有一点容易被忽略:分析周期跨月、跨年时,DATE_SUB和DATE_ADD的边界条件。比如要取“最近90天”,用DATE_SUB('2024-03-31', 89)还是90,不同人的习惯不一样。我们团队统一口径是“包含起始日,不包含截止日”,所以区间是[date_sub(截止, 89), 截止],亲测这个口径最好解释。
4.4 任务资源消耗与执行时间调优
分析SQL跑得慢,很多时候不是逻辑问题,而是资源分配不合理。我们项目里有一个典型的性能优化场景:留存分析JOIN行为日志表,一张表几千亿行,直接JOIN会占用大量reduce资源。
调优思路有几个方向:一是通过分区裁剪减少扫描量,行为日志表按日期分区,存量分析只用当天的分区而不是扫全表;二是开启小文件合并,把大量小分区的数据先合并成少量大文件,减少MR任务数;三是对核心JOIN的表用MAPJOIN提示,如果小表能加载进内存的话,会极大减少shuffle。
另外一个容易被忽略的调优点是中间结果的压缩。开启中间结果压缩可以减少磁盘IO和网络传输,代价是CPU略有上升。对于以IO瓶颈为主的Hive任务,这个优化效果很明显。建议开启hive.exec.compress.intermediate=true,并设置snappy压缩。
4.5 分析结果回刷与数据修正
业务分析还有一个逃不掉的环节:修正历史结果。比如RFM分层的阈值调整之后,不仅要影响新跑的数据,还要回刷历史分区。我们项目里对这类问题做了一张结果版本表,每次跑批都记录当时的参数版本号,便于追溯和回刷。
回刷时最怕忘记清理旧分区导致数据重复。我推荐使用INSERT OVERWRITE PARTITION,它会自动覆盖指定分区的旧数据,避免数据翻倍。同时回刷前一定要先把影响范围内的分区检查一遍,确认没有下游依赖表在消费这些数据,否则会出现报表短暂波动。
最后分享几条个人经验
我在几个Hive项目里做业务分析,最大的体会是:写好SQL只是项目的下限,把分析逻辑讲清楚才是上限。每次跑完数,我都会写一段简短的结论,把分析结果翻译成业务能听懂的语言——比如“头部用户贡献了70%的GMV,但这部分用户最近活跃度下降,需要重点做召回”,而不是丢给业务方一张写满数字的表。
另外建议大家在项目里把每个分析脚本的注释写完整,包括业务背景、口径说明、依赖表和结果去向。这个习惯一开始很费时间,但项目周期拉到半年以上时,你会发现它能救你的命。过了三个月,你自己都记不清某个SQL为什么要这样写。
最后一个小技巧:分析任务全部排到凌晨执行,但每天上班第一件事不是看报表,而是先看任务日志有没有失败重试,并对比今日指标和昨日指标有没有突变。这能让你在业务方找你之前就发现问题。做业务分析,主动发现数据异常,永远比被动解释数据异常更让人信任。