第 3 章 · 数据库第 2 页,共 4 页
在几百万行里,怎样很快找到一行?
百家菜的菜谱表有了 300 万行。这一页先假设没有索引,看数据库怎样逐行翻看;再换成编号的索引,看它怎样只看二十来次就找到,以及每新增一行要为它多做的工作。
为什么需要
读者打开一道菜谱、按配料找菜、看自己收藏了哪些菜,每一次,服务器都要向提一个问题,叫(query),比如「找出第 1024 号菜谱」。查询是一种:只把数据找出来,不改变它。
百家菜越办越大,菜谱表有了 300 万行。数据库怎样在 300 万行里找出第 1024 号?
怎样做
数据库不保证表里的行按编号排好,第 1024 号可能在任何位置。没有别的办法时,数据库只能从第一行起逐行翻看,这叫(full table scan):运气好,翻几行就到;平均要翻一半,150 万行;要找的菜已经删掉时,非得翻完全部 300 万行,才能确定没有。
快的办法是(index):为某一列另外整理一份按顺序排好的清单,每一项记着这一行在表里的位置,表本身不动。这像书末的索引,词按顺序排好,翻到要找的词,就知道它在第几页。找的时候先看清单正中间的一项:要找的比它小,就只看前一半,比它大就只看后一半。每看一次,范围缩小一半。
300 万对半分下去,最多看 22 次,就能确定有没有。100 万行约 20 次,200 万行约 21 次:行数翻一倍,只多看 1 次。编号是主键,设了主键的列,数据库通常会自动替它建这样一份索引。为了看清它帮了多大的忙,下图可以先假设没有它,再换成有索引,各找一次。
找一行:没有索引时逐行翻看,有索引时对半找
先看这里先选「假设没有索引」找第 1024 号,看翻看的次数怎样一路上涨;再换成「有编号的索引」找一次,比一比次数,也看看图下「每新增一行要做的工作」那根条。
| 第几行 | 编号 | 菜名 |
|---|---|---|
| 1 | 1,624,801 | 宫保鸡丁 |
| 2 | 469,622 | 土豆炖牛肉 |
| 3 | 1,955,887 | 麻婆豆腐 |
| 4 | 2,686,889 | 酸辣汤 |
| 5 | 1,900,252 | 宫保鸡丁 |
| 6 | 2,092,683 | 土豆炖牛肉 |
| 7 | 2,126,288 | 麻婆豆腐 |
找一行的示意图:一边是菜谱表的一段,高亮的是正在翻看的那一行;有索引时,另一边每看一次就多一条横条,表示这一次还要找的那一段,一条比一条窄,太窄时放大成整条接着画;找到以后,表跳到索引指向的那一行。翻看的次数和结果写在图下的读数里。
还没开始。选好要找的菜,按「下一步」,也可以直接看结果。
没有索引:只写进表这一处。
表里每一行的编号、菜名和位置都是为演示虚构的,每次打开都一样;同一个菜名可能有好几道,编号各不相同。没有索引时数的是翻看了几行,有索引时数的是在索引里看了几个编号,每看一个编号算一次,两种用的是同一个单位。
查询常用(SQL)来写,大多用它。它只说要什么,比如「编号是 1024 的那一行」,不说怎样找,用不用索引由数据库自己决定。
索引也可以建在别的列上。要找用到番茄的菜,就给配料表的配料名一列建一份索引:写着番茄的几项排在一起,对半找到第一项,再顺着往下读。索引只帮得上按这一列找的查询;很小的表也不必建,直接翻反而更快。
代价是什么
书末的索引这个比方有一处不成立:书印好以后不再变,索引编一次就够;表却随时在变。每新增或删除一行,或者改到被索引的那一列,数据库都要顺手改索引:先在索引里对半找到位置,300 万项也要看二十来次,再写进或删去一笔。每多一个索引,就多一份这样的工作,写入就慢一些。
索引本身也要占硬盘,表越大,它也越大。所以索引不是越多越好:常按某一列找、每次只要其中几行,才值得为它建一个;写得多、很少按它找的列,就不必建。
资料来源
以下资料均于 2026-10-23 访问。