专业书籍精读 · Database Internals · 第 2 章

B-Tree 基础

Database Internals · Ch 2 · Alex Petrov · 2019

EN →

这一章讲什么?

你在淘宝按订单号查一笔三年前的订单,几十亿条记录里,零点几秒就翻了出来。数据库靠的是索引——一本能瞬间定位的目录。这一章讲的就是那本目录几乎唯一的做法:B-Tree。它 1972 年被提出,五十多年过去,MySQL、PostgreSQL、SQLite、Oracle 的默认索引仍然是它。为什么偏偏是它?

先说个怪事

计算机课上教的「查找树」,是每次问一个是非题:比它大还是比它小?往左还是往右?这在内存里非常好用。可一旦把同样的树搬到硬盘上,它就慢得不能用——快慢的账,突然算错了。

旧世界为什么难

关键在于:硬盘不零卖。哪怕只想读一个字节,它也得整块整块地搬——一次最少几千字节。跑一趟很贵,搬多搬少反倒差别不大。

而那种「每次只岔两条路」的树,一个岔路口只有一个小小的路标。几十亿条数据,你得连着问三十来次「往左还是往右」,每问一次就要往硬盘跑一趟。慢就慢在跑腿的次数上,不是比大小的次数上。

核心机制:把路标做成目录页

第一招是不再让一个岔路口只放一个路标。既然每次都要整块搬,那就把这一整块塞满路标——一页纸上列出几百个区间:「订单号 1 万到 5 万在这架,5 万到 9 万在那架……」看一眼就知道下一步去哪,就像查字典先看侧边的字母索引。结果是树被压扁了:原来三十几层,现在三四层;最上面那两三层很小、常年待在内存里,于是查一次通常只要真去硬盘跑一趟。

第二招更妙:这棵树是从下往上长的。新数据先塞进最底下那一页;那页满了就一分为二,把中间那个路标交给上一级;上一级也满了再往上交;一直到最顶上那页也撑不住时,整棵树才整体长高一层。因为长高永远发生在顶上,每片叶子离树根永远一样远——它天生就是平的,不用像别的树那样翻来覆去调平衡。

第三招给「翻页」用:真正的数据只放最底下一层,而最底层的页手拉手串成一条链。所以「把三月一号之后的一千条订单列出来」,就是找到起点顺着链走,不用再爬回树顶。

带来了什么 / 该怎么选

五十年不倒,是因为它一次满足了三件事:找一条快、连着找一批也快、还能随时插删并保持有序。哈希表找一条也快,但它不认顺序,后两件都做不了。

诚实的代价只有一句:它偏爱按顺序灌进来的数据——自增编号、时间戳都省心;主键要是随机码那样到处乱插,页就会被劈得普遍只装半满,白占空间、写入也更费劲。

一句话记住

B-Tree 不是「更聪明的查找树」,而是为硬盘的脾气重新设计过的查找树:既然硬盘只能整块搬,那就把一整块塞满路标、一次岔出几百条路,把树从三十层压成三四层——省的不是比大小的次数,是跑硬盘的次数。

想进到具体机制、记号和示意图? → 切到精读版