会议
在上周的一次会议中,客户正在讨论如何更快地建立重要的搜索页面。该页面通过询问任何字段上的值(字符串)来搜索单个表(12列,2000万行);它返回50行(带分页),从指定的标准开始(每列可以是升序或降序)。当条件与现有索引不匹配时,搜索变慢,客户端不满意。
然后 - 在会议中间 - 半技术分析师将这一点抛到空中:为什么我们不在桌面上创建所有可能的索引以使一切变得快速? /强>
我立即回复了#34;不,有太多,这会使表格修改得很慢,所以我们需要创建几个巧妙选择的索引来做到这一点" 。我们最终创建了最有用的页面,现在页面速度更快了。问题解决了。
问题
但是仍然......我一直在思考这个问题,我希望能够更好地理解它,所以这就是:
理论上,我可以在包含N列的表上创建多少可能有用的索引?
我认为有用的我们应该考虑(我可能是错的):
因此,具有单个列(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
答案 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)
所有其他答案都包含一些有价值的东西,但是我有足够的说法要保证第三个答案。
你提出的问题没有确切的答案。在某种程度上,它就像是在问“什么超出限制,你会叫一个人疯狂?”有一个很大的灰色区域。
我的观点是:
如果添加过多索引会发生什么:
修改表格要慢得多。即使索引很少,数据操作也会变得慢一个数量级。如果您想要INSERT
,UPDATE
或DELETE
,那么包含所有可能的索引的表格会使这样的操作变得非常缓慢。
对于许多索引,查询计划程序必须考虑许多不同的访问路径,因此使用您添加的任何索引来规划查询的速度会稍慢。使用非常多的索引,很可能计划开销会使得查询在执行程序开始工作之前变得太慢。
您可以做些什么来减少所需的索引数量:
看看运营商。如果从未使用过运算符<
,<=
,>=
和>
,则添加具有降序列的索引是没有意义的。
请记住,(a, b, c)
上的索引也可用于仅在其条件中使用a
的查询,因此您不需要{{1}上的额外索引}}
为您提供什么实用的方法?
我有两点建议:
在您的十二列中添加简单索引的一种方法。
十二个指数已经相当多了,但你还没有处于疯狂的范围内。
PostgreSQL可以在具有多个列的条件的查询中有效地使用这些索引,即使单独的条件都不足以保证索引扫描。
这是因为PostgreSQL有位图索引扫描。请参阅the documentation中的此示例:
(a)
扫描每个索引并形成一个位图,该位图对于与条件匹配的每一行包含1。然后组合位图,最后从表中提取行。
另一个想法是使用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也应该有类似的工具。