128

当您有一个需要性能调整的查询或存储过程时,您首先尝试的是什么?

4

29 回答 29

114

这是我总是给问我优化问题的人提供的方便的花花公子清单。
我们主要使用 Sybase,但大多数建议将全面适用。

例如,SQL Server 带有许多性能监控/调整位,但如果你没有类似的东西(也许即使你有),那么我会考虑以下...

我见过的99% 的问题是由于在一个连接中放置了太多的表造成的。解决此问题的方法是执行一半的连接(与某些表)并将结果缓存在临时表中。然后在该临时表上执行其余的查询连接。

查询优化清单

  • 在基础表上运行 UPDATE STATISTICS
    • 许多系统将此作为计划的每周作业运行
  • 从基础表中删除记录(可能归档已删除的记录)
    • 考虑每天或每周自动执行一次。
  • 重建索引
  • 重建表(bcp 数据输出/输入)
  • 转储/重新加载数据库(剧烈,但可能会修复损坏)
  • 建立新的、更合适的索引
  • 运行 DBCC 以查看数据库中是否可能存在损坏
  • 锁/死锁
    • 确保没有其他进程在数据库中运行
      • 特别是 DBCC
    • 您使用的是行级还是页级锁定?
    • 在开始查询之前以独占方式锁定表
    • 检查所有进程是否以相同的顺序访问表
  • 是否正确使用了索引?
    • 仅当两个表达式的数据类型完全相同时,连接才会使用索引
    • 仅当索引上的第一个字段在查询中匹配时才会使用索引
    • 是否在适当的地方使用了聚集索引?
      • 范围数据
      • value1 和 value2 之间的 WHERE 字段
  • 小连接是很好的连接
    • 默认情况下,优化器一次只考虑 4 个表。
    • 这意味着在与超过 4 个表的连接中,它很有可能选择非最佳查询计划
  • 分解加入
    • 你能打破连接吗?
    • 将外键预选到临时表中
    • 做一半的连接并将结果放在临时表中
  • 您是否使用了正确的临时表?
    • #temp@table表的性能可能比具有大量(数千行)的变量好得多。
  • 维护汇总表
    • 在基础表上使用触发器构建
    • 每天/每小时/等构建。
    • 构建即席
    • 增量构建或拆卸/重建
  • 使用 SET SHOWPLAN ON 查看查询计划是什么
  • 使用 SET STATS IO ON 查看实际发生的情况
  • 使用 pragma 强制索引: (index: myindex)
  • 使用 SET FORCEPLAN ON 强制表顺序
  • 参数嗅探:
    • 将存储过程分成 2
    • 从 proc1 调用 proc2
    • 如果 @parameter 已被 proc1 更改,则允许优化器在 proc2 中选择索引
  • 你能改进你的硬件吗?
  • 你几点跑?有没有更安静的时间?
  • Replication Server(或其他不间断进程)是否正在运行?可以暂停吗?运行它,例如。每小时?
于 2008-09-19T15:53:33.253 回答
19
  1. 对在脑海中运行查询的最佳路径有一个很好的了解。
  2. 检查查询计划 - 总是。
  3. 打开 STATS,这样您就可以检查 IO 和 CPU 性能。专注于降低这些数字,不一定是查询时间(因为这可能会受到其他活动、缓存等的影响)。
  4. 寻找进入运算符的大量行,但出来的行数很少。通常,索引会通过限制进入的行数来帮助(这可以节省磁盘读取)。
  5. 首先关注最大成本子树。更改该子树通常会更改整个查询计划。
  6. 我见过的常见问题是:
    • 如果连接很多,有时 Sql Server 会选择扩展连接,然后应用 WHERE 子句。您通常可以通过将 WHERE 条件移动到 JOIN 子句或内联条件的派生表中来解决此问题。视图可能会导致同样的问题。
    • 次优连接(LOOP vs HASH vs MERGE)。我的经验法则是,当与底部相比,顶行的行数很少时使用 LOOP 连接,当集合大致相等且有序时使用 MERGE,其他所有内容使用 HASH。添加连接提示可以让您测试您的理论。
    • 参数嗅探。如果您首先使用不切实际的值运行存储过程(例如,为了测试),那么缓存的查询计划对于您的生产值可能不是最佳的。再次运行 WITH RECOMPILE 应该可以验证这一点。对于一些存储过程,尤其是那些处理不同大小范围的过程(例如,今天和昨天之间的所有日期 - 这将需要一个 INDEX SEEK - 或者,去年和今年之间的所有日期 - 使用 INDEX SCAN 会更好) 您可能必须每次都使用 RECOMPILE 运行它。
    • 缩进不好...好的,所以 Sql Server 对此没有问题 - 但我确实发现在修复格式之前无法理解查询。
于 2008-08-20T21:48:53.547 回答
18

稍微偏离主题,但如果你能控制这些问题……
高水平和高影响力。

  • 对于高 IO 环境,请确保您的磁盘用于 RAID 10 或 RAID 0+1 或 RAID 1 和 RAID 0 的某些嵌套实现。
  • 不要使用小于 1500K 的驱动器。
  • 确保您的磁盘仅用于您的数据库。IE 没有日志记录没有操作系统。
  • 关闭自动增长或类似功能。让数据库使用所有预期的存储。不一定是当前使用的。
  • 为类型查询设计架构和索引。
  • 如果它是一个日志类型表(仅插入)并且必须在数据库中,请不要对其进行索引。
  • 如果您正在分配报告(具有许多连接的复杂选择),那么您应该考虑使用星形或雪花模式创建数据仓库。
  • 不要害怕复制数据来换取性能!
于 2008-08-20T21:14:11.477 回答
8

CREATE INDEX

确保有可用于您的WHEREandJOIN子句的索引。这将大大加快数据访问速度。

如果您的环境是数据集市或仓库,那么几乎所有可以想到的查询都应该有大量索引。

事务环境中,索引的数量应该更少,它们的定义更具战略性,以便索引维护不会拖累资源。(索引维护是指必须更改索引的叶子以反映基础表中的更改,就像INSERT, UPDATE,DELETE操作一样。)

此外,请注意索引中字段的顺序 - 字段的选择性越强(基数越高),它应该出现在索引中的越早。例如,假设您要查询二手车:

SELECT   i.make, i.model, i.price
FROM     dbo.inventory i
WHERE    i.color = 'red'
  AND    i.price BETWEEN 15000 AND 18000

价格通常具有较高的基数。可能只有几十种颜色可供选择,但很可能有数千种不同的要价。

在这些索引选择中,idx01提供了更快的路径来满足查询:

CREATE INDEX idx01 ON dbo.inventory (price, color)
CREATE INDEX idx02 ON dbo.inventory (color, price)

这是因为与颜色选择相比,能够满足价格点的汽车更少,因此查询引擎要分析的数据要少得多。

众所周知,我有两个非常相似的索引,仅在字段顺序上有所不同,以加快查询(名字,姓氏)的速度和(姓氏,名字)的速度。

于 2008-08-28T03:34:35.140 回答
6

我最近学到的一个技巧是 SQL Server 可以在更新语句中更新局部变量和字段。

UPDATE table
SET @variable = column = @variable + otherColumn

或者更易读的版本:

UPDATE table
SET
    @variable = @variable + otherColumn,
    column = @variable

在实现递归计算时,我用它来替换复杂的游标/连接,并且在性能上也获得了很多。

以下是在性能方面取得显着改进的详细信息和示例代码:http: //geekswithblogs.net/Rhames/archive/2008/10/28/calculating-running-totals-in-sql-server-2005---the-optimal。 aspx

于 2009-01-23T17:42:07.383 回答
6

假设这里是 MySQL,使用 EXPLAIN 来找出查询发生了什么,确保尽可能有效地使用索引并尝试消除文件排序。High Performance MySQL: Optimization, Backups, Replication, and More 和MySQL Performance Blog一样是一本关于这个主题的好书。

于 2008-08-20T20:48:53.037 回答
5

@Terrapin isnull 和 coalesce 之间还有一些其他值得一提的区别(除了符合ANSI,这对我来说很重要)。

合并与 IsNull

于 2008-08-20T23:33:41.487 回答
4

有时在 SQL Server 中,如果在 where 子句中使用 OR,它确实会提升性能。而不是使用 OR 只是做两个选择并将它们联合在一起。您以 1000 倍的速度获得相同的结果。

于 2008-08-20T20:56:36.747 回答
3

查看 where 子句 - 验证索引的使用/验证没有做傻事

where SomeComplicatedFunctionOf(table.Column) = @param --silly
于 2008-08-20T20:48:24.037 回答
3

我通常会从连接开始 - 我将一次将它们中的每一个从查询中剔除,然后重新运行查询以了解是否存在我遇到问题的特定连接。

于 2008-08-20T20:52:02.297 回答
3

在我所有的临时表上,我喜欢添加唯一约束(在适当的情况下)来创建索引和主键(几乎总是)。

declare @temp table(
    RowID int not null identity(1,1) primary key,
    SomeUniqueColumn varchar(25) not null,
    SomeNotUniqueColumn varchar(50) null,
    unique(SomeUniqueColumn)
)
于 2008-08-20T21:00:37.377 回答
2

如果可能,将 NOT IN 查询转换为 LEFT OUTER JOINS。例如,如果您想查找 Table1 中未被 Table2 中的外键使用的所有行,您可以这样做:

SELECT *
FROM Table1
WHERE Table1.ID NOT IN (
    SELECT Table1ID
    FROM Table2)

但是您可以通过以下方式获得更好的性能:

SELECT Table1.*
FROM Table1
LEFT OUTER JOIN Table2 ON Table1.ID = Table2.Table1ID
WHERE Table2.ID is null
于 2009-01-30T16:08:33.200 回答
2

@大卫M

假设这里是 MySQL,使用 EXPLAIN 找出查询发生了什么,确保索引被尽可能有效地使用......

在 SQL Server 中,执行计划为您提供相同的功能 - 它告诉您正在命中哪些索引等。

于 2008-08-20T20:50:54.763 回答
2

不一定是 SQL 性能技巧本身,但肯定相关:

一个好主意是尽可能使用 memcached,因为直接从内存中获取预编译数据而不是从数据库中获取它会更快。还有一种内置了 memcached 的 MySQL(第三方)。

于 2008-08-20T20:56:56.357 回答
2

确保您的索引长度尽可能小。这允许数据库一次从文件系统中读取更多键,从而加快您的连接。我认为这适用于所有数据库,但我知道这是对 MySQL 的具体建议。

于 2008-08-20T21:01:10.323 回答
2

第一步:查看查询执行计划!
TableScan -> 错误的
NestedLoop -> 在 NestedLoop 后面的 meh 警告
TableScan -> DOOM!

设置统计信息 IO 开启
设置统计时间开启

于 2008-09-16T20:01:44.080 回答
2

我已经养成了始终使用绑定变量的习惯。如果 RDBMS 不缓存 SQL 语句,绑定变量可能无济于事。但是如果您不使用绑定变量,RDBMS 就没有机会重用查询执行计划和解析的 SQL 语句。节省的费用可能是巨大的:http ://www.akadia.com/services/ora_bind_variables.html 。我主要使用 Oracle,但 Microsoft SQL Server 的工作方式几乎相同。

根据我的经验,如果您不知道您是否使用绑定变量,那么您可能没有。如果您的应用程序语言不支持它们,请找到支持的语言。有时您可以通过对查询 B 使用绑定变量来修复查询 A。

之后,我与我们的 DBA 交谈,以找出导致 RDBMS 最痛苦的原因。请注意,您不应该问“为什么这个查询很慢?” 这就像要求您的医生取出您的阑尾一样。当然,您的查询可能是问题所在,但也有可能出现其他问题。作为开发人员,我们倾向于根据代码行来思考。如果线路很慢,请修复该线路。但是 RDBMS 是一个非常复杂的系统,您的缓慢查询可能是更大问题的征兆。

太多的 SQL 调优技巧是货物崇拜的偶像。大多数情况下,问题与您使用的语法无关或很少相关,因此通常最好使用最简洁的语法。然后,您可以开始寻找调整数据库(而不是查询)的方法。仅在失败时调整语法。

像任何性能调整一样,始终收集有意义的统计数据。不要使用挂钟时间,除非它是您正在调整的用户体验。相反,请查看 CPU 时间、获取的行和从磁盘读取的块等内容。人们经常为错误的事物进行优化。

于 2008-08-20T22:47:59.817 回答
2

使用 WITH (NoLock) 运行查询几乎是我的标准操作。任何人在没有它的情况下在数十 GB 的表上运行查询都会被取出并射击。

于 2008-09-22T14:26:08.303 回答
1

在 SQL Server 中,使用 nolock 指令。它允许选择命令完成而无需等待——通常是其他事务完成。

SELECT * FROM Orders (nolock) where UserName = 'momma'
于 2008-09-19T16:01:20.690 回答
1
SET NOCOUNT ON

通常是我的存储过程中的第一行,除非我真的需要使用@@ROWCOUNT.

于 2008-08-21T03:31:48.543 回答
1

在不需要的地方删除游标。

于 2008-08-26T14:52:46.103 回答
1

按您过滤的 clm 索引表

于 2008-08-20T20:52:43.930 回答
1
  • 使用 dbo 为所有表添加前缀。以防止重新编译。
  • 查看查询计划并寻找表/索引扫描。
  • 2005 年,对缺失索引的管理视图进行搜索。
于 2008-08-20T20:58:50.140 回答
1

我喜欢用

isnull(SomeColThatMayBeNull, '')

超过

coalesce(SomeColThatMayBeNull, '')

当我不需要合并为您提供的多参数支持时。

http://blog.falafel.com/2006/04/05/SQLServerArcanaISNULLVsCOALESCE.aspx

于 2008-08-20T21:03:53.000 回答
1

我留意:

  • 展开任何 CURSOR 循环并转换为基于集合的 UPDATE / INSERT 语句。
  • 寻找任何应用程序代码:
    • 调用返回大量记录的 SP,
    • 然后在应用程序中,遍历每条记录并调用带有参数的 SP 来更新记录。
    • 将其转换为在一个事务中完成所有工作的 SP。
  • 任何进行大量字符串操作的 SP。有证据表明数据的结构不正确/标准化。
  • 任何重新发明轮子的SP。
  • 任何我无法理解它在一分钟内试图做什么的 SP!
于 2008-08-20T22:12:15.987 回答
1

删除 Sprocs 中的函数调用,其中很多行将调用该函数。

我的同事使用函数调用(例如从 userid 获取 lastlogindate)来返回非常宽的记录集。

负责优化,我用函数代码替换了存储过程中的函数调用:我将许多存储过程的运行时间从 > 20 秒降低到 < 1。

于 2009-08-05T10:55:52.697 回答
0

我总是先去 SQL Profiler(如果它是一个有很多嵌套级别的存储过程)或查询执行计划器(如果它是一些没有嵌套的 SQL 语句)。90% 的情况下,您可以使用这两种工具之一立即发现问题。

于 2009-01-30T15:42:08.230 回答
0

Dirty reads -

set transaction isolation level read uncommitted

Prevents dead locks where transactional integrity isn't absolutely necessary (which is usually true)

于 2008-08-21T15:32:12.430 回答
0

不要在存储过程名称前加上“sp_”,因为系统过程都以“sp_”开头,当 SQL Server 被调用时,它必须更加努力地搜索才能找到您的过程。

于 2008-08-20T21:05:57.363 回答