想象你是一家连锁餐厅的中央厨房负责人。每天清晨,成箱的食材从不同供应商那里送达——有凌晨刚卸船的活鱼,也有冷鲜车运来的蔬菜。如果你等所有食材到齐再统一加工,客人至少要等半天;如果你每来一箱食材就直接下锅炒,厨房会乱成一锅粥。聪明的做法是:把厨房分成几个功能明确的区域——收货区先原样接收食材,清洗区负责去泥去根,配菜区按菜单把食材组合成半成品包,出餐区只需简单翻炒就能上桌。实时数仓的分层设计,就是给“数据”搭建这样一条中央厨房流水线。 实时数仓的核心思路是:先用一个统一的流式引擎(Flink)处理源源不断到达的数据,再把数据按 ODS、DWD、DWS、ADS 四层组织——分别对应收货区、清洗区、配菜区、出餐区。每一层只做自己的事,层与层之间用消息队列天然衔接。这样,业务方既能看到分钟级延迟的经营指标大屏,又能回溯原始明细排查问题。学完这篇,你就能看懂从数据产生到指标展示的完整链路,知道每一层该放什么、不该放什么,以及维表关联、状态TTL这些工程细节为什么如此重要。
实时数仓分层是流式计算最核心的落地范式。掌握分层设计与Flink工程细节,你就能从“只会写SQL”升级为“能设计实时数据链路”的流式工程师,这是当前数据团队最稀缺的能力之一。
目录
可视化演示
展示系统处理流水线、实时性能指标曲线及多方案对比分析,包含数据流转、吞吐延迟监控与方案评估。
本节配有交互动效(进入视口后自动加载,约 3KB)。
系统可视化 处理流程:输入→处理→模型→输出 性能监控:实时指标 P=L×T
流批一体:统一引擎的前世今生
中央厨房出现之前,传统餐厅只有两种备菜方式:一种是“大锅提前炖”,把一天的菜量提前做好放着,客人来了一盛就行,量大但口味不新鲜——这是离线批处理;另一种是“单锅现炒”,客人点了才开火,新鲜但灶台压力巨大——这是实时流处理。传统数仓只能二选一,批处理链路跑离线报表,流处理链路算实时指标,两套系统代码不通,统计口径经常对不上。
Lambda架构干脆用两套系统并行:批处理链路算离线结果,流处理链路算实时结果,最后合并。但维护两套代码意味着同样的业务逻辑要写两遍,就像厨房里备菜区和炒菜区各用各的菜谱,口味容易跑偏。Kappa架构提出,只要流引擎能力足够强,就只保留一条流式链路,离线结果只是流处理结果在一段时间后的重放。
流批一体架构正是Kappa的工程化落地。Flink用一套引擎同时支撑批和流:批处理本质是“有界流”,流处理是“无界流”,两者共享同一个执行引擎、同一套状态管理、同一份代码逻辑。你写一遍ETL逻辑,既能按分钟级延迟跑实时指标,也能按小时级批跑全量回溯,数据口径天然一致。选型时,业务对时效要求高选实时链路,对追溯要求高则重放批链路,这就是“实时与准实时”的灵活切换。
中央厨房里,食材从后门到出餐口要经过收货、清洗、配菜、炒制四站。想客人少等,不能只盯着炒锅,传送带上任何一站的堵塞都会让出餐变慢。流批一体就是“用同一套厨房设备同时做备菜和现炒”,理解了这一点,就能逐层看清每个站房的设计。
- 采集耗时 T_ingest数据从业务系统发出到落入ODS层的传输时间,由消息队列吞吐和分区数决定。
- 清洗耗时 T_cleanDWD层对原始数据进行过滤、去重、格式转换所花费的时间,状态规模和清洗逻辑复杂度决定这段延迟。
- 聚合耗时 T_aggrDWS层在滚动窗口内完成维度聚合所消耗的时间,窗口越小聚合越快。
- 服务耗时 T_serveADS层从Redis或HBase读取指标结果并返回前端的耗时,通常要求控制在百毫秒级。
- 总延迟求和四段延迟线性累加,任何一层成为瓶颈都会拉高整体时效。
ODS层:原样接入的收货区
中央厨房后门是收货区,供应商送货来,收货员过个秤、签个字,整箱整筐推进冷库。不管是土鸡还是洋鸡、这筐菜有没有烂叶,先原样收下再说。ODS层就是数仓的收货后门,任务是“贴源”:把消息队列里的原始数据最快速地承接进来,保存成与源头几乎一致的结构。
ODS(Operational Data Store)是实时数仓第一站,落地存储通常选Kafka topic本身或HDFS上的原始日志。数据带时间戳,按业务主题建立topic分区。实时采集通过Flink CDC或Canal监听业务库binlog,把增删改操作逐条同步过来。这里刻意“保持原样”,是因为清洗规则会随业务变化,保存最原始的形态,将来规则改了还能重新加工——就像食材先冷藏不急着下锅。
ODS层的关键设计决策是:不做过滤、不做聚合、不做格式改造。有人觉得“顺手过滤一下垃圾数据”没什么,但在每秒百万条数据的吞吐下,任何额外的计算都会拖慢摄入速度,还会污染原始数据,让后续无法回溯。数据新鲜度是衡量ODS层健康度的核心指标。
收货区只做“过秤入库”,不挑菜不切菜,看似简单,却是整条流水线的源头。如果收货区开始挑三拣四,必然拖慢收货速度。ODS层守住了“原样摄入”的边界,后续DWD层才能从容地清洗切配。
- 事件时间 T_event业务实际发生那一瞬间的时间戳,比如用户下单的14:23:07,而不是数据到数仓的14:24:30。
- 当前时间 T_now数据到达ODS层、被消费处理时的系统时间。
- 新鲜度差值两者相减,差值越小说明数据流转越快,实时数仓越“新鲜”。
DWD层:清洗明细的切配间
洗菜阿姨把食材从冷库取出来,抖掉泥土、摘掉烂叶、削皮去核,再按标准切成肉丝、葱段、姜片。DWD层就是这间切配间,做的是“减法”和“整理”,但不改变食材的本质形态——每一条数据依然是一条明细记录。
DWD(Data Warehouse Detail)是明细层,上游ODS的数据带着各种噪声:重复订单、字段缺失、时间格式混乱。DWD层通过Flink SQL或DataStream API完成过滤空值、去重、schema标准化,再使用维表补上品类名称、城市区域等维度属性。加工完成后,数据粒度与源头保持一致,一行就是一个业务事实。
去重是最关键的算子。实时场景下,订单创建、支付、退款可能有多个渠道重复上报,需要用业务主键(如order_id)在Flink状态里做去重。状态中保存已见过的key,新记录主键已存在则直接丢弃。这正是“洗菜时看到烂叶子就摘掉”。同时,明细数据要保留足够长的时间,供维表回溯和问题排查,不可轻易清理。
切配间出来的肉丝、葱段,形态变了,但每一条都看得见摸得着。这就是明细层的原则:不清不行,但绝不丢弃细节。
- 原始数据 E_rawODS层接入的未经加工的原始事件流,可能含有重复与噪声。
- 去重算子 dedup以业务主键为维度,过滤重复事件的转换操作,seen集合记录历史key。
- 清洗结果 D_clean去重后的干净明细,仍然保留原始粒度,一行对应一个事实。
DWS层:多维汇总的配菜区
配菜师傅根据午市菜单,把肉丝、木耳、胡萝卜丝、调味包组合成“鱼香肉丝半成品包”,一份份装好。客人点单后,灶台师傅把半成品包倒进锅里炒两分钟就能上桌。DWS层就是这间配菜区。
DWS(Data Warehouse Summary)是汇总层,把DWD的明细数据按业务维度——店铺、城市、商品——做实时预聚合。最典型的做法是用Flink的滚动窗口或滑动窗口按分钟、小时粒度计算GMV、订单量、UV等指标,结果写入宽表。宽表预先算好,下游应用来查时直接拿结果,不用现场算,这正是“半成品包”的意义。
设计DWS层要选好两个参数:维度组合和时间粒度。维度组合决定宽表的字段结构,粒度决定每行数据代表的时间窗口。粒度太小(秒级)状态压力大,查询频繁;太粗(天级)失去实时意义。工程上常用“分钟级明细汇总+小时级预聚合”双层结构,既保证时效,又控制存储成本。维度的选择要对着业务方的高频查询来设计,砍掉用不上的维度组合,避免维度爆炸。
半成品包让灶台师傅出餐快,但配菜区要提前研究菜单排期。DWS层的宽表让下游查询快,但设计宽表结构时就要盘算好业务方到底会查哪些维度——这既是架构活,也是业务活。
- 时间窗口 Δ聚合的时间范围,比如一个完整的滚动窗口60秒。
- 指标值 x_t窗口内第t条事件的业务数值,如订单金额、点击次数。
- 累加求和对窗口内所有数值求和,得到该维度下的汇总指标。
ADS层:面向出餐的服务台
前台出餐台贴着今日菜单:糖醋排骨、麻婆豆腐、例汤。客人直接报菜名,服务员从保温柜端出成品,两分钟就能上桌。ADS层就是这张菜单和保温柜——面向业务方直接提供已经算好的最终指标。
ADS(Application Data Store)是应用数据层,接收DWS的聚合结果,再按业务需求加工成报表、大屏、推送所需的最终形态,存储到HBase、Redis、Doris或ClickHouse。业务方不再面对原始明细或大宽表,而是像点菜一样直接拿最终指标。报表团队不用再写复杂的聚合SQL,一个简单的读请求就能拿到数据。
ADS层的关键是查询加速。考虑到大屏每秒刷新、报表定时拉取,通常采用“KV存储+缓存”双层架构:高频指标放Redis,历史趋势放HBase。Flink将DWS结果实时写入这些存储,用幂等upsert避免重复计数。查询时先查Redis,命中即返回,未命中再走HBase,把P99延迟控制在百毫秒以内。这里还要考虑数据过期策略——出餐台的菜不能一直放着,分时数据到了就刷新。
保温柜里几十份半成品只是“备好”,出餐台直接端给客人,客人从不会问“这条鱼几点杀的”。ADS层的意义也是如此——业务方只关心结果多快返回,不关心数据清洗了多久。
- 网络耗时 T_network客户端与ADS层存储之间的网络往返时间,通常占大头。
- 缓存耗时 T_cache命中Redis缓存读取结果的时间,一般只有几毫秒。
- 存储耗时 T_store缓存未命中时回源HBase等存储的查询耗时,可能到几十毫秒。
- 延迟求和三层时间累加,优化目标是提高缓存命中率,把T_cache为主的路径作为常态。
Flink流式ETL:传送带上的加工
中央厨房最核心的资产不是某个灶台,而是贯穿收货区、洗切间、配菜区的传送带。食材不用人工搬运,沿着传送带依次经过每个工位自动完成加工。Flink就是这条传送带,是流批一体引擎的承重墙。
实时数仓的ETL逻辑全部跑在Flink上,它通过窗口算子按时间切分数据流做聚合,通过状态存储记录中间结果,通过事件时间和水位线处理乱序数据,通过检查点机制保障故障恢复。这套能力让“实时ETL”从理论变成工程实践,而不是一遍遍写shell脚本定时调度。
时间语义上,Flink支持事件时间(业务发生时间)、摄取时间、处理时间,实时数仓优先用事件时间+watermark应对迟到数据。窗口类型上,滚动窗口(Tumble)做固定周期统计,滑动窗口(Hop)做重叠周期统计,会话窗口按活跃间隔切分。状态管理上,Keyed State按key隔离状态,配合RocksDB状态后端支撑超大状态集。检查点周期性做快照,故障时从最近一次检查点恢复,如同传送带断带后从断点重新接上,而不用从头再来。
传送带不会因为某个工位太慢而停下来等。Flink的窗口到了触发时间就算结果,迟到数据走侧输出流兜底,就像洗切间没切完的食材放进下一轮备菜,绝不会堵住后面的工位。
- 事件数量 n窗口内到达的数据条数,由窗口类型和触发策略决定。
- 处理函数 f对每条事件的转换逻辑,如提取金额、过滤无效值、拆分字段。
- 窗口聚合和将转换后的各结果求和,得到窗口维度下的最终指标。
维表关联:实时查菜谱的智慧
配菜师切好肉丝,贴上一张标签:“鱼香肉丝——肉丝200g、木耳50g、胡萝卜100g、豆瓣酱1勺”。这张标签就是菜谱,切菜时翻对应页就是维表关联。实时数仓中,明细数据从Kafka过来,维度属性从MySQL或Redis来,两者按key拼接成更丰富的上下文。
维表关联在实时数仓几乎无处不在:订单流需要店铺名称、商品类目、用户城市。批处理里直接JOIN数据库表即可,但高吞吐流式场景下,每来一条事件去查MySQL会把数据库打爆。解决办法是:把维表缓存到Flink本地状态或Redis,按key取维字段;高延迟维表用异步IO发起并行请求;更新不频繁的小维表做成广播流,每个并行算子本地全量持有。
四种实现各有适用场景:维表缓存(lookup join with cache)适合维表数据量中等、更新不频繁的场景;异步IO适合每个key都要求强制查源、且源能抗住并发压力的场景;广播状态适合几万条以内的小维表,但维表变更需重启任务。双流Join则是把两条流在窗口内按key连接,适合订单流关联支付流这种“两边都是流”的情况。选择哪种,要从维表大小、更新频率、查询并发三个维度权衡。
配菜师翻菜谱不会通读全本,而是直接翻到对应页——这就像维表缓存按key命中。特殊菜品要打电话问总厨,就像异步IO查外部维表,次数不多但必须精准。
- 事实流 LDWD层的明细事件流,如订单数据,每条记录携带关联键。
- 维表 D存储维度属性的外部表,如店铺信息、用户画像,以key为索引。
- 连接条件两边按照共同业务键进行匹配,key相等则拼接维度属性。
状态TTL:给数据贴上保质期
冷库里的每箱食材贴着保质期标签,临期先出库,过期直接扔掉。过期食材留在库里只会占空间、增加翻找成本。Flink的状态管理也一样,每个key的状态不能无限累积,必须设TTL——相当于数据的保质期。
Flink的状态(聚合中间值、去重key)保存在堆内存或RocksDB中,状态无限增长会导致内存溢出或磁盘膨胀。TTL机制给状态附加“存活时间”,状态超过设定时间未更新即过期,后续访问自动删除。这在实时数仓里是控制资源的命门。
TTL不能拍脑袋设定。设太短,窗口还没算完状态就没了,结果出错;设太长,状态堆积浪费资源。经验公式是:TTL ≥ 最大乱序时间 + 窗口长度。比如流量统计用1小时滚动窗口,业务数据最大乱序10分钟,TTL应设为1小时10分以上。工程上还可以用onTimer清理配合延迟删除加速状态回收,定期对过期key做scan-and-rewrite主动清理。
中央厨房每周盘点冷库,把临期食材移到外层优先消耗。实时数仓团队也要周期性审视状态大小、命中率,动态调整TTL——保质期既是底线,也是成本线,定得太宽松就是在给集群喂过期食材。
- 最大乱序延迟 T_max_delay业务事件到达Flink的最大时间偏差,即最晚与最早到达时间差。
- 窗口长度 T_window当前计算所用窗口的时间跨度,如滚动窗口60秒。
- TTL下限两者之和确保窗口触发后状态内数据仍有效,不会被提前清掉。
AI 应用场景
场景一:电商大屏实时GMV监控
阶段:实时数仓ETL与指标服务
解决的问题:传统T+1报表当天看不到实时业绩,运营决策滞后。实时数仓将订单流从Kafka接入,经过ODS→DWD→DWS→ADS四层流水线,分钟级输出GMV、订单量、客单价等指标,推送到大屏供管理层实时决策。
具体案例:某电商平台促销期间,订单峰值每秒2万笔。团队用Flink消费Kafka订单流,ODS原样接入,DWD层按order_id去重并过滤测试订单,DWS层按城市+类目维度滚动窗口聚合,ADS层写入Redis,大屏每秒刷新。促销开始后3分钟,业务方看到GMV曲线飙升,及时调整了优惠券发放策略。
场景二:金融风控实时特征管道
阶段:实时特征工程与维表关联
解决的问题:风控模型需要用户历史交易特征(近5分钟交易次数、金额),传统批处理无法满足毫秒级评估要求。实时数仓通过维表关联把用户画像拼接到交易流,再用DWS层实时滑动窗口计算特征,供风控系统在线调用。
具体案例:某支付平台对每笔交易实时评估风险。交易流进入DWD层清洗后,通过异步IO关联用户维表获取风险等级;DWS层用5分钟滑动窗口统计该用户交易频次;特征写入Redis,风控服务以<50ms延迟读取并决策,成功拦截大量盗刷交易。
场景三:物流轨迹实时追踪
阶段:流式ETL与状态管理
解决的问题:快递轨迹数据乱序严重、重复上报,传统方案难以可靠聚合。实时数仓用Flink事件时间+水位线处理迟到轨迹,状态TTL控制长时间未更新的包裹状态,保证追踪准确。
具体案例:某快递公司每天处理3亿条轨迹。Flink按运单号keyBy,DWD层去重同一运单的重复轨迹,DWS层统计每个节点进站包裹数,ADS层输出“中转场实时库存”看板。状态TTL设为72小时,超过3天的运单状态自动清理,集群内存保持稳定。
Python 演示
import timeimport randomfrom collections import defaultdict class RealTimeKitchen: """实时数仓分层流水线 模拟中央厨房从收货到出餐""" def __init__(self): self.ods_list = [] # ODS层 原样接收 self.dwd_list = [] # DWD层 清洗后明细 self.dws_map = defaultdict(float) # DWS聚合结果 self.ads_list = [] # ADS出餐结果 self.seen_keys = set() # 去重key集合 def ods_ingest(self, record): """ODS原样接入 不做加工""" self.ods_list.append(record) return record def dwd_clean(self, record): """DWD清洗 去空值加去重 输出干净明细""" if record.get('value') is None: return None dedup_key = (record['shop_id'], record['ts']) if dedup_key in self.seen_keys: return None self.seen_keys.add(dedup_key) row = { 'shop_id': record['shop_id'], 'gmv': round(float(record['value']), 2), 'ts': record['ts'], } self.dwd_list.append(row) return row def dws_aggregate(self, row): """DWS按店铺维度实时累加GMV""" self.dws_map[row['shop_id']] += row['gmv'] def ads_serve(self): """ADS输出当前GMV TOP3店铺""" rank = sorted(self.dws_map.items(), key=lambda x: x[1], reverse=True) self.ads_list = [{'shop': k, 'gmv': round(v, 2)} for k, v in rank[:3]] return self.ads_list def cleanup_expired_state(self, batch_id): """状态TTL 每50批模拟触发一次状态清理""" if batch_id % 50 == 0 and self.dws_map: self.dws_map.clear() print(f" TTL触发 第{batch_id}批状态已清理") def fake_record(): """模拟一笔实时订单事件""" return { 'shop_id': random.randint(1, 100), 'value': max(random.gauss(100, 20), 0.01), 'ts': int(time.time()), } def main(): kitchen = RealTimeKitchen() for i in range(200): rec = fake_record() kitchen.ods_ingest(rec) row = kitchen.dwd_clean(rec) if row is not None: kitchen.dws_aggregate(row) if i % 20 == 0: top = kitchen.ads_serve() kitchen.cleanup_expired_state(i) print(f"第{i:3d}批 ODS={len(kitchen.ods_list):3d} " f"DWD={len(kitchen.dwd_list):3d} " f"DWS={len(kitchen.dws_map):3d} TOP={top}") print("实时数仓分层流水线运行完成 数据闭环 OK") if __name__ == '__main__': main()
self.dws_map = defaultdict(float)DWS层用字典按店铺ID累加GMV,缺失key自动初始化为0,不需要手动判断dedup_key = (record['shop_id'], record['ts'])用店铺+时间戳作为业务主键,等价于实战中的order_id去重键if dedup_key in self.seen_keys: return None已见过的数据直接丢弃,这就是DWD层明细去重的核心逻辑self.dws_map[row['shop_id']] += row['gmv']一行代码完成实时累加,对应DWS层维度预聚合rank = sorted(self.dws_map.items(), key=lambda x: x[1], reverse=True)ADS按GMV降序排序,取出TOP3店铺if batch_id % 50 == 0 and self.dws_map:模拟TTL清理,周期性清空聚合状态防止无限增长
常见误区
误区
正解ODS的核心是“贴源”,原样快速摄入是第一职责,清洗是DWD的活。这就像中央厨房收货区只负责过秤进冷库,不会在货车门口就开始剥菜——那会堵住收货口。ODS里做过多清洗会拖慢摄入速度、增加故障面,而且清洗规则一旦错了,原始数据已被污染,无法回溯。保持ODS原样,是给整个数仓留一条后悔路。
为何常见:把“分层设计”误解为“处理步骤流水线”,觉得越早处理越省事,忽略了各层职责边界的设计哲学。
误区
正解DWD明细是数据资产,绝对不能删。DWS宽表只保存预聚合结果,一旦业务方想分析新的维度组合(比如从看城市改成看年龄段),没有明细就无法回溯重算。就像配菜区打包了100份鱼香肉丝半成品,但后厨冷库里依然要留着肉丝、木耳原材料,万一今晚要改做宫保鸡丁呢?
为何常见:只看到宽表覆盖了现有查询需求,忽略了明细层的不可替代性——维度组合是无限的,预聚合只能覆盖已知需求。
误区
正解流式场景每秒几万条事件,每条都查MySQL,数据库立马被打爆。这也是从批处理思维跳进流处理时最大的坑。正确做法是维表缓存到本地状态,或异步批量查询,或用广播状态全量载入小维表。就像厨师炒菜时不会每次放盐都跑去仓库拆一包盐,而是把常用调料罐放在灶台边。
为何常见:把数据库当成无限资源来设计,不了解流处理高吞吐对存储带来的压力模型。
误区
正解状态空间与TTL成正比,设得过大,内存磁盘迅速膨胀,GC压力和恢复时间翻倍上涨。TTL的科学值是“最大乱序时间+窗口长度”,再留少量安全余量。就像冷库食材有保质期,过期食材既占地方又会发臭污染其他食材,状态也会拖垮整个流任务。
为何常见:对“状态=资源”缺乏意识,把正确性建立在无限资源假设上,忽视了状态的边际成本。
误区
正解处理时间统计的是“数据到达数仓的时间”,而非“业务发生时间”。网络延迟、Flink重启、峰值排队都会让处理时间严重偏离事件时间,导致高峰期数据被归错窗口。比如大促期间消费者20:00下单,23:00数据才到,按处理时间统计就成了23:00的单。实时数仓指标必须用事件时间+watermark。
为何常见:事件时间需要额外处理乱序和迟到数据,初学者嫌麻烦,而业务早期对延迟不敏感,等到对不上账时才追悔莫及。
面试题
Q1. 实时数仓中ODS层最主要的职责是什么?
- A.清洗过滤垃圾数据
- B.原样接入保持全量← 正确答案
- C.预聚合维度宽表
- D.面向业务提供服务
答案:B。ODS是贴源层,职责是原样摄入不做加工。A是DWD的职责,C是DWS的职责,D是ADS的职责,均与ODS定位不符。
Q2. DWS层相比DWD层最主要的变化是什么?
- A.数据格式保持原样
- B.数据粒度被聚合汇总← 正确答案
- C.数据量变得更加庞大
- D.数据延迟有所降低
答案:B。DWS做维度预聚合,把明细数据聚合成宽表,数据粒度变粗,数据量通常大幅下降。C说数据量庞大正好相反,D说延迟降低也不对,每层加工都会增加延迟。
Q3. Kappa架构相比Lambda架构最本质的区别是什么?
- A.Kappa处理数据更快
- B.仅用流处理一条链路← 正确答案
- C.Kappa需要更多服务器
- D.Lambda不支持实时计算
答案:B。Kappa只用流式一条链路,而Lambda用批+流双链路。A不是本质区别,C恰好相反,D错误因为Lambda是支持实时计算的,只是还要额外维护批链路。
Q4. 关于Flink时间语义,正确的说法包括?
- A.事件时间是业务发生时间← 正确答案
- B.处理时间是数据到达时间← 正确答案
- C.事件时间无需处理乱序
- D.水位线用于推进事件时间← 正确答案
答案:ABD。事件时间是业务实际发生时刻,处理时间是数据到达Flink的时刻,水位线(watermark)正是用来推进事件时间并处理乱序数据的。C错误,事件时间恰恰需要处理乱序。
Q5. 实时数仓中维表关联最常用的工程手段是?
- A.每条数据直接查数据库
- B.维表缓存到本地缓存← 正确答案
- C.将维表写入Kafka队列
- D.仅使用双流Join操作
答案:B。高吞吐流场景直接查库会打爆数据库,维表缓存到本地是标配。C不是常见做法,D双流Join适合两条流关联,不适合关联外部维表。
Q6. 设置状态TTL时需要考虑的因素包括?
- A.数据最大乱序延迟← 正确答案
- B.窗口计算时间长度← 正确答案
- C.集群并行度与内存← 正确答案
- D.数据源数据库类型
答案:ABC。TTL的经验下限是“最大乱序时间+窗口长度”,同时必须考虑集群资源和并行度,否则状态膨胀会拖垮任务。数据源数据库类型与TTL没有直接关系。
Q7. 实时数仓端到端延迟逐层分布趋势是?
- A.逐层递减趋于零
- B.逐层累积不断升高← 正确答案
- C.各层完全相等
- D.只取决于ADS层
答案:B。每层加工都有计算与传输耗时,端到端延迟逐层累积。ADS层的查询延迟只是总延迟的一部分,不是全部,更不是各层相等或递减。
Q8. Flink中“检查点”机制的主要作用是?
- A.加速窗口聚合计算
- B.故障时恢复任务状态← 正确答案
- C.自动优化SQL计划
- D.清理过期数据状态
答案:B。检查点是容错机制,周期性对状态做快照,故障时从最近检查点恢复。A是优化器的事,C是查询优化,D是TTL的职责,都与检查点无关。
Q9. 流批一体架构的优势包括?
- A.一套代码批流复用← 正确答案
- B.系统运维成本更低← 正确答案
- C.数据口径保持一致← 正确答案
- D.实时延迟完全消除
答案:ABC。流批一体用一套引擎、一份代码同时支持批和流,降低运维成本并保证同一逻辑产出口径一致。但实时延迟无法消除,D是错误选项。
Q10. 关于双流Join与维表关联的选择,正确的是?
- A.双流Join适合事实流关联维表
- B.维表关联适合流与外部表← 正确答案
- C.双流Join无需设置窗口
- D.维表关联没有状态开销
答案:B。维表关联面向“事实流+外部维度表”场景,双流Join面向“两条流”场景,A正好说反。C错误,双流Join需要窗口才能匹配;D错误,维表缓存本身就有状态开销。