17

我一直采用的方法是首先使用最少的索引集部署数据库,然后根据性能要求添加/更改索引。

这种方法相当有效。但是,它仍然没有告诉我在哪里可以提高性能。它只告诉我哪里的性能如此糟糕以至于用户抱怨它。

目前,我正在重构我们许多应用程序上的数据库对象。

那么,既然“过早的优化是万恶之源”,我不应该费心去寻找性能改进吗?

在重构应用程序代码时,开发人员一直在寻找提高代码质量的方法。有没有办法不断寻求数据库性能的改进?如果是这样,您发现哪些工具和技术最有帮助?

我曾短暂地玩过“数据库引擎调优顾问”,但根本没有发现它有帮助。也许我只是需要更多的经验来解释结果。

4

14 回答 14

11

我的方法是使用 SQL Server Profiler 将针对服务器或数据库的命令收集到一个表中。一旦你有了它,你就可以根据最大和平均执行时间、最大和平均 cpu 时间以及(也非常重要的)查询运行的次数进行查询。

由于我尝试将所有数据库访问代码放在存储过程中,因此我很容易分解查询。如果您使用内联 SQL,它可能会更难,因为更改查询中的值会使它看起来像一个不同的查询。您可以尝试使用 LIKE 运算符来解决此问题,将相同类型的查询放入相同的存储桶中以计算聚合(最大值、平均值、计数)。

一旦你有了潜在问题的“前 10 名”列表,你就可以开始逐个查看它们,看看是否可以重新设计查询、索引是否有帮助,或者进行小的架构更改是否合适。要得出前 10 名,请尝试以不同的方式查看数据:平均 * 计算期间的总成本,最大违规者,普通平均等。

最后,如有必要,请务必在不同的时间段内进行监控。早上每个人都进入并运行他们的每日报告时,数据库的使用可能与中午用户输入新数据时不同。您还可以决定,即使某些夜间进程比任何其他查询花费的时间更长,也没有关系,因为它是在非工作时间运行的。

祝你好运!

于 2008-09-19T16:33:45.760 回答
11

“过早优化是万恶之源”

在数据库编程方面,我认为这句话是无稽之谈。重新编写整个应用程序非常昂贵,因为您的开发人员不关心第一次编写高效的代码。所有的 t-sql 代码都应该从它如何影响数据库性能的角度来考虑(当然,数据完整性是第一位的)。除了数据完整性之外,性能应该胜过一切。

是的,有一些优化的事情在你遇到问题之前是不应该做的,但有些事情应该理所当然地完成,以后不要修复。编写具有更高效率的代码不需要更多时间,而一旦您了解如何使用糟糕的代码影响效率,就不会花费更多时间。Cervo 对光标代码的讨论就是一个例子。基于集合的操作几乎总是比游标解决方案快得多,因此当基于集合的解决方案可以使用时,最初不应该编写游标。编写基于集合的解决方案几乎总是花费我更少的时间来编写游标,但获得这种方式的唯一方法是永远不要编写游标。

并且没有理由使用 select * 而不是指定您的字段名称。在 MSSQL 中,您可以将这些名称从对象资源管理器中拖过来,这样您就不能告诉我这样做太难了。但是通过仅指定您实际需要的字段,您可以节省网络资源和数据库服务器资源以及 Web 服务器资源。那么,程序员为什么要选择懒惰的选择 * 并担心以后的优化呢?

索引也是如此。你说你做了一组最小的索引。根据您定义最小的方式,这可能没问题,但是在所有外键上都有索引是至关重要的,我不想推送一个在最常见的几个字段上没有索引的数据库 where条款。如果您的用户是外部客户而不是内部客户,他们不会抱怨您的网站有多慢,他们会去其他地方。从一开始就计划有效的数据库访问只有在商业上才有意义。

我对未能从一开始就考虑效率的主要担忧之一是,最初几次事情太慢的时候,公司往往只是在问题上投入更多的设备而不是性能调整。当人们开始进行性能调优时,您已经拥有了数 GB 或更多的数据库,其中有许多不满意的客户,他们得到的超时多于结果。在这一点上,数据库中的几乎所有内容通常都必须重新编写,同时您正在失去客户。我记得在一家公司的商业应用程序中提供支持,客户服务代表在试图通过电话帮助已经不满的客户时,实际上需要十分钟才能从一个屏幕移动到另一个屏幕。

于 2008-09-19T18:17:49.597 回答
5

SQL Server 执行计划!!!去这里:http ://dbalink.wordpress.com/2008/08/08/dissecting-sql-server-execution-plans-free-ebook/

于 2008-09-19T16:31:39.337 回答
2

分析后,将您认为麻烦的查询放入 SQL 查询分析器并显示执行计划。识别执行代价高昂的表扫描的查询部分并重新索引这些表以最小化此成本。

试试这些参考:

优化 SQL
如何优化查询

于 2008-09-19T16:29:52.963 回答
1

分析您的查询,不是显而易见的查询,而是访问不同表、视图等的复杂查询和/或从不同表返回许多行的查询

这会告诉你应该把重点放在哪里

于 2008-09-19T16:25:58.447 回答
1

分析是关键,但是在使用分析集时,您必须确保它是准确的数据测试集,否则调整工具将无法为您提供所需的准确结果。

此外,2005 年使用报告的碎片化管理对象也非常有用!

于 2008-09-19T16:29:10.653 回答
1

当然,您必须分析您的查询并查看执行计划。但是一遍又一遍地出现的两个主要事情是尽可能快地过滤掉,并尽量避免使用游标。

我看到一个应用程序,其中有人将整个数据库事件表下载到客户端,然后根据某些条件逐行过滤每一行。在将过滤条件传递给数据库并让查询在 where 子句中应用条件方面,性能有了巨大的提升。这对于使用数据库的人来说是显而易见的,但我也看到过类似的事情。此外,有些人的查询存储了一堆临时表,其中充满了他们不需要的行,然后在临时表的最终连接中将其消除。基本上,如果您从填充临时表的查询中消除,那么其余查询的数据就会更少,整个查询运行得更快。

光标很明显。如果您有一百万行并逐行进行,那么它将永远存在。做一些测试,如果你即使使用 Perl 这样“慢”的动态语言连接到数据库,并对数据集执行一些逐行操作,速度仍然会比数据库中的游标快得多。使用 Java/C/C++ 之类的东西,速度差异更大。如果您可以在数据库代码中找到/消除游标,它将运行得更快......如果您必须使用游标,用任何编程语言重写该部分并将其从数据库中取出可能会产生巨大的性能提升。

关于游标的另一个注意事项,注意像 SELECT @col1 = col1, @col2 = col2, @col3 = col3 这样的代码,其中 id = @currentid 在循环中遍历 ID,然后在每一列上执行语句。基本上这也是一个游标。不仅如此,使用真正的游标通常比这更快,尤其是静态和 forward_only。如果您可以将操作更改为基于设置,它将更快......话虽如此,光标在某些事情上占有一席之地......但从性能的角度来看,使用它们基于集合是有代价的方法。

还要注意执行计划。有时它估计需要几秒钟的操作非常昂贵,而需要几分钟的操作非常便宜。查看执行计划时,请确保通过在代码中插入一些 SELECT 'At this area', GETDATE() 来检查所有内容。

于 2008-09-19T16:44:57.590 回答
1

我的建议是,在这种情况下,“过早的优化是万恶之源”是一派胡言。

在我看来,这一切都与设计有关——在设计数据模式时,您需要考虑并发性、热点、索引、缩放和使用模式。

如果您不知道需要哪些索引以及如何在不进行分析的情况下立即配置它们,那么您已经失败了。

有数百万种优化查询执行的方法,这些方法都很好,但最终数据会落在您告诉它的位置。

于 2008-11-23T21:54:47.653 回答
1

在数据库中的表列中应用适当的索引

  • 确保数据库中的每个表都有一个主键。

这将确保每个表都创建了一个聚集索引(因此,表的相应页面在磁盘中根据主键字段进行物理排序)。因此,任何使用主键从表中检索数据的操作,或对主键字段或 where 子句中指定的任何主键值范围的任何排序操作,都会非常快速地从表中检索数据。

  • 在列上创建非聚集索引

    常用于搜索条件。

    用于连接其他表。

    用作外键字段。

    具有高选择性(从特定值的总行数中返回低百分比 (0-5%) 行的列)。

    在 ORDER BY 子句中使用。

不要在 SQL 查询中使用“SELECT*”

可能会获取不必要的列,这会增加数据检索时间的费用。数据库引擎无法利用“覆盖索引”的优势,因此查询执行缓慢。

例子:

SELECT Cash, Age, Amount FROM Investments; 

代替:

SELECT * FROM Investments;

尽量避免在 Select 语句中使用 HAVING 子句

HAVING 子句用于在选择所有行之后过滤行,并且用作过滤器。尽量不要将 HAVING 子句用于任何其他目的。

例子:

SELECT Name, count (Name) FROM Investments WHERE Name!= ‘Test’ AND Name!= ‘Value’ GROUP BY Name;

代替:

SELECT Name, count (Name) FROM Investments GROUP BY Name HAVING Name!= ‘Test’ AND Name!= ‘Value’ ;

尽量减少查询中子查询块的数量

有时我们的主查询中可能有多个子查询。我们应该尽量减少查询中子查询块的数量。

例子:

SELECT Amount FROM Investments WHERE (Cash, Fixed) = (SELECT MAX (Cash), MAX (Fixed) FROM Retirements) AND Goal = 1; 

代替:

SELECT Amount FROM Investments WHERE Cash = (SELECT MAX (Cash) FROM Retirements) AND Fixed = (SELECT MAX (Fixed) FROM Retirements) AND Goal = 1;

避免 SELECT 列表中不必要的列和连接条件中不必要的表

在 Select 查询中选择不必要的列会增加实际查询的开销,尤其是当不必要的列是 LOB 类型时。在连接条件中包含不必要的表会迫使数据库引擎检索和获取不必要的数据并增加查询执行时间。

不要在子查询中使用 COUNT() 聚合来进行存在性检查

当您使用 COUNT() 时,SQL Server 不知道您正在执行存在检查。它通过执行表扫描或扫描最小的非聚集索引来计算所有匹配值。当您使用 EXISTS 时,SQL Server 知道您正在执行存在性检查。当它找到第一个匹配值时,它返回 TRUE 并停止查找。

尽量避免连接两种类型的列

在不同数据类型的两列之间连接时,必须将其中一列转换为另一列的类型。类型较低的列是被转换的列。如果您要连接类型不兼容的表,其中一个可以使用索引,但查询优化器不能在它转换的列上选择索引。

尽量不要使用 COUNT(*) 获取表中的记录数

要获取表中的总行数,我们通常使用以下 Select 语句:

SELECT COUNT(*) FROM [dbo].[PercentageForGoal]

此查询将执行全表扫描以获取行数。以下查询不需要全表扫描。(请注意,这可能不会始终为您提供 100% 完美的结果,但这仅在您不需要完美计数时才很方便。)

SELECT rows FROM sysindexes
WHERE id = OBJECT_ID('[dbo].[PercentageForGoal]') AND indid< 2

尝试在查询中适当地使用 EXISTS、IN 和 JOINS 等运算符

  • 通常 IN 的性能最慢。
  • IN 是有效的,只有当大多数用于选择的过滤条件都放在 SQL 语句的子查询中时。
  • 当用于选择的大多数筛选条件位于 SQL 语句的主查询中时,EXISTS 是有效的。

尽量避免动态SQL

除非确实需要,否则尽量避免使用动态 SQL,因为: 动态 SQL 很难调试和排除故障。如果用户向动态 SQL 提供输入,则存在 SQL 注入攻击的可能性。

尽量避免使用临时表

除非确实需要,否则尽量避免使用临时表。而是使用表变量。在 99% 的情况下,表变量驻留在内存中,因此速度要快得多。临时表驻留在 TempDb 数据库中。因此对临时表进行操作需要数据库间通信,因此会更慢。

代替 LIKE 搜索,使用全文搜索来搜索文本数据

全文搜索总是优于 LIKE 搜索。全文搜索将使您能够实现使用 LIKE 搜索无法实现的复杂搜索条件,例如搜索单个单词或短语(以及可选地对结果集进行排名)、搜索靠近另一个单词或短语词或短语,或搜索特定词的同义形式。实现全文搜索比 LIKE 搜索更容易实现(尤其是在搜索需求复杂的情况下)。

尝试使用 UNION 来实现“或”操作

尽量不要在查询中使用“OR”。而是使用“UNION”来组合两个有区别的查询的结果集。这将提高查询性能。如果不需要区分结果,最好使用 UNION ALL。UNION ALL 比 UNION 更快,因为它不必对结果集进行排序以找出可区分的值。

为大对象实现延迟加载策略

将大对象列(如 VARCHAR(MAX)、图像、文本等)存储在与主表不同的表中,并在主表中放置对大对象的引用。检索一次查询中的所有主表数据,如果需要加载大对象,则仅在需要时才从大对象表中检索大对象数据。

在用户定义的函数中实施以下良好实践

不要在存储过程、触发器、函数和批处理中重复调用函数。例如,您可能在过程的许多地方都需要字符串变量的长度,但不要在需要时调用 LEN 函数;相反,调用 LEN 函数一次,并将结果存储在变量中以备后用。

在触发器中实施以下良好实践

  • 尽量避免使用触发器。触发触发器并执行触发事件是一个昂贵的过程。
  • 永远不要使用可以使用约束来实现的触发器。
  • 不要对不同的触发事件(插入、更新和删除)使用相同的触发器。
  • 不要在触发器中使用事务代码。触发器始终在触发触发器的代码的事务范围内运行。
于 2017-08-29T12:49:59.223 回答
0

您似乎在谈论 MS SQL。

启动分析器并记录您在数据库上运行的最常见查询。然后在打开执行计划的情况下运行这些查询,您将看到是什么(如果有的话)减慢了您的查询速度。然后,您可以优化查询或在字段上添加更多索引。

SQL Books 将为您提供概要分析和查询分析功能的良好概述。

于 2008-09-19T16:27:37.553 回答
0

您可能想要检查当前索引的内部和外部框架,然后删除并重新创建它们或重新组织它们。

于 2008-09-19T16:35:37.050 回答
0

确保您正在使用生产量进行分析 - 就行数负载而言。查询及其计划在不同的负载/容量场景下表现不同

于 2008-09-19T16:41:44.430 回答
0

一般来说,这里的提示:

http://www.sql-server-performance.com/

过去对我来说质量很高且很有用。

于 2008-09-20T16:58:29.467 回答
-1

我的建议是从适用于所有数据库的技术开始,然后尝试特定于 MsSQL 的技术。

优化 SQL 是困难的,并且没有硬性规定。您可以遵循的通用准则很少,例如:

  • 95% 的性能改进将来自应用程序,而不是来自服务器或数据库引擎配置。
  • 先设计正确性,再调整性能
  • 减少访问数据库的次数
  • 尝试以适合您的数据模型的方式表达事物
  • 忽略关于性能的一般建议 - 是的,在某些时候,您会发现其中一个规则不适用的系统或 SQL 语句。

但关键是你应该始终应用 80-20 规则。这意味着在任何系统中,您都需要调整 20%(通常少得多)的代码才能获得最大的性能提升。这就是供应商提供的工具通常会失败的地方,因为他们通常无法猜测执行的应用程序/业务上下文。

于 2008-11-19T08:19:47.847 回答