Bigtable — 把巨大表格切到上千台机器上
待复核Bigtable 是 Google 设计的分布式结构化存储系统。日常类比:它像一张超大的电子表格,只是这张表大到一台机器放不下,只能切成很多小片分散在上千台机器上。
它的每个格子不是一个值,而是一串按时间排序的版本。你可以把它想成“行、列、时间”三个坐标定位到一摞小纸条。
最小写入长这样:
Put("com.example.www", "contents:html", 20060815, "<html>...</html>")这表示:在 com.example.www 这一行、contents:html 这一列、20060815 这个时间版本里写入页面内容。
- 不理解 Bigtable,就很难解释 HBase、Cassandra、Cloud Bigtable 这些系统为什么长成宽列表。
- 不理解 Bigtable,就会误把 NoSQL 理解成“不要 SQL”,而看不到它真正牺牲了 join、二级索引和跨行事务。
- 不理解 Bigtable,就看不懂 LSM-tree、SSTable、compaction 为什么成了后续存储引擎的常用套路。
- 不理解 Bigtable,就无法判断 row key 设计为什么比“加几列字段”更影响性能。
-
三维稀疏有序表。Bigtable 的 key 是
(row, column, timestamp),value 是任意字节。类比图书馆:row 是书架,column 是书格,timestamp 是同一本书的不同版本。 -
tablet 是扩展单位。一张表会按 row range 切成 tablet,不同 tablet 放到不同 tablet server。类比把一本电话簿按姓氏范围撕成很多册,分给不同柜台查。
-
写入走 LSM 思路。写先进入内存表和日志,再刷成不可变 SSTable,后台合并。类比先把新账记在便签本,晚上再整理进正式账册。
案例 1:网页爬虫存页面历史
Section titled “案例 1:网页爬虫存页面历史”爬虫可以把 URL 反转后当 row key。
row = "com.cnn.www/index.html"column = "contents:html"timestamp = 20060815value = "<html>v1</html>"逐部分解释:
- 反转域名让同一个站点的页面在字典序里靠近,范围扫描更顺。
contents:html是列族加列名,适合把页面内容放在一起。- timestamp 让系统能保留多个历史版本,而不是每次覆盖旧页面。
案例 2:时序指标不要直接用时间做 row key
Section titled “案例 2:时序指标不要直接用时间做 row key”直觉上你可能想这样写:
row = "2026-07-09T12:00:00"column = "cpu:machine-42"value = "0.87"问题是所有新写入都会集中到最新时间附近,容易把同一个 tablet server 打热。
更稳的方式是:
row = "machine-42#cpu"column = "value:"timestamp = 1783579200value = "0.87"逐部分解释:
- row 用机器和指标分散写入位置。
- timestamp 放进版本维度,用来保留历史值。
- 过期数据可以靠版本保留策略和 compaction 清掉。
案例 3:用户画像宽表
Section titled “案例 3:用户画像宽表”row = "user-12345"profile:name = "Alice"behavior:click_news = "17"computed:churn_score = "0.12"逐部分解释:
profile、behavior、computed是 column family,通常建表时确定。click_news这种 qualifier 可以动态增加,不需要传统 SQL schema migration。- 稀疏列不占空间,适合“每个用户拥有不同属性”的数据。
-
把 Bigtable 当关系数据库:它没有 join、外键、复杂 SQL 和跨行事务,强行套 SQL 思维会让应用层补很多逻辑。
-
row key 单调递增:timestamp 或自增 ID 当前缀时,新写入集中到一个 tablet,系统会出现热点。
-
column family 随便建:family 会影响存储布局和 compaction,按访问局部性分组比按业务名词分组更重要。
-
忽略 compaction 成本:后台合并能提升读性能和回收旧版本,但会吃 IO,线上需要限速和错峰。
适用 vs 不适用
Section titled “适用 vs 不适用”适用:
- PB 级数据,需要按行随机读写,也需要范围扫描。
- 爬虫页面、日志、时序、用户画像这类宽而稀疏的数据。
- 写入很多、查询模式相对固定的系统。
- 能接受应用自己设计 row key 和访问路径的团队。
不适用:
- 需要复杂 join、临时分析 SQL、二级索引组合查询的业务。
- 需要跨很多行做强一致事务的 OLTP。
- 数据量很小、单机 Postgres 或 SQLite 已经够用的场景。
- row key 访问模式不清楚、查询需求经常变的早期产品。
历史小故事(可跳过)
Section titled “历史小故事(可跳过)”- 2003 年:Google 发表 GFS,先解决“很多机器一起存大文件”的问题。
- 2004 年:MapReduce 发表,批处理计算和大规模存储开始配套。
- 2006 年:Bigtable 和 Chubby 同年出现在 OSDI,Google 的存储栈拼图基本成形。
- 2008 年后:HBase、Cassandra 等系统吸收 Bigtable 的列族、SSTable、LSM 思路,NoSQL 浪潮扩散。
- 2012 年:Spanner 在 Bigtable 思路上补全球事务,回应了 Bigtable 不擅长跨行强一致的问题。
- Bigtable 的取舍很清楚:放弃通用查询,换来巨大规模下的简单读写。
- row key 是 Bigtable schema 设计的核心,它决定数据会不会聚在一台机器上。
- 不可变文件加后台合并,是很多现代存储引擎提升写入吞吐的共同套路。
- 一个好系统常常不是全都自己做,Bigtable 依赖 GFS 存字节、Chubby 做协调。
- 论文 PDF:Bigtable: A Distributed Storage System for Structured Data
- gfs —— Bigtable 的底层文件存储基础。
- chubby —— Bigtable 用来做 master 选举和元数据协调。
- spanner-2012 —— 给 Bigtable 路线补上全球事务。
- cassandra-2010 —— 把 Bigtable 数据模型和 Dynamo 拓扑拼到一起。
- rocksdb-lsm —— 单机 LSM 存储引擎的现代代表。
- gfs —— Bigtable 的 SSTable 和 commit log 都放在分布式文件系统上。
- chubby —— 用于协调 master、schema 和 tablet 元数据。
- mapreduce —— Bigtable 常作为 MapReduce 的输入和输出。
- spanner-2012 —— 在 Bigtable 式存储上补强事务和 SQL。
- cassandra-2010 —— 继承宽列模型,但改成无中心化架构。
- rocksdb-lsm —— 展示 LSM/SSTable 思想在单机 KV 里的延续。
- snowflake-2016 —— 同样强调存储与计算拆分,但面向分析数仓。
- aurora —— Aurora — 把数据库的下半身换成日志机
- borg —— Borg — Google 把一万台机器假装成一台
- cap-12-years-later-2012 —— CAP 十二年后 — Brewer 自己承认”三选二”是误读
- chubby —— Chubby — 给凡人用的分布式锁服务
- dynamo —— Dynamo — 让购物车永远能写入的分布式存储
- f1-2013 —— F1 2013 — 把 Spanner 包成 SQL,扛起 AdWords 全部账单
- fermi-architecture-2010 —— NVIDIA Fermi — 把 GPU 从游戏卡推上超算
- ingres-1976 —— INGRES 1976 — Berkeley 平行实现的关系数据库
- kafka —— Kafka — 把消息系统降维成只追加的日志文件
- lamport-1978 —— Lamport 1978 — 分布式系统里没有”绝对的同时”
- lfs-1991 —— LFS 1991 — 把整个磁盘当日志写
- lsm-tree-1996 —— LSM-Tree 1996 — 写优化存储引擎
- pagerank-1998 —— PageRank — 用随机游走给整个网络的页面打分
- paxos-1998 —— Paxos 1998 — 古希腊议会寓言里藏的共识协议
- paxos-simple-2001 —— Paxos Made Simple — Lamport 用平直英语把共识协议推导一遍
- skip-list-1990 —— Skip List — 用抛硬币代替平衡树
- smr-1990 —— SMR 1990 — 把”容错服务”还原成”多副本一起跑同一台状态机”
- spanner-2012 —— Spanner 2012 — 用原子钟和 GPS 给全球数据库发时间戳
- system-r-1976 —— System R 1976 — 第一个跑起来的关系数据库
- tao-2013 —— TAO — Facebook 给十亿人好友列表造的专用图数据库
- trustrank-2004 —— TrustRank — 用一小撮可信种子把整张 Web 的信誉算出来
- volcano —— Volcano — 把’算子可组合’与’并行可分离’拼成执行器范式
- volcano-1994 —— Volcano 1994 — 把 SQL 执行写成 next() 拉式数据流
- leveldb —— LevelDB — Google LSM 库
- rocksdb —— RocksDB — 嵌入式 LSM 引擎