表可以拥有的最大有用索引数?

时间:2018-04-25 14:43:39

标签: sql postgresql query-optimization

会议

在上周的一次会议中,客户正在讨论如何更快地建立重要的搜索页面。该页面通过询问任何字段上的值(字符串)来搜索单个表(12列,2000万行);它返回50行(带分页),从指定的标准开始(每列可以是升序或降序)。当条件与现有索引不匹配时,搜索变慢,客户端不满意。

然后 - 在会议中间 - 半技术分析师将这一点抛到空中:为什么我们不在桌面上创建所有可能的索引以使一切变得快速? /强>

我立即回复了#34;不,有太多,这会使表格修改得很慢,所以我们需要创建几个巧妙选择的索引来做到这一点" 。我们最终创建了最有用的页面,现在页面速度更快了。问题解决了。

问题

但是仍然......我一直在思考这个问题,我希望能够更好地理解它,所以这就是:

理论上,我可以在包含N列的表上创建多少可能有用的索引?

我认为有用的我们应该考虑(我可能是错的):

  • 其他索引尚未涵盖的索引:例如,如果包含(a,b,c),则不应计算(a,b)。
  • 为了显示多行(不仅仅是相等),当它们是复合索引的一部分时,升序和降序索引应该被视为单独的索引。即:(a)与(DESC)的目的相同,但(a,b)的目的与(DESC,b)不同。

因此,具有单个列(a)的表只能有一个索引:

<DataTemplate DataType="{x:Type local:Person}">
    <TextBlock Foreground="Gold" Text="{Binding Path=Name}" />
</DataTemplate>

有两列(a,b)我可以有四个有用的索引:

(a)

有三列(a,b,c):

(a, b)
(b, a)
(a DESC, b)
(b DESC, a)
(a) -- already covered by #1
(b) -- already covered by #2
(a, b DESC) -- already coverred by #1 (reading index in reverse)
(b, a DESC) -- already covered by #2
(a DESC, b DESC) -- already covered by #3
(b DESC, a DESC) -- already covered by #4
(a DESC) -- already covered by #3
(b DESC) -- already covered by #4

3 个答案:

答案 0 :(得分:2)

我们假设您有一个包含a,b和c列的表格。

查询

select a from t where b = 1 order by c;

最佳索引在t(b,c,a)上,因为您首先使用b查找值,然后按c排序结果并在结果中包含a。

对于此查询:

select a from t where c = 1 order by b;

最佳指数在t(c,b,a)上。

对于此查询:

select b from t where c = 1 order by a;

最佳指数在t(c,a,b)上。

对于更多列,查询可能如下所示:

select a from t where b = 1 order by c, d, e;

并且您最希望获得t(b,c,d,e,a)的索引。

select a from t where b = 1 order by e, d, c;

你想要一个关于t(b,e,d,c,a)的索引。

因此,n列的最大有用索引数是n !,即所有排列。

这仅适用于光柱上的索引。正如Gordon Linoff在评论部分中提到的那样,你可能也需要函数索引(例如t(upper(a),lower(b))。有用的函数索引的数量在理论上是无限的。是的,Gordon是关于进一步的指数类型也是正确的。

所以最后的答案是理论上每个表的有用索引数是无限的。

答案 1 :(得分:2)

所有其他答案都包含一些有价值的东西,但是我有足够的说法要保证第三个答案。

你提出的问题没有确切的答案。在某种程度上,它就像是在问“什么超出限制,你会叫一个人疯狂?”有一个很大的灰色区域。

我的观点是:

  • 如果添加过多索引会发生什么:

    • 修改表格要慢得多。即使索引很少,数据操作也会变得慢一个数量级。如果您想要INSERTUPDATEDELETE,那么包含所有可能的索引的表格会使这样的操作变得非常缓慢。

    • 对于许多索引,查询计划程序必须考虑许多不同的访问路径,因此使用您添加的任何索引来规划查询的速度会稍慢。使用非常多的索引,很可能计划开销会使得查询在执行程序开始工作之前变得太慢。

  • 您可以做些什么来减少所需的索引数量:

    • 看看运营商。如果从未使用过运算符<<=>=>,则添加具有降序列的索引是没有意义的。

    • 请记住,(a, b, c)上的索引也可用于仅在其条件中使用a的查询,因此您不需要{{1}上的额外索引}}

  • 为您提供什么实用的方法?

    我有两点建议:

    1. 在您的十二列中添加简单索引的一种方法。

      十二个指数已经相当多了,但你还没有处于疯狂的范围内。

      PostgreSQL可以在具有多个列的条件的查询中有效地使用这些索引,即使单独的条件都不足以保证索引扫描。

      这是因为PostgreSQL有位图索引扫描。请参阅the documentation中的此示例:

      (a)

      扫描每个索引并形成一个位图,该位图对于与条件匹配的每一行包含1。然后组合位图,最后从表中提取行。

    2. 另一个想法是使用Bloom filter

      如果条件中唯一的运营商是EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100 AND unique2 > 9000; QUERY PLAN ------------------------------------------------------------------------------------- Bitmap Heap Scan on tenk1 (cost=25.08..60.21 rows=10 width=244) Recheck Cond: ((unique1 < 100) AND (unique2 > 9000)) -> BitmapAnd (cost=25.08..25.08 rows=10 width=0) -> Bitmap Index Scan on tenk1_unique1 (cost=0.00..5.04 rows=101 width=0) Index Cond: (unique1 < 100) -> Bitmap Index Scan on tenk1_unique2 (cost=0.00..19.78 rows=999 width=0) Index Cond: (unique2 > 9000) ,您可以

      =

      并在所有表格列上创建单个索引CREATE EXTENSION bloom;

      此类索引可用于USING bloom子句中的查询。缺点是它是一个有损的索引,所以你会得到误报结果,必须提取并过滤掉。

      这取决于您的情况,但这可能是一种优雅(且被低估!)的解决方案,可以平衡查询和更新速度。

答案 2 :(得分:1)

  

理论上,我可以在具有N列的表上创建多少可能的有用索引?

而不是理论上回答这个问题 实用答案要好得多。

要注意的第一点是应该避免所有顺序搜索(除非表格非常小​​)。 “非常小”,我的意思是,只有几行(比如说,最多10行)。 (但是,即使在这样的表中,也鼓励使用主键来强制执行唯一性。当然,这将作为索引实现。)

因此,如果客户端具有有效的搜索路径,则需要索引。如果现有索引达到目的,那就没问题;否则,很可能需要额外的索引。

根据我的经验,一个应用程序中的一个事务表有8个索引。客户坚持某些搜索路径,因此我们别无选择,只能提供它们。当然,我们告知客户端更新会变慢,但客户发现可以接受。实际上,更新期间的速度减慢并不明显。

这就是建议的方法 - 相应地警告客户。

在设计过程中,必须验证SQL语句是否使用索引搜索路径(对于每个访问过的表),而不是按顺序搜索。 ORACLE有一个工具,名为EXPLAIN PLAN。其他DB也应该有类似的工具。