添加数据库索引是否过早优化?


61

今天,我的一位同事建议我们仔细检查应用程序中的所有查询并相应地添加索引。

我觉得这是过早的优化,因为我们的应用程序尚未发布。我建议我们上线后监视慢速查询,然后相应地添加索引。

在设计数据库时,一般的共识是什么?每次编写新查询时都应添加匹配的索引吗?还是只监视并查看进展情况更好?


32
可能是一种见解,但是我认为可以先验地添加一些索引。
Basile Starynkevitch

2
@BasileStarynkevitch完全同意我们已经有了主键索引和工作。但是,您在哪里划清界线?
Marco de Jongh,2015年

1
我的经验是两分钱:我正在数据库的一个子集上测试一些早期搜索查询。我在本地副本上进行的测试完全正常。然后,我将应用程序推送到托管整个数据库的临时区域。我的测试运行时间少于500毫秒,而登台系统却花了几分钟才能解决。我的老板对于为什么不加载该应用程序感到非常困惑。解释型操作是您的朋友...至少至少要在大表上查找顺序扫描!
克里斯·西里菲斯

2
不添加索引就像使用Bubblesort。通常,在测试时不会发现任何问题,但是一旦程序开始实时扩展,您将面临很多问题。索引很容易使速度差异成100倍。
Pieter B

3
永远记住:索引不是神奇的事物,它可以加快查询速度。索引将确保大多数DML操作的成本,并且取决于类型,当许多人更新同一张表时,它可能导致大量等待。对于查询:许多查询根本无法从索引中受益,FTS最快,而Partitioning可以为您完成所有工作。-仅在您知道它们将对您有所帮助的地方添加索引!
法尔科

Answers:


132

由于模糊,直观的感觉,过早的优化正在“优化”某些东西,您可能知道,这可能会很慢,特别是对代码的可读性和可维护性不利。这并不意味着会故意不遵循公认的良好绩效规范。

有时候这很难画一条线,但是我肯定会说,在上线之前不添加任何索引是太迟的优化;这将惩罚早期采用者-您最渴望和最重要的用户-并给他们负面评价您的产品,然后他们会在评论,讨论等中广泛传播。监视查询以查找需要索引的痛点是好主意,但我会确保不迟于Beta测试。


11
是的,应该在负载测试阶段完成
Alvaro

152
在您知道慢零件在哪里之前进行优化就是过早的优化。在知道慢速零件在哪里之前放开东西是过早释放
MathematicalOrchid

4
@MathematicalOrchid:这是一个很好的措辞!我可以在其他地方借吗?
Pieter Geerkens 2015年

3
@PieterGeerkens当然,把自己踢出去!;-)我为91票以上的投票没有给我任何回报而感到遗憾。
MathematicalOrchid

3
@MathematicalOrchid应该是一个答案。可以运行“有史以来最小的直达点”的答案。
Mindwin'2

48

我们上线后监视慢速查询

因为没有什么能像让您的用户因缺乏设计而遭受苦难的质量了!

设计表时,应该知道哪些查询需要索引,知道在where子句和联接中要查询哪些列。这些应该已经被索引了,因为当负载或存储的数据增加时,在实时环境中可能看不见的内容很快就会变得显而易见。当发生这种情况时,您不想做的就是在每个“慢速”查询上打入索引,最终将得到所有内容的索引。


10
对。将索引视为数据库设计的一部分。使用索引可以避免对最终用户通常实时进行的任何查询进行全表扫描。
AE

1
@DocBrown我不太确定,当您设计表时,您已经(或应该)了解如何使用它。人员表将通过ID或姓来查询。如果有人开始通过DoB,地址或电话号码访问,那么您将为每个字段添加索引-结束于何处?
gbjbaanb 2015年

4
@gbjbaanb:当人们停止向产品中添加功能时结束,根据您的方法,该功能可能“永不”。
史蒂夫·杰索普

1
@SteveJessop我的意思是您根据要访问的主列建立索引。对于人员表,您可能具有搜索功能(例如,如果您忘记了用户名,则可以在电子邮件上进行搜索),但是之后始终使用ID。因此,ID是唯一需要索引的索引。如果您在其他字段上进行大量搜索,则可能需要一个索引,这会及时显示出来,但是通常您不希望为某列建立索引,因为某人有时决定编写非标准查询,但是您可能对这些“一次性”案件采用不同的机制。
gbjbaanb 2015年

2
@gbjbaanb:当然,人们不应该在表中重复查找相同的姓,因为与表的正确键相比,这对于他们来说是一个稍稍方便的句柄。我要说的是,是否在该表上使用姓氏索引都是这种情况,实际上,因为一段代码非常令人费解,这些代码假定所有代码都在“同一用户”上操作,但无法完全表达出来在代码中,通过记住ID :-)我在想象这样一种情况,直到客户提到它之前,才需要进行反向查找……
Steve Jessop 2015年

26

从贬义上讲,“过早的优化”意味着可能不需要的昂贵的优化。这并不意味着在防止破产前的所有可能的优化措施已实施!

特别是,在上线之前根据性能测试进行优化是合法的,以确保您可以满足一些合理的(尽管是近似的)要求,以使应用程序不会完全崩溃。

至少,您应该使用合理数量的测试数据加载数据库,并检查应用程序的响应能力。这还为时过早,因为您知道它会发生,并且它将捕获任何触发异常缓慢的扫描的查询。正如AE在评论中所说:

使用索引可以避免对最终用户通常实时进行的任何查询进行全表扫描

至少对于计划使用的表。

然后,作为捷径,如果您对数据库引擎有丰富的经验,并且在编写代码的第一部分时已经计划了测试,那么通常甚至在不运行它的情况下,您都知道查询是没有索引,写作将太慢。当然,您可以随意假装不知道,并且可以在添加索引使其通过之前观看测试失败,但是没有理由使已知的错误代码(因为没有响应)上线。


20

我觉得这是过早的优化,因为我们的应用程序尚未发布。我建议我们上线后监视慢速查询,然后相应地添加索引。

您不能像质量保证那样对待最终用户和生产环境。换句话说,您要在生产中解决这个问题。我认为这不是正确的方法,而且我每天都看到这种方法非常错误

您需要牢记一件事,因为您不能使用宽泛的画笔来绘制它。

常见的工作量是多少?

这听起来可能很明显或很乏味,但实际上却很重要。如果您有10个查询构成了您工作量的98%(非常普遍,信不信由你),我的建议是在生产之前进行艰难的分析。使用现实且具有代表性的数据,请确保这10个查询尽可能地完好(完美查询是在浪费宝贵的时间,并且几乎是无法实现的)。

对于其他200个查询(占工作量的2%),这些查询很可能不值得花很多精力,并且将弥补生产中故障排除的特殊情况。这也是现实,不是一件坏事。但这并不意味着忽略索引最佳实践或对数据检索进行估计的假设。

在生产之前确定数据库性能是一种常见且好的做法。实际上,这种类型的东西在开发DBA中有一个相对普遍的立场。

但...

有些人走得太远,疯狂地添加索引以防万一。有人建议这是缺少的索引吗?添加它,以及其他四个变体。也是个坏主意。您不仅需要考虑数据检索,还需要考虑数据修改吗?表上的索引越多,通常来说,修改数据时开销就越大。

像大多数事物一样,保持着健康的平衡。

作为一个有趣的小注意事项...“索引”的复数

“指数”适用于金融界人士

“索引”适合我们


2
这需要更多的选票。我完全同意。
RubberDuck

一为“以防万一”位(即是一个过早的优化)。如果可以的话,我会再次投票赞成“常见工作量”。
大卫

希望您事先知道哪10个查询属于98%,哪些不属于。
圣保罗Ebermann

@PaŭloEbermann大多数DBMS都可以非常快速,轻松地捕获该信息。在这种情况下,没有借口不知道。
托马斯·斯金格

@ThomasStringer当然,这仅在生产之前的测试用例与生产中实际用户所进行的操作相关的情况下才有效。
圣保罗Ebermann

4

不,这不是过早的优化,但必须正确执行,因为任何优化都应该如此。

这是我会做的:

  1. 用足够的测试数据加载数据库以模拟生产负载。您无法获得100%的准确度,但这很好:仅放入足够的数据。一张表中的数据量是否固定?加载它。您是否有一个表可以容纳大量数据,例如,该站点上有哪个表存在问题?即使只是虚拟数据,也要加载几百万条记录。
  2. 在数据库服务器中打开分析
  3. 使用自动化脚本(提供数量)和实际用户(他们知道如何破坏事物)的组合来破坏应用程序。
  4. 查看分析数据。特定查询速度慢吗?检查解释计划,看看数据库服务器告诉你它想要的索引,但它不存在。

数据库服务器是复杂而智能的软件。如果您会听,他们会告诉您如何优化他们。

关键是在优化前后评估性能,并让数据库告诉您它需要什么


3

遵循已知问题的经过验证的模式(例如通过其ID查找记录)并不为时过早。这是明智的。

也就是说,索引并不总是一件容易的事。在设计阶段通常很难知道您的流量将依赖哪个索引以及哪些瓶颈将成为写操作。因此,我主张利用一些“显而易见的”模式设计最佳实践(使用适合于设计的读/写模式和索引FK的PK);但是,除非压力测试要求,否则不要在其他任何索引上添加索引。


花费额外的30秒来做几乎可以肯定的事情,以提高性能,而几乎不会损害它,这不是“过早的优化”。如果表上90%的操作都使用特定的列作为键,那么对它建立索引将提高性能,或者性能永远不会变得很慢,而添加代码来创建索引所花费的时间可能比确定它是否为真的很有必要。
2015年

@supercat“ never” ...直到您开始在生产环境中看到死锁...
svidgen

您设想什么样的现实方案与使用列作为键的90%的操作一致,并且在哪里添加索引会导致死锁?
2015年

@supercat我不确定我是否完全理解您的要求。对于活动的应用程序,几乎任何执行时间或ios数量的增加都有可能引入死锁。...但是,更重要的是,在数据库达到临界大小和/或并发级别之前,大多数应用程序中索引的存在与否可以忽略不计。例如,当您所有的索引都不再适合内存时…
svidgen

1
关键是,要在典型用例通过压力测试之前(或者直到您看到生产中用户意外行为的问题),否则很难知道您的查询构成是什么。如果您有一个从tablex.fieldy键入的页面,但是每千次插入只被击中一次……索引可能会导致净降级。
svidgen

2

发布应用程序时,为时已晚。

但是任何适当的开发过程都应包括性能测试。

使用性能测试的结果来确定要添加的索引,并通过重复性能测试来验证其有效性。


在发布应用程序时,确实是调整索引的好时机。看一下这个站点,stachexchange,您可以肯定它的索引在上线很长时间之后已经发生了变化。
LosManos

@LosManos:无需付费即可使用Stack Exchange。
Lightness Races in Orbit

@LightnessRacesinOrbit:抱歉,广告客户需要付费才能使用Stack Exchange。

@JonofAllTrades:他们不在乎是否由于缺少索引而导致几个小时的性能下降。我的观点是,具有永久发行周期的大型,免费使用的面向社区的网站与定期发布的自包含商业产品非常不同。因此,SE不是一个很好的例子。
Lightness Races in Orbit

1

尽管我不认为每个查询都应该进行优化,但是索引在RDBMS中占据了很大一部分,因此在发布之前需要对其进行考虑。当您执行查询时,与其他形式的编程不同,您不会告诉系统如何执行查询。他们制定自己的计划,并且几乎总是基于索引的可用性。以后还会考虑数据的构成和数量。

这是我要考虑的一些事项:

  1. 您应该在早期开发中确定一些查询,这些查询您会经常使用。专注于他们。
  2. 查询将会很慢。通过首先为它们建立索引,然后可以确定性能是否仍然不够快,然后考虑进行重新设计(去规范化可能为时过早)。我宁愿在发布之前这样做。没有人想要一个需要10分钟才能在清单中找到东西的系统。
  3. 索引可以提高查询性能,但它们并不妨碍数据修改。
  4. 许多系统都有分析您的查询的工具,因此不要害怕使用它们。

初次审核后,您应该考虑一些注意事项,以决定何时应再次审核以及如何收集信息以进行此操作(监视使用情况,获取客户端数据的副本等)。

我知道您不想过早地进行优化,但是几乎可以肯定,如果不对数据库建立索引,则性能会很差。通过解决这个问题,您可以确定是否还有其他区域导致性能问题。


0

它还取决于您期望的用户数量。您绝对应该进行一些负载测试,并确保您的数据库可以满足10s至100s至1000s的并发请求。同样,这取决于您期望有多少流量,以及期望使用更多区域的资源。

通常,我会微调我希望用户最先击中的区域。然后,从用户体验的角度来看,我会微调所有缓慢的内容。每当用户不得不等待某事时,他们就会感到不愉快,甚至会被拒绝。不好!


0

通过一些预先的分析来确定哪些列确实需要索引是一个好习惯。如果您绝对没有索引,那么随着数据库大小的增加,生产中确实存在逐渐或意外的性能下降的风险。您要避免的情况是,通常运行的查询需要扫描大量表行。将索引添加到关键列并不是过早的优化,因为您拥有许多可用的必要信息,并且潜在的性能差异非常大(数量级)。在某些情况下,索引的好处不太明确或更多地取决于数据-您可能可以推迟对其中某些情况的决定。

您需要问的一些问题是:

  • 每个表的大小的设计限制是多少?

如果表总是很小(例如<100行),那么数据库必须扫描整个表就不会造成灾难。添加索引可能是有益的,但这需要更多的专业知识或度量来确定。

  • 每个查询多久运行一次,所需的响应时间是多少?

如果查询不经常运行并且没有严格的响应时间要求(例如,报告生成)并且行数不是很大,则推迟添加索引可能是相当安全的。同样,专业知识或度量可以帮助判断它是否将是有益的。

  • 查询是否需要除主键之外还通过其他方式查找表?例如按日期范围过滤,并加入外键?

如果这些查询经常运行并且触摸具有很多行的表,那么您应该认真考虑先占添加索引。如果不确定查询是否是这种情况,则可以用实际的数据量填充数据库,然后查看查询计划。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.