专业书籍精读 · Fundamentals of Data Engineering · 第 5 章
Fundamentals of Data Engineering · Ch 5 · Data Generation in Source Systems · Reis & Housley · 2022
你在网上下一单,订单记进了商城的数据库;你点开一个页面,服务器记下一行日志;你的手机每隔几秒报一次位置。这些就是数据的「出生地」。这一章讲的是:数据在哪里出生、以什么样子出生,以及想把它取出来做报表的人,该怎么取才不闯祸。
一个订单从「已下单」变成「已付款」再变成「已发货」,前后改了三次。可每天半夜去数据库里抄一遍的报表,只能看到最后那一次——中间发生过什么,彻底消失了。更糟的是,如果那单被删掉,抄的人根本不知道它存在过。
数据量小、只要每天一张报表,半夜抄一遍就够;需要完整历史或分钟级新鲜度,就去跟流水账。一句诚实的代价:跟流水账更准、更实时,但要多搭一套系统、多一份维护。
数据工程从你管不着的上游开始:先弄懂数据怎么诞生,用不伤源头的方式取,再和源头的主人把规矩说清楚。
想看 OLTP 与 ACID、批式与日志式 CDC 的区别、事件时间与处理时间、消息队列和流的分区与投递语义,以及 Databus、Maxwell、Debezium 的实证? → 切到精读版
你以为数据工程从「采集」开始,其实它从一个你不拥有、也管不着的上游系统开始。本章要建立的认知是:源系统是为业务而建、不是为分析而建的;数据工程师必须懂它怎么产生数据(数据库、API、日志、消息与流),用不伤源头、不丢历史的方式抽取(首推读日志的 CDC),并和源系统的主人把变更规则提前说清。
Part II「生命周期详解」的第一章,对应第 2 章生命周期的第一站「生成」。下接第 6 章存储、第 7 章采集。现实里就是「从业务库、SaaS 接口、埋点日志、Kafka 里把数据拿出来」这件事的上游一半。
数据工程师消费源系统,却不拥有它。作者反复强调:你要像了解自己的系统一样了解源系统——它怎么生成数据、多久生成一次、量多大、会不会突然改结构——否则下游的一切都建在流沙上。不懂源系统的典型后果有三种:
数据先分模拟数据(真实世界里发生的事,如说话、手写)和数字数据(由数字化转换或系统原生产生,如电商下单、传感器读数)。数字化之后,数据工程师面对的源系统主要是五类:
OLTP(在线事务处理)数据库保存应用的当前状态——账户余额、库存、订单,特点是高并发、低延迟、一次读写少量行。它们多数支持 ACID:原子性(事务要么全做要么全不做)、一致性(书中定义为:任何读取都返回该项最近写入的版本)、隔离性(并发事务互不干扰)、持久性(提交后断电也不丢)。对比之下,OLAP(在线分析处理)系统为大范围扫描与聚合而建。直接在 OLTP 上跑分析,既慢又伤业务,这就是为什么要把数据抽出来——或至少走读副本。
源表怎么写入,决定了你能拿到什么:
CDC(change data capture)指从数据库里提取每一次变更(插入、更新、删除)。书里分两种:
批式 CDC:给表加 updated_at 列,每隔一段时间查「上次之后改过的行」。实现简单,但只能看到每行在查询那一刻的最终状态:一行在两次查询之间改了三次,你只拿到第三次;物理删除的行根本不会出现在结果里。
连续 CDC:直接读数据库的预写日志(PostgreSQL 的 WAL、MySQL 的 binlog),把每一次提交的变更变成一条事件,通常写进 Kafka 这类流平台。每一次中间状态和删除都被捕获,延迟可到秒级以下;读日志也不像大查询那样扫表。它和数据库复制同源——副本本来就是靠回放日志跟上主库的。
CDC 也不是免费的:读日志仍占用源库的 CPU、磁盘与网络。实践中还有两件事要管——以 PostgreSQL 为例,长期不消费的复制槽会让日志堆积、占满磁盘;而 CDC 只给你「变化」,首次接入要先做一次全量快照再接上日志。
日志记录系统里发生的事件:谁(用户或系统账号)、做了什么(事件与元数据)、何时(时间戳)。按编码分三种:二进制(如数据库日志,紧凑但需专门解析)、半结构化(如 JSON,机器易读,最常见)、纯文本(控制台输出,最难解析)。还要关心分辨率(记录多细:只记一个主键,还是整行前后的值)和延迟(批量落盘还是实时推送)。数据库日志之所以重要,正因为它是 CDC 和复制的共同底座。
书里区分两者:消息(message)是在两个系统之间传递的一条数据,通常被消费后就从队列里移除;流(stream)是只追加的事件记录日志,事件会保留一段时间,多个消费者可以各自从不同位置读,甚至回放。
流平台(如 Kafka)把事件按主题(topic)归类,主题再切成多个分区(partition)并行处理。事件按分区键(如用户 ID)路由:同一个键始终进同一分区,所以顺序只在单个分区内有保证。键选得不好——比如某个大客户的事件占了一半——就会出现热点(hotspotting),一个分区被压垮、其余闲着。
投递语义也要心里有数:至少一次(at-least-once)可能重复,下游要做成幂等;恰好一次(exactly-once)可以做到,但依赖平台与下游的配合,代价是吞吐与复杂度。
这就引出本章的三种时间:事件时间(事情在源头发生的时刻)、采集时间(事件进入采集系统的时刻)、处理时间(被管道处理的时刻)。三者之间的差就是延迟。作者的建议很朴素:每个时间都打上时间戳,并且在设计时就按事件时间算业务指标,否则一个离线几小时的手机会把数据算进错误的那一天。
关系型数据库之外,书里点名了几类 NoSQL:键值库(按键极快读写,常作缓存与会话)、文档库(存 JSON 式嵌套文档,结构灵活)、宽列库(如 Cassandra,海量写入)、图数据库(存节点与关系)、搜索引擎(全文检索)、时序库(按时间顺序的指标)。对数据工程师,关键问题始终是:怎么把数据抽出来、抽取会不会伤到它、有没有类似 CDC 的变更流。
API 方面:REST 最普遍,按资源取数,常要处理分页和限流;GraphQL 让调用方一次请求里精确声明要哪些字段;Webhook 是「反向 API」,事件发生时源系统主动推给你,你得保证接收端不丢;gRPC 用二进制高效传输,适合内部服务间调用。此外还有数据共享(云数仓之间直接授权读取,不用拷贝)和第三方数据(买来的外部数据,质量与更新节奏要额外核实)。
本章最务实的一段是与源系统的利益相关方协作。数据工程师要和系统 owner 约好:可用性和数据新鲜度的 SLA/SLO、schema 变更提前多久通知、出了问题找谁。横切的「暗流」同样适用于源系统:安全(凭据放密钥管理器、走 VPN 或私有链路、只给只读权限)、数据管理(谁对数据质量负责)、DataOps(监控源系统的可用性与变化,而不只是监控自己的管道)、编排(抽取节奏要和源系统的产出节奏对齐)。
表 1 · 从数据库抽数:四种方式
| 方式 | 适合 | 代价 |
|---|---|---|
| 全量快照 | 小表、维度表,每天一次即可 | 表一大就慢且压源库;看不到变化过程 |
| 批式 CDC(updated_at) | 中等表、小时级新鲜度、只关心终态 | 漏中间状态、漏物理删除;依赖上游每次都正确维护该列 |
| 连续 CDC(读日志) | 要完整历史、分钟级以内新鲜度、多个下游订阅 | 多一套流平台与连接器;要管快照衔接、日志堆积、schema 演化 |
| 读副本上查询 | 需要复杂查询又不想碰主库 | 副本有复制延迟;仍然只见终态 |
表 2 · 源表的写入模式
| 模式 | 好处 | 代价 |
|---|---|---|
| CRUD(原地更新) | 表小、查当前状态快,应用最自然 | 历史默认丢失,分析侧要靠 CDC 补 |
| 只追加 | 历史天然保留,可审计、可回放 | 表持续膨胀;查当前状态要找最新一条 |
表 3 · 消息队列 vs 事件流平台
| 类型 | 适合 | 代价 |
|---|---|---|
| 消息队列(如 RabbitMQ、SQS) | 系统解耦、任务分发,消费完即删 | 不能回放;一条消息通常只给一类消费者 |
| 事件流平台(如 Kafka、Kinesis) | CDC 管道、多下游订阅、需要回放 | 分区、保留期、消费位点都要运维;有序只在分区内 |
判据:先问「下游要不要中间状态和删除」,要就上日志 CDC;再问「新鲜度要到多少」,天级用快照或批式增量即可;最后问「会不会伤源库」,任何方案都别在业务高峰对主库扫全表。
面试和架构评审里常见的一问是:「订单库的数据怎么同步到数仓和搜索?」及格答案是定时 SELECT ... WHERE updated_at > 上次;好的答案会说出它漏删除、漏中间态、压主库的问题,转向读 binlog / WAL 的 CDC 写入 Kafka,再说清首次全量快照怎么衔接、分区键怎么选以保住同一订单的顺序、下游怎么做幂等。另一个高频陷阱是双写:应用同时写数据库和消息队列,一旦其中一个失败,两边就不一致——日志 CDC 正是为消除双写而生。
大厂与开源实证
① 数据工程从你不拥有的源系统开始:要像懂自己的系统一样懂它的产出方式、频率、量级与怪癖。
② 五类源:文件、API、应用数据库、日志、消息与流;核心是 OLTP 库——高并发、低延迟、ACID,不适合直接跑分析。
③ CRUD 只留当前状态,只追加留住历史但表会膨胀;写入模式决定你能拿到什么。
④ 批式 CDC 只见终态、漏删除;连续 CDC 读 WAL / binlog,拿到每次变更,是消除双写的标准做法。
⑤ CDC 也有成本:首次快照衔接、日志堆积、源库资源与 schema 演化。
⑥ 消息消费即删,流只追加可回放;流按分区并行,有序只在分区内,分区键选错会热点。
⑦ 至少一次要求下游幂等;区分事件时间、采集时间、处理时间,全部打上时间戳。
⑧ 和源系统 owner 约定 SLA、schema 变更通知与联系人,安全上只读、凭据入密钥管理。