研究报告 · 约 30,776 字 · 预计推送 4 部分

数据建模入门:范式、反范式、时间序列与事件数据取舍 —— 横纵综合报告 未开始

目录

题记与说明

课题:数据建模入门:范式、反范式、时间序列与事件数据取舍
报告性质:纵轴(历时)与横轴(2026-11-05 截面对比)两份章节报告的综合稿。本文件为最终稿,已按独立审读意见(8 条)逐条修订。
日期:2026-11-05
信源声明:两份章节报告的关键事实均在撰写当日经 WebSearch 与网页抓取工具核验并随文附来源 URL(纵轴章:WebSearch 12 次 + Wikipedia: Apache Cassandra 全文核读;横轴章:WebSearch 16 次 + WebFetch 实抓 6 页 + WebReader 实抓 2 页)。本综合稿合并两章来源并保留全部链接;凡属本稿新增的综合判断而非章节原文,均在文中标注"综合判断"。
综合处理说明(对两章差异与重复的处理记录):
1. 两章使用了两套案例叙事——纵轴的"青苔账本"七时代叙事、横轴的 A/B/C 三个基准场景。本稿将其统一为同一套案例体系:A/B/C 即青苔账本三个功能面在当下时间截面上的切片,映射表见 §2.4。
2. Timescale 公司已更名为 Tiger Data。本稿统一写法:公司名作"Tiger Data(Timescale)",其产品名作"TimescaleDB"。
3. 纵轴阶段三(NoSQL,2005–2012)与阶段五(事件数据,2005–2019)时间区间重叠并非矛盾,而是两条并行演化的支线,正文 §1.3 已明确标注。
4. Kimball《The Data Warehouse Toolkit》1996 年初版(纵轴事件)与 2013 年第 3 版(横轴竞品)是同一方法论的不同版本,不构成冲突。
5. 本稿面向入门读者:正文首现的技术术语(CDC、TTL、写放大、ELT、GIN、向量化执行、hypertable、连续聚合、物化视图等)在附录 C 术语表中给出一句话白话解释;§2.2 图例说明两套评分刻度(✔/△/✘ 与 ★)的定义及换算关系。

TL;DR

数据建模五十六年只由一个变量驱动:查询模式的可预知程度 × 存储/算力成本曲线。范式(1971)与反范式(星型模型,1996)不是对错,是同一问题在不同读写比下的时间差;NoSQL 撕开固定模式后,时序与事件把建模对象从实体扩展到时间线与日志;2020 年代重新融合、单体单库回归。横向对比 15 项(五类)学习资源:没有单一竞品能同时做到四主题覆盖、免费且贴合个人项目尺度;时序与事件两子域,课程类全部缺席、书籍类除 DDIA 2e 外也无人系统覆盖,厂商文档权威但只讲自家。建议:先用归一化的 PostgreSQL 起步,用三问做取舍——查询可预知吗、数据是实体/时间/事件吗、量级多大(判断顺序与量级参考值见附录 A);最贵的错误是用企业数仓的粒度解决一个人的问题(青苔账本 2026 年的完整建表 DDL 与取舍清单见附录 B)。


一、纵轴:发展脉络(1970–2026)

1.1 一条主线

数据建模五十六年的演化,表面上是"范式 vs 反范式"的钟摆之争,实际上由一个真正的变量驱动:读写比例与查询模式的可预知程度,叠加存储/算力的价格曲线。

驱动每一次转向的从来不是数据库厂商的品味,而是"下一个数量级的数据是谁写入的、按什么形状查询"这个朴素问题。

1.2 贯穿案例:「青苔账本」

按课题"个人项目案例驱动"的补充要求,全文以一个个人项目作为主线案例——「青苔账本」:一个自用记账 + 博客流量面板的个人小项目,功能包括:①日常记账(收入/支出/分类/账户);②个人博客的文章与评论;③"我这周花了多少、博客哪篇文章最火"的统计面板;后期又接入电表/体重秤(时序数据)与自动化记账(事件流)。

必须如实声明:这是一个为教学叙事构建的复合示例(composite case),由各时代个人开发者最典型的技术栈与决策场景拼合而成,并非某个真实存在并持续运行的项目;每个阶段的"案例节点"演示的是当年一个入门者会面对的真实取舍。所有工程事实与版本节点均有来源支撑,案例本身不冒充真实经历。它从 1970 年代到 2026 年被七个时代的主流范式各"建模"一遍。

(横轴章的三个基准场景——记账 App、传感器日志、埋点事件流——正是青苔账本三个功能面在 2026 年的切片,映射见 §2.4。)

1.3 七个阶段

阶段一:范式奠基(1970–1985)——"一切为了消除冗余"

关键事件与版本节点

案例节点 1:青苔账本如果活在 1980 年,入门者会这样建模——把"账户""分类""交易"拆成三张表用外键相连,金额冗余字段会被老师用红笔圈出来:省下的每一 KB 都是钱。这时期学的是"如何把事实只存一遍"。

主要参与者:Codd(IBM)、Chen、Stonebraker(伯克利)、Chamberlin 与 Boyce(SEQUEL)。驱动因素:磁盘昂贵、文件系统数据冗余引发的更新异常灾难、以及"数据独立性"的理论追求。

阶段二:反范式合法化(1986–2004)——"为查询而冗余"

关键事件与版本节点

案例节点 2:青苔账本的统计面板要做"本周各类目支出趋势"。归一化的三张表 JOIN 起来慢得难受,入门者第一次学到那句反直觉的口诀:"报表库里允许、甚至应该冗余"——预聚合一张宽表,空间换时间。范式与反范式的分工雏形就此出现:OLTP 归一化保一致,OLAP 反范式保查询。

主要参与者:Inmon、Kimball、Codd(二度)、MySQL/PostgreSQL 社区。驱动因素:BI 分析需求爆发、硬件变便宜使"冗余换查询速度"从奢侈变成划算。

阶段三:NoSQL 冲击(2005–2012)——"模式被撕开的十年"

关键事件与版本节点

案例节点 3:2010 年的青苔账本赶了时髦——迁移 MongoDB,"想加字段就加,再也不写迁移脚本"。三个月后为统计面板聚合账目时才撞上第一课的反面:当查询模式不可预知时,模式自由是 gift;当查询模式很明确时(报表就是明确查询),它变成账单。最终博客与账本回到 PostgreSQL,只留一个缓存用 Redis。入门者由此学到"取舍"这个词的真正含义。

主要参与者:Google、Amazon、Facebook、10gen、antirez、Apache 社区。驱动因素:Web 2.0 数据量指数级增长、单机 RDBMS 扩展成本、可用性优先的互联网业务形态。

阶段四:时间序列成为一等公民(2010–2017)——"当数据的本质是时间"

关键事件与版本节点

案例节点 4:青苔账本接上了电表和体重秤,数据第一次有了"append-only、按时间查询、定期过期"的形状。沿用案例节点 3 回归的 PostgreSQL 存了半年,B 树索引的写放大与"过期数据只能手写 DELETE 逐批清理"的维护负担开始报警;入门者学到时序建模三件套:时间为主键前缀、按标签分 series、downsampling + retention 策略——这是与"实体建模"完全不同的一套肌肉。

主要参与者:Kx、StumbleUpon、SoundCloud、InfluxData、Tiger Data(Timescale)、Grafana Labs。驱动因素:IoT、DevOps 监控与金融行情把"时间戳数据"推到主流,通用数据库的写放大与 TTL 缺陷暴露无遗。

阶段五:事件数据与流(2005–2019,与阶段三、四并行演化)——"当数据的本质是过程"

(注:本阶段与阶段三时间区间重叠,因为"NoSQL 撕开 schema"与"事件建模成形"是同一场 Web 2.0 数据洪流的两个侧面,属于并行支线而非先后继承。)

关键事件与版本节点

案例节点 5:青苔账本想加"转账后自动记账 + 报表实时刷新"。第一版用定时轮询 + 双写,很快遭遇"两边对不上"。重构成事件模型后:记账命令追加事件流,报表是事件的物化视图,CDC 同步两库。入门者学到事件建模的核心三问:事实(Fact)、命令(Command)、读模型(Read Model)各自是什么——这是与实体建模并立的第二套世界观。

主要参与者:Fowler、Greg Young、Kreps 团队、Confluent、Red Hat、Dymitruk。驱动因素:微服务解耦、实时数仓需求、以及"日志即真相"哲学的普及。

阶段六:现代数据栈与湖仓(2015–2022)——"建模对象变成表格式与管道"

关键事件与版本节点

案例节点 6:青苔账本的原始流水一键 Fivetran/CSV 进对象存储,dbt 里写 stage → mart 两层模型,git 版本管理。入门者第一次发现"数据建模"的重心从怎么设计表漂移到怎么设计管道与契约。

主要参与者:Snowflake、Fishtown/dbt Labs、Uber、Netflix、Databricks。驱动因素:对象存储价格崩溃、计算存储分离、ELT 取代 ETL、分析师工程化。

阶段七:融合与回归(2019–2026)——"一个引擎吃下所有,然后重新组合"

关键事件与版本节点

案例节点 7(当下):2026 年的青苔账本大概长这样——一个 PostgreSQL(账本 + JSONB 存原始收据 + TimescaleDB 存传感器时序 + pgvector 存"记账助手"语义检索),一个 DuckDB(本地随手分析 Parquet),一条轻量事件流(CDC 到下游物化视图)。当年"为每种形状养一个数据库"的教条,让位给"先一体、长痛再拆"的工程务实。

主要参与者:PostgreSQL 全球社区、Yandex/ClickHouse Inc.、CWI、InfluxData、37signals。驱动因素:云成本审计、开发者体验优先、单体性能回归(Prime Video 效应)、AI 负载与向量数据的加入。

1.4 纵轴收束:给入门者的五条历史结论

  1. 范式与反范式不是对错,是时间差:归一化押注"查询模式将来才知道",反范式押注"查询模式现在已经知道"。三范式(1971)与星型模型(1996)相隔 25 年,本质是同一问题在不同读写比下的两种答案。
  2. 每个"反范式时刻"都伴随硬件/成本曲线的拐点:磁盘贵 → 归一化;CPU/IO 贵但磁盘便宜 → 物化;对象存储白菜价 → 湖仓宽表。
  3. 建模对象在扩大,从没有替换:实体(1976)→ 维度(1996)→ 文档/宽列(2009)→ 时间线(2013)→ 事件日志(2013)→ 表格式与管道(2019)——今天的入门者需要的是一张"取舍地图",而不是"唯一正解"。
  4. 钟摆的摆锤是复杂度税:NoSQL 解放了 schema,也把一致性责任甩给应用;微服务解放了部署,也甩来了分布式事务。2023 年 Prime Video 与 37signals 的"回归"不是倒退,是对超出问题规模的复杂度说不。
  5. 个人项目的最佳策略与五十六年历史同构:先用一个归一化的 SQL 库开始(1985 的答案仍然正确),等负载的真实形状显现后,再按本史各阶段的工具箱对症物化——这就是"数据建模入门"五个字在 2026 年的全部含义。

二、横轴:横向对比(2026-11-05 截面)

2.1 研究方法与课题画像

方法:把本课题当成一个"产品"做竞品分析。竞品按学习路径而非按产品划分,归为五类:①经典书籍;②付费专栏与在线课程;③官方文档;④免费交互式练习平台;⑤权威短文与 AI 助教。对比维度:主题覆盖度(范式/反范式/时序/事件四子域逐一打分)、成本、入门友好度、实践性、时效性与维护状态、生态与社区、中文可用性、与个人项目的贴合度。事实核验方式:本会话 WebSearch 共 16 次;WebFetch 抓取 SQLBolt、Select Star SQL、MongoDB 数据建模文档、PostgreSQL 教程与 DDL 章节、Fowler Event Sourcing 共 6 页全部成功;WebReader 抓取极客时间《MySQL 实战 45 讲》官方页与 O'Reilly DDIA 第 2 版书目页成功。

课题画像:

属性本课题的设定
受众个人开发者 / 独立项目作者,不是 DBA 或数仓工程师
覆盖范式 + 反范式 + 时序数据 + 事件数据,四主题合一的入门取舍指南
教学方式个人项目案例驱动:用自己要写的 App / 传感器日志 / 埋点来引出建模决策
成本假设趋近于零(免费、自托管、单机可复现)
语言假设中文为主,代码与术语英文

这个画像决定了竞品地图:任何单独一种传统资源都不能同时满足"四主题覆盖 + 案例驱动 + 免费 + 个人项目尺度",这正是本课题的定位空间,也是逐项对比要验证的假设。

2.2 总对比表

评分说明:✔=完整覆盖该主题;△=部分覆盖(如只讲思想不讲取舍,或只覆盖自家产品);✘=基本不覆盖。"贴合"指读者能否把内容直接套进自己的个人项目。其余各列采用高/中/低三档定性评级:高=可直接照做、无额外门槛;中=需要补充材料、翻译或迁移才能用上;低=需要大量改造或基本用不上。时效性按 2026-11-05 截面判断:高=一年内有新版或持续在线更新;中=多年未再版但方法论仍被公认有效;低=内容已明显过时。

本表的主题覆盖刻度(✔/△/✘)与 §2.4 的星级(★~★★★★★)是两套刻度:前者衡量"该子域的知识是否在这份资源里",后者衡量"放进那个场景能不能直接抄"。大致换算:✔ ≈ ★★★★–★★★★★,△ ≈ ★★–★★★,✘ ≈ ★;但覆盖≠好用,语境不匹配照样低星——例如 IBM 短文对范式是 ✔(速查级覆盖),在场景 A 的完整建模决策里只值 ★★★★;DDIA 2e 的时序是 △(散落在存储引擎章节),到场景 B 就无法直接照做。

竞品(类别)范式反范式时序事件成本入门友好实践性时效性中文贴合
DDIA 2nd ed(书)△✔△✔~$70 / O'Reilly 订阅低(中级起)中高(2026-02 新版)英文(有旧版中译)中
Database Design for Mere Mortals 4e(书)✔△✘✘印刷/电子 ~$53+高中中(25 周年版)英文中
Kimball Toolkit 3e(书)✘✔(分析侧)△△~$19–70中高中(2013)英文(有中译)低(个人项目尺度偏大)
《大数据之路》(1/2)(书)✘✔△△人民币纸书价中高中(第 2 部 2025-03)✔低(公司级数仓尺度)
极客时间《MySQL 实战 45 讲》(专栏)△△✘✘¥68(划线 ¥199)中高中(已完结)✔中
Coursera Duke SQL 专项(课程)△△✘✘旁听免费 / 证书付费高高高英文(有字幕)中
DataCamp Premium(订阅课程)△△△✘~$28/月按年付(约 $330/年)高高高英文中
PostgreSQL 官方文档(文档)△△△✘免费中中高(PG18 在线版)英文(社区中译)高
MongoDB Data Modeling 文档(文档)△✔△△免费高高高英文(社区中译)高
InfluxDB 3 / Tiger Data(Timescale)/ ClickHouse 文档(文档)✘△✔△免费中高高英文高(限自家产品)
SQLBolt(交互练习)✘✘✘✘免费极高高高英文低(不教建模)
Select Star SQL(交互练习)✘✘✘✘免费极高高中英文低(不教建模)
freeCodeCamp 关系数据库认证(交互练习)△✘✘✘免费(含认证)高高高(V9 为 2025-12 新版)英文/部分社区中文中
IBM / Fowler 等权威短文(短文)✔(速查)✔(思想)△✔(模式)免费高低中英文中
AI 助教(LLM 对话式学习)(新形态)△△△△免费~订阅极高视提问而定即时✔高(可按项目定制)
本课题✔✔✔✔免费高高(案例驱动)截面交付✔高

2.3 五类竞品逐项评述

① 经典书籍:深度最高,但四主题从不"打包"。

② 付费专栏与在线课程:体验好、覆盖窄。

③ 官方文档:免费、权威、最贴近"抄作业",但视角锁死在自家产品。

④ 免费交互式练习平台:语言层体验极佳,建模层为零。

⑤ 权威短文与 AI 助教:免费速查与新型变量。

2.4 三个基准场景:青苔账本的三个功能面(与纵轴案例的映射)

纵轴的青苔账本活在时间轴上;横轴把它按功能切成三个 2026 年的真实场景做"基准测试",检验每类竞品的实际可用性。映射关系:场景 A = 账本本体(案例节点 1、2 的当下版本);场景 B = 电表/体重秤(案例节点 4 的当下版本);场景 C = 博客与自动记账的事件/埋点(案例节点 5 的当下版本)。

场景 A:个人记账 App(SQLite 或 PostgreSQL)

会碰到的建模决策:交易表要不要把"账户名"冗余进来(反范式)还是只存 account_id(范式)?转账必须双边记账,怎么靠范式化保证一致性?月末报表很慢,要不要建月度汇总宽表?

竞品适配表现
Mere Mortals 4e★★★★★ 范式决策全程对口,正是该书写的内容
IBM 短文★★★★ 五分钟补齐 1NF–3NF
DDIA 2e ch3★★★★ Normalization/Denormalization 一节直接回答冗余取舍
MongoDB 文档★★★☆ "accessed together"原则可迁移,但语境是文档库
Kimball / 大数据之路★★ 方法论对,尺度过大
45 讲★★☆ 帮你理解索引与事务,不回答表怎么分
SQLBolt / Select Star★ 会查询,不解决建模

结论:场景 A 是关系范式与反范式的标准战场,书籍类竞品表现最好;本课题的价值在于把这几本书的答案压缩成一次可执行的取舍清单(三问卡速查版见附录 A,记账 App 建表 DDL 与取舍清单见附录 B),并配上记账 App 的完整案例。

场景 B:电表 / 体重秤读数日志(树莓派传感器,每分钟 1 条,约 50 万行/年)

会碰到的建模决策:宽表(每个传感器一列)还是长表(每行一条读数)?要不要按时间分区?旧数据怎么压缩/丢弃?查询几乎全是"按传感器 + 时间范围聚合"。

竞品适配表现
Tiger Data(Timescale)文档★★★★★ hypertable/连续聚合/保留策略就是为这个场景设计的
InfluxDB 3 文档★★★★★ tags/fields 模型 + query-first schema 直接给答案
ClickHouse 文档★★★★☆ MergeTree ORDER BY (time, sensor_id) 是标准解法,但单机个人项目略重
PostgreSQL 文档★★★★ 原生表分区(DDL §5.11)是不引第三方的折中路线
其余全部竞品★ 三本书、45 讲、SQLBolt 均不覆盖

结论:时序子域是书籍与课程竞品几乎无人覆盖的领域——书籍中只有 DDIA 2e 在存储引擎章节散落谈及(总表计 △),没有一本经典书或主流课程系统教个人开发者"50 万行传感器日志该怎么建模",唯一成体系的知识来源就是三家时序库官方文档,而它们给出三种彼此分歧的建模世界观。表中两家 ★★★★★ 的决胜规则就在量级(三问之③,见 §3.2):50 万行/年的树莓派数据,Tiger Data(Timescale)把答案留在 PostgreSQL 生态内,是"不引入第二套运维"的默认解;InfluxDB 的 tags/fields 模型适合以监控为中心、写多读聚合的独立负载;ClickHouse 留给日增亿级以上的档位。本课题在此子域的横向对比价值最高:把三家的取舍放进同一张表,正是个人项目作者最需要而市场上没有的东西。

场景 C:个人博客 / 小工具的埋点事件流(page_view、sign_up、button_click)

会碰到的建模决策:事件表要不要 append-only?事件属性用固定列还是 JSONB?要不要引入完整的 Event Sourcing?事件名怎么命名才不会半年后失控?

竞品适配表现
Fowler Event Sourcing 短文★★★★★ 定义 + 适用/不适用清单,一篇文章就是取舍清单
DDIA 2e ch3/ch12★★★★ Event Sourcing & CQRS 与 Stream Processing 两节
MongoDB 文档(反模式章)★★★☆ 事件属性膨胀的反模式警示可迁移
其余书籍★★ Kimball 事实表思想相关但语境是企业数仓
全部课程/练习平台★ 无覆盖

结论:事件子域与场景 B 同构——经典书只有 DDIA 谈到,课程类竞品(45 讲、Coursera、DataCamp、SQLBolt)全部缺席。本课题"要不要上 Event Sourcing"的答案(通常:不上,用 append-only 事件表就够)恰好与 Fowler 文章立场一致,可以引用其 URL 作为权威背书。

三场景综合:

子域对应场景最强竞品本课题的差异化
范式AMere Mortals 4e / IBM 短文把书里的 25 年方法压缩成个人项目尺度的清单
反范式A+CMongoDB 文档 / DDIA ch3 / Kimball三种语境(文档库/分析数仓/OLTP)的冗余决策统一到一张取舍表
时序B三家时序库官方文档在彼此分歧的三个官方答案之间做横向裁判
事件CFowler 短文 / DDIA ch12给"要不要上 ES"一个个人项目尺度的明确答案

2.5 竞争格局五条结论

  1. 没有全能竞品,但每类竞品在单点上都有优势:DDIA 2e(能力上限)、Mere Mortals(范式通俗度)、MongoDB 文档(反范式可操作性)、三家时序文档(时序权威)、SQLBolt/freeCodeCamp(入门体验)、Fowler 短文(事件模式)、45 讲(中文内核视角)。
  2. 本课题的生态位是"地图层":四个子域各自的头号竞品都已存在且基本免费,缺的是把它们放在同一张取舍表里、并用个人项目案例串联的中文入门路径。因此正确策略是衔接而非对抗——先修层挂 SQLBolt/freeCodeCamp,深度层外链 DDIA 与 Kimball,时序与事件层逐家引用官方文档。本稿以附录 A(三问卡速查版)与附录 B(青苔账本 2026 建表 DDL 与取舍清单)兑现该路径的可执行部分。
  3. 两大结构性空档构成课题最大价值:时序与事件两个子域在书籍与课程竞品中几乎无人覆盖,而个人项目(传感器、埋点、日志)恰恰最容易碰到这两个子域。
  4. 时效风险:竞品中 DDIA 2e(2026-02)与 freeCodeCamp V9(2025-12)刚更新,本报告结论在半年内应保持稳定;Kimball 3e 若出新版、Tiger Data 若再更名,需回刷 §2.3。
  5. 成本维度上的提醒:对个人项目作者,最贵的不是钱而是"学错粒度"——用企业数仓的方法论(Kimball/大数据之路)建一张 50 万行的个人表,或用 DataCamp 330 美元换来的 SQL 熟练度去做本该 10 分钟完成的范式取舍。本课题把所有竞品的粒度对齐到"一个人的项目",这是横向对比的最终落点。

三、交叉洞察:两轴结合说明了什么

以下四条洞察为本稿新增的综合判断:把纵轴(五十六年怎么走到今天)与横轴(今天谁在教、谁缺席)叠在一起,能得到任何单轴都得不到的结论。

3.1 洞察一:今天的竞品格局,是历史的沉积层

横轴的五类竞品不是随机分布的,它们恰好对应纵轴各时代知识的"固化形态":

这条沉积律直接解释了横轴最大的发现——时序与事件两子域,课程类全部缺席,书籍类除 DDIA 2e 外也无人覆盖:不是作者们疏忽,而是这两个子域 2010 年代才成形(§1.3 阶段四、五),知识还在"厂商文档"这一层,尚未完成向书籍和课程的"下沉"——书籍类唯一的例外 DDIA 2e(2026-02)恰是离这两个子域成书最近的一次尝试。历史进度表恰好解释了竞品缺口的位置(横轴先发现缺口、纵轴事后给出解释,而非预言)。(综合判断)

3.2 洞察二:历史的主线变量,就是个人的决策变量

纵轴的主线变量是"查询模式的可预知程度 × 成本曲线"——它解释了五次范式转移,反过来它也就是个人开发者今天做建模决策时唯一需要问的变量。把五十六年压缩成一张三问决策卡(综合判断):

三问若答案偏向左若答案偏向右
① 查询模式现在就可预知吗?预知(报表/面板固定)→ 放心反范式:物化、宽表、预聚合不可预知 → 保持归一化 + 灵活索引,等形状显现再动
② 数据的本质是实体、时间还是过程?实体 → ER + 范式化时间 → 时序三件套(时间主键前缀/标签分 series/降采样);过程 → append-only 事件表 + 物化视图
③ 量级多大?个人尺度(总量百万行级以内、写入持续低于百行/秒)→ 一个 PostgreSQL 到底(JSONB/Timescale/pgvector 扩展按需加)写入爆炸(持续万行/秒以上、单表日增亿级)或多服务规模 → 才轮到 NoSQL/流/湖仓那一格

执行顺序(三问的使用规则):三问不是并列的选择题,而是一条有先后的流水线——

  1. 先问 ②,定形状:数据的本质(实体/时间/过程)决定用哪套建模范式——实体走 ER + 范式化,时间走时序三件套,过程走 append-only 事件表。这一步与名气、时尚无关。
  2. 再问 ③,定档位:量级决定工具档位,参考值:个人尺度(总量百万行级以内、写入持续低于百行/秒)→ 一个 PostgreSQL 到底;中间档(总量千万至亿行、写入千行/秒级)→ 单机加专用引擎(如 TimescaleDB hypertable;ClickHouse 对个人项目略重);写入爆炸(持续万行/秒以上、单表日增亿级、需要水平分片)→ 才轮到 NoSQL/流/湖仓那一格。
  3. ① 只作触发器:查询模式可预知只是"允许反范式"的许可条件,不是动手的理由——与建议 3"范式默认开、反范式等触发"一致,等报表真变慢且查询形状固定,才物化宽表/预聚合。

按此顺序,"查询可预知(想反范式)"与"量级小(一个 PG 到底)"同时命中时不再无所适从:②定形状、③定档位、①只在慢了以后触发冗余,三步走完答案唯一。速查版见附录 A。(综合判断)

这张卡的三个格子恰好覆盖纵轴七个阶段的所有答案,也和横轴的最佳适配资源一一对应:问①对应 Mere Mortals/MongoDB 文档/DDIA ch3,问②对应三家时序文档/Fowler 短文,问③对应 PostgreSQL/ClickHouse/InfluxDB 文档。历史给个人项目的最大馈赠是:你不需要记住 56 年,只需要记住这三个问题。

3.3 洞察三:两轴共同的警告——"复杂度税"与"学错粒度"

纵轴结论 4(钟摆的摆锤是复杂度税)与横轴结论 5(最贵的是学错粒度)其实是同一条定律的宏观版与微观版(综合判断):

两轴合起来给出一条精确的边界线:架构复杂度应该与数据形状的多样性成正比,而不是与"看起来专业"的程度成正比。青苔账本 2026 年的形态(一个 PostgreSQL + 一个 DuckDB + 一条轻量事件流)之所以是"对的",不是因为它是新潮流,而是因为它的问题规模只值这么多复杂度;同样,一旦你的传感器真到每天亿级点位,三家时序文档里 ClickHouse 那种列存世界观就会变成正解——横轴 §2.4 场景 B 中三家文档"彼此分歧"的表象,在纵轴的时间变量下其实是统一的:它们各自对应不同量级与负载形状下的最优解,矛盾只在你选错尺子时才出现。

3.4 洞察四:"地图层"生态位是历史结构的必然,衔接是唯一正确策略

横轴结论 2 说本课题的生态位是"地图层、衔接而非对抗";纵轴为这个判断补上了结构性理由:四个子域的头号资源分属四个不同时代的产物(1970s 的范式理论、1990s 的维度建模、2010s 的厂商文档、2020s 的 DDIA),它们在时间上从未同框,自然也不会有人把它们放进同一张图。书籍不会替你补厂商文档,文档不会教你理论,课程不知道你个人项目的规模。地图层的空档不是商业巧合,而是知识生产速度差造成的永久性结构缝——这也是"入门类横向综述"类内容长期有效的根本原因。(综合判断)

3.5 对用户的判断与建议

基于两轴证据,给出如下可执行的判断与建议(综合判断,工具与文档均引用 §1/§2 中已核验来源):

总体判断:2026 年个人项目的数据建模,"起步姿势"五十六年没变(一个归一化的 SQL 库),"上限"被一个引擎吃下所有负载的融合潮流大幅抬高(PostgreSQL 系扩展 + DuckDB + 轻量事件流);真正的入门难点不在选库,而在识别自己数据的时间/事件属性并拒绝超尺度的复杂度。

建议清单:

  1. 起步:一个归一化的 PostgreSQL(或 SQLite)。1985 年的答案在 2026 年依然正确,且 PG 已通过 JSONB(2014)、TimescaleDB、pgvector(2021)吃下文档、时序、向量三种形状——先一体,长痛再拆。
  2. 三问决策卡:每次加表/加库前过一遍 §3.2 的三问(可预知吗?实体/时间/过程?量级多大?),按"先②定形状、再③定档位、①只作触发器"的顺序执行,量级参考值见附录 A。问完通常会发现什么都不用做。
  3. 范式默认开,反范式等触发:报表或面板慢了、且查询形状固定了,再物化宽表/预聚合(青苔账本案例节点 2 的教训反着用:不要提前预聚合)。
  4. 时序看形状:数据一旦是 append-only + 按时间查询 + 要过期,就换时序肌肉——个人尺度优先 PG 原生分区或 TimescaleDB hypertable(场景 B ★★★★★),别一上来就 ClickHouse;InfluxDB 的 tags/fields 模型适合监控类负载。三家文档代表三种世界观,按量级选,不按名气选(参考阈值见附录 A:总量年增百万行级以内留在 PG 或加 hypertable;持续万行/秒以上、单表日增亿级,才轮到 ClickHouse 这类列存引擎)。
  5. 事件克制:埋点、操作审计日志、自动记账这类"只看流水、不靠重放重建状态"的需求,用 append-only 事件表 + JSONB 属性列就够了;完整 Event Sourcing 留给"必须重放历史重建状态"的场景——需要重放出任意时点余额与报表的会计账本、协作编辑——与 Fowler 的适用清单一致(§2.3 ⑤)。
  6. 学习路径:SQL 先修挂 SQLBolt / freeCodeCamp(免费、上手最快)→ 用本课题的地图层建立四子域取舍观 → 深度外链按需选:范式补 Mere Mortals,反范式补 MongoDB 文档与 DDIA ch3,时序补三家官方文档,事件补 Fowler 短文,进阶通读 DDIA 2e。AI 助教当答疑陪练,不当大纲。
  7. 防粒度税自查:如果你正在为个人项目读《大数据之路》或给博客引入 Kafka + Event Sourcing + 微服务数据层,先回到三问卡——大概率是学错粒度了。
  8. 时效维护:本报告结论稳定期约半年;DDIA 2e(2026-02)与 freeCodeCamp V9(2025-12)为最新竞品基线,Kimball 出新版或 Tiger Data 再更名时回刷横轴 §2.3。

四、参考来源

4.1 纵轴(历史脉络,按阶段)

阶段来源
一Codd 1970 原文(ACM)、PDF、IBM 史话、3NF、Normalization、Chen 1976、Chen PDF、Oracle V2 文档、ANSI Blog、Wikipedia: SQL、PostgreSQL 史话、Two Bit History
二Inmon PDF、Kimball Group、Codd OLAP 1993、Oracle MySQL 回顾、Widenius
三Bigtable PDF、Dynamo PDF、CAP 定理、Gilbert & Lynch、Brewer 2012、Cassandra(已全文核读)、MongoDB、Redis
四Kx/kdb、kdb 史话、OpenTSDB、Prometheus@SoundCloud、CNCF 毕业、InfluxDB 1.0 回顾、TimescaleDB 官宣、Grafana 4 年、Grafana UX 史话
五Fowler: Event Sourcing、Fowler: CQRS、Greg Young、Kafka 论文、Wikipedia: Kafka、The Log、Schema Registry、Debezium MySQL CDC、Event Modeling、Confluent 课程
六Snowflake IPO、CNBC、dbt Labs、湖仓表格式对比
七PG 9.4 JSONB、ClickHouse 开源 10 年、Wikipedia: ClickHouse、宽表讨论、DoubleCloud 解析、DuckDB 论文、pgvector、Prime Video 原文存档、HN 讨论、DHH、InfluxDB 3.0、InfluxDB 3.0 开源计划

4.2 横轴(2026-11-05 截面竞品,检索与抓取来源汇总)

(口径与 §2.3 一致:标注"实抓"者为当日 WebFetch/WebReader 全文抓取并核验;其余条目来自当日 WebSearch 搜索摘要。)

书籍类

课程与专栏类

官方文档类(均免费)

交互练习与短文类


五、附录

附录 A:三问卡速查版(打印/置顶用)

判断顺序:先 ② 定形状 → 再 ③ 定档位 → ① 只作触发器。(使用规则详见 §3.2;与 §3.5 建议 3"范式默认开、反范式等触发"一致。)

第 1 步|问②:数据的本质是什么?(定形状)

第 2 步|问③:量级多大?(定档位)

档位参考量级对应答案
个人尺度总量百万行级以内,写入持续低于百行/秒一个 PostgreSQL 到底(JSONB / TimescaleDB / pgvector 按需加,SQLite 亦可)
中间档总量千万至亿行,或写入千行/秒级单机加专用引擎:TimescaleDB hypertable(ClickHouse 对个人项目略重)
写入爆炸持续万行/秒以上、单表日增亿级、需要水平分片才轮到 NoSQL / 流 / 湖仓那一格(Dynamo/Cassandra/Kafka 思路),或分布式 ClickHouse/列存集群

典型例子对号入座:青苔账本全年记账数万条(个人尺度);场景 B 的传感器每分钟 1 条 ≈ 50 万行/年(个人尺度);传感器真到每天亿级点位(写入爆炸 → ClickHouse 列存世界观变成正解,见 §3.3)。

第 3 步|问①:查询模式现在就可预知吗?(只作触发器)

常见误用:①③同时命中(查询可预知 + 量级小)→ 仍按顺序走:②定形状、③定档位,①只决定"何时开始冗余";问完大概率是什么都不用做(建议 2)。

附录 B:青苔账本 2026 建表 DDL 与取舍清单

形态即案例节点 7:一个 PostgreSQL(账本 + JSONB 收据 + TimescaleDB 时序 + pgvector 检索)+ 一个 DuckDB(本地分析 Parquet)+ 一条轻量事件流(CDC 到下游物化视图)。以下 DDL 为可运行的最小示意(PostgreSQL 18 + TimescaleDB + pgvector,术语见附录 C)。

① 账本与博客:范式默认开(场景 A)

CREATE TABLE accounts (
  id    SERIAL PRIMARY KEY,
  name  TEXT NOT NULL UNIQUE,
  type  TEXT NOT NULL CHECK (type IN ('cash','bank','card','virtual'))
);

CREATE TABLE categories (
  id    SERIAL PRIMARY KEY,
  name  TEXT NOT NULL UNIQUE,
  kind  TEXT NOT NULL CHECK (kind IN ('income','expense'))
);

-- 交易头 + 双边分录:转账 = 两条分录同一事务写入,靠外键与 CHECK 保证借贷相抵
-- 取舍:不冗余账户名/分类名(范式),改名只改一处
CREATE TABLE transactions (
  id           SERIAL PRIMARY KEY,
  occurred_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
  note         TEXT
);

CREATE TABLE transaction_entries (
  id             SERIAL PRIMARY KEY,
  transaction_id INT NOT NULL REFERENCES transactions(id),
  account_id     INT NOT NULL REFERENCES accounts(id),
  category_id    INT REFERENCES categories(id),
  direction      TEXT NOT NULL CHECK (direction IN ('in','out')),
  amount         NUMERIC(12,2) NOT NULL CHECK (amount <> 0)
);

-- 博客(案例节点 3 回归 PG 的那一半):两张标准范式表
CREATE TABLE posts (
  id          SERIAL PRIMARY KEY,
  slug        TEXT NOT NULL UNIQUE,
  title       TEXT NOT NULL,
  created_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE comments (
  id          SERIAL PRIMARY KEY,
  post_id     INT NOT NULL REFERENCES posts(id),
  author      TEXT NOT NULL,
  body        TEXT NOT NULL,
  created_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);

② 报表:反范式等触发(触发器①)

-- 只有当"按月×分类汇总"被反复查询且真的变慢了,才建物化视图(案例节点 2 的教训反着用)
CREATE MATERIALIZED VIEW monthly_category_spend AS
SELECT date_trunc('month', t.occurred_at) AS month,
       e.category_id,
       sum(e.amount) FILTER (WHERE e.direction = 'out') AS outflow,
       sum(e.amount) FILTER (WHERE e.direction = 'in')  AS inflow
FROM transaction_entries e
JOIN transactions t ON t.id = e.transaction_id
GROUP BY 1, 2;
-- 刷新:REFRESH MATERIALIZED VIEW monthly_category_spend;(定时或报表前手动)

③ 传感器时序:时序三件套(场景 B)

CREATE EXTENSION IF NOT EXISTS timescaledb;
CREATE TABLE sensor_readings (
  ts        TIMESTAMPTZ NOT NULL,
  sensor_id TEXT NOT NULL,              -- 电表 / 体重秤
  value     DOUBLE PRECISION NOT NULL
);
SELECT create_hypertable('sensor_readings', 'ts');     -- 按时间自动分块

-- 降采样:连续聚合,按小时增量维护
CREATE MATERIALIZED VIEW readings_hourly
WITH (timescaledb.continuous) AS
SELECT time_bucket('1 hour', ts) AS hour,
       sensor_id, avg(value) AS avg_value,
       min(value) AS min_value, max(value) AS max_value
FROM sensor_readings
GROUP BY 1, 2;

-- 保留策略:明细只留两年,到期自动清理,不手写 DELETE
SELECT add_retention_policy('sensor_readings', INTERVAL '2 years');

④ 事件流:事件克制(场景 C)

-- append-only 事件表:只插不改不删;属性进 JSONB,等知道要查什么再加 GIN 索引
CREATE TABLE app_events (
  id           BIGSERIAL PRIMARY KEY,
  occurred_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
  event_name   TEXT NOT NULL,           -- page_view / sign_up / entry_auto_created
  payload      JSONB NOT NULL DEFAULT '{}'
);

-- 完整 Event Sourcing 不上:账本状态以分录表为准,无需重放事件重建;
-- 下游报表由 CDC(如 Debezium)把变更同步成物化视图,避免双写不一致(案例节点 5)

⑤ 原始收据与语义检索:一个库吃下文档与向量

-- 结构未定的原始收据先整包进 JSONB,解析稳定后再提字段(PG 9.4+,案例节点 7)
CREATE TABLE raw_receipts (
  id      SERIAL PRIMARY KEY,
  txn_id  INT REFERENCES transactions(id),
  raw     JSONB NOT NULL
);
CREATE INDEX ON raw_receipts USING gin (raw);   -- GIN:支持"含某键/某值"类查询

CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE assistant_docs (          -- "记账助手"语义检索
  id         SERIAL PRIMARY KEY,
  content    TEXT NOT NULL,
  embedding  vector(1536)
);

取舍清单(一页版)

决策点选择一句话理由
交易表要不要冗余账户名不冗余,存 account_id范式默认开:改名只改一处
转账一致性靠什么同一事务写两条分录 + 外键/CHECK让库保证,不靠应用自觉
月末报表慢怎么办先索引;查询形状固定后再建物化视图反范式等触发,不提前预聚合
传感器数据放哪同一个 PG 的 hypertable50 万行/年犯不着第二套运维(决胜规则见 §2.4 场景 B)
旧读数怎么清保留策略自动过期append-only 数据别手写 DELETE
事件要不要上 Event Sourcing不上,append-only 事件表就够账本状态以分录为准,无需重放重建
事件属性怎么存JSONB 列 + 按需加 GIN 索引半年后才知道要查什么
什么时候才拆库日增亿级 / 持续万行/秒以上按量级选,不按名气选(附录 A 档位表)

附录 C:术语表(按文中首现顺序的白话一句话)

术语白话解释
append-only只追加、不修改、不删除的写入方式——日志、事件流、时序读数的天然形态。
series(时间线)时序数据库里"同一来源的连续读数":某一只电表的全部读数就是一条 series,靠标签(如 sensor_id)区分。
降采样(downsampling)把高频原始读数按时间窗口压成低频汇总(如每分钟一条 → 每小时一条均值),给老数据省空间。
保留策略(retention policy)数据到期自动清理的规则,例如"明细只留两年",替代手写 DELETE。
写放大(write amplification)实际写入磁盘的数据量远大于应用写入量——每插一行都要同步更新所有索引,高频写入时被索引拖垮。
TTL(Time To Live)数据的"保质期",到期自动删除;监控与指标类数据最常见的过期方式。
CDC(Change Data Capture,变更数据捕获)盯住数据库自己的事务日志,把每一行变更转成事件发给下游;下游不用"双写"也能与主库保持同步。
物化视图(materialized view)把一个查询的结果实际存成表,查询时直接读现成答案;代价是底层数据变了之后要手动或定时刷新。
ELT(Extract-Load-Transform)先把原始数据原样搬进仓库,再用 SQL 做转换;与传统"先转换再装载"的 ETL 顺序相反。
GIN 索引PostgreSQL 的倒排索引,专治"一个字段里装了很多东西"的查询——比如 JSONB 里有没有某个键、数组里含不含某个值。
向量化执行查询引擎一次处理一整批(一列向量)数据而非逐行处理,是列存分析数据库快的关键之一。
hypertableTimescaleDB 的核心抽象:一张逻辑上的普通 PG 表,背后按时间自动切成小块(chunk);一条 create_hypertable() 即可变出来。
连续聚合(continuous aggregate)TimescaleDB 的自动预聚合:按小时/天等时间窗口持续维护汇总结果,新数据一到自动增量更新。

*本综合稿由纵轴章(2026-11-05-数据建模入门-vertical.md)与横轴章(2026-11-05-数据建模入门-horizontal.md)综合而成,未验证细节已在两章原文用保守措辞处理并随文标注。本稿为最终稿:已按 2026-11-05 独立审读意见(8 条)逐条修订,新增附录 A(三问卡速查版)、附录 B(青苔账本 2026 建表 DDL 与取舍清单)与附录 C(术语表)。*

← 资料库今日播报 →