为什么具有极少数唯一值的列上的索引无效?

时间:2013-06-26 00:38:26

标签: database

因此,大多数数据库专家表示,相对于表的大小,在具有非常少的唯一值的列上创建索引是无效的。

基于数据库内部如何工作(我知道大多数数据库使用B树存储索引),为什么具有很少唯一值的B-Tree会使搜索效率低下?

4 个答案:

答案 0 :(得分:16)

首先,您需要了解列上的索引是如何工作的。简单来说,它只不过是,

  

给定列中所有可能值的有序列表,其中指针返回到数据库中的实际记录。

由于它是有序的,因此可以使用二进制搜索,而不是线性搜索,这可以提高大型数据集的性能。

想象一下,你的索引作为电话簿按列排序,比如last name;但是在具有类似last name的记录集中,记录没有共同的模式或有意义的顺序:它们是纯粹随机的。并说我们需要搜索此记录:

  

Ike Smith 4783 Random Ave.西雅图,WA 98117

由于电话簿是按last name订购的,我们只需要转到S,然后转到m,然后转到i等,直到我们找到Smith。并且(希望)Smith下只有几条记录,所以我们可以很快找到我们想要的记录。

现在,假设您有city而不是last name订购的电话簿。在与给定city匹配的记录中,没有特定的顺序。所以我们再次尝试搜索。但是,一旦我们找到Seattle(使用非常复杂的二进制搜索),我们就会留下接近620,778条记录,我们 按顺序检查,因为它们完全随机排序。我们一直在检查每个单个条目以查找我们想要的记录。

当您使用非常常见的列作为索引的基础时会发生这种情况:二进制搜索返回一组非常大的可能记录,数据库无法在初始索引列值之外做出任何假设,因此它需要在结果集中按顺序检查匹配记录。

如果电话簿是由zip code(一个不常见的变量)订购的,那么您可能会发现自己只搜索位于98117的18,623条记录。

此外,真正的电话簿通常类似于复合索引:而不是仅按单个列排序(即last name),结果集中的值然后由另一列排序(比如{{1} }),然后另一个(first name?)所以搜索可以在每个步骤线性完成,直到找到所需的记录。它基本上是索引中的索引,即使第一列不常见,与第二列的组合提供了足够的特定条件,只需要线性搜索一小组记录。

答案 1 :(得分:3)

通常,索引的目标是通过避免必须扫描表中的大部分数据来提供比线性搜索更快的速度(参见http://en.wikipedia.org/wiki/Database_index)。如果许多可能的索引值相同,即使成功完成索引查找,数据库也必须扫描表的重要部分。

因此,具有很少唯一值的索引将提供与其实现无关的非常小的性能优势。

答案 2 :(得分:2)

当您的唯一值很少时,哈希(如果您使用哈希表)对于许多条目将是相同的并且不提供加速。使用b树,范围内的许多条目将非常小。基本上,当您遇到非唯一值时,您必须返回更多条目作为结果或使用更多条件来搜索数据库
由于保证主键具有所有唯一值,因此通常将其编入索引 一个很好的例子是考虑所有值相同的最坏情况,在b树或散列表中,通过索引数据没有获得性能优势

答案 3 :(得分:2)

在b树中,索引与数据分开存储(至少在磁盘上)。对b-tree的搜索需要O(log n)进行查找,另一个O(1)用于搜索表本身。

在没有索引的情况下进行搜索时,通过扫描表格会遇到大的查找时间,即O(n)。但是,当匹配结果存储在整个表中时,索引上的查找通过执行表扫描超过(在资源方面)查找。

当您有许多可以与查询匹配的值时,您可以对索引进行O(log n)查找,并查找表数据本身。然后你几乎到达一个表扫描(因为大多数表的顺序查找都在扫描附近)所以索引的表扫描的小幅减少小于搜索索引本身的浪费。

更多细节: 必须重新定位磁头的树查找和寻道延迟(在硬盘上)每次匹配都会发生一次(使用简单的索引查找方法),而表扫描只发生一次。即使数据聚集在索引中,也必须进行计算和扫描,而查询优化器则选择表扫描。

请原谅这篇文章的糟糕组织,我是因为睡眠不足