堆上的等值搜索成本为0.5B*D?求技术解析
嘿,这个问题戳中了数据库存储里一个容易混淆的点,我来给你拆解清楚:
首先得明确:这里说的“堆”是数据库堆文件(Heap File),不是数据结构里的二叉堆/优先队列——堆文件就是完全无序、没有索引的记录集合,记录随机存在数据页面中,插入时直接找空闲位置存放,完全不维护顺序或唯一性。
先解释公式 0.5B*D 的由来
这个公式描述的是在堆文件中查找一个已知存在且唯一的键值记录的平均磁盘访问成本:
B是堆文件的总页面数,D是单次磁盘IO的平均时间- 为什么是
0.5B?因为堆文件完全无序,你不知道目标记录在哪,只能从第一个页面开始逐个扫描。假设目标记录均匀分布在所有页面中,运气好第一页就找到,运气差要扫到最后一页,平均下来就是总页数的一半,所以平均需要访问0.5B个页面,乘以单次IO时间D就是总成本。
解答你的几个疑问
“无索引无法确认唯一性,但资料说针对‘键(恰好匹配一条记录)’”
这里的前提是查询场景预先明确目标键只会匹配一条记录(比如业务上保证了唯一性,但堆文件本身没有索引来强制或验证这点)。这种情况下,你扫到包含目标记录的页面后就可以停止扫描,不需要遍历全量页面——这也是公式取平均0.5B的核心前提。如果是不确定唯一性、需要找出所有匹配记录的场景,那必须扫完所有页面,成本就是B*D了。“堆插入时不会扫描堆去重”
这完全符合堆文件的设计逻辑:堆文件的优势就是插入成本极低(找个空闲页直接放,最多1次IO),如果插入时要扫描全堆去重,那插入成本就变成了B*D,完全失去了堆文件的意义。这点和查询成本是两个独立的逻辑:插入不去重意味着堆里可能有重复键,但查询成本的公式是针对“找某一条特定记录”的场景,和堆里有没有重复无关——只要你知道要找的记录存在,平均扫一半页面就能找到。“已知row_id时查找速度应与键查找相当?”
这是完全错误的认知!row_id在大多数数据库里是物理定位标识,它直接包含了记录所在的页面号和页内偏移位置。用row_id查找时,根本不需要扫描任何页面,直接根据row_id定位到目标页面,只需要1次磁盘IO(成本是1*D),和盲扫键值的0.5B*D成本天差地别。两者的本质区别是:row_id是直接的物理定位信息,而键值查找在堆文件里是无引导的盲扫,完全不是一个场景。
总结一下:那个公式是有严格前提的——针对已知存在且唯一的键值,在无索引的堆文件中进行盲扫式等值查找的平均成本。如果脱离这些前提,公式就不适用了。
内容的提问来源于stack exchange,提问作者Dimitris

