最喜欢的性能调整技巧

时间:2008-08-20 20:45:15

标签: sql sql-server database performance

如果您的查询或存储过程需要进行性能调整,那么您首先要尝试的是什么?

29 个答案:

答案 0 :(得分:114)

以下是我总是给有人询问优化问题的方便清单 我们主要使用Sybase,但大多数建议都将全面适用。

例如,SQL Server附带了许多性能监视/调优位,但是如果你没有这样的东西(甚至可能没有),那么我会考虑以下内容......

99%的问题我看到的是因为连接中的表太多。对此的修复是执行连接的一半(使用某些表)并将结果缓存到临时表中。然后在该临时表上继续查询的其余部分。

查询优化核对表

  • 对基础表运行UPDATE STATISTICS
    • 许多系统将此作为计划的每周工作运行
  • 从基础表中删除记录(可能存档已删除的记录)
    • 考虑每天一次或每周一次。
  • 重建索引
  • 重建表(bcp数据输出/输入)
  • 转储/重新加载数据库(极端,但可能会修复损坏)
  • 构建新的,更合适的索引
  • 运行DBCC以查看数据库中是否存在损坏
  • 锁定/死锁
    • 确保数据库中没有其他进程在运行
      • 特别是DBCC
    • 您使用的是行级或页级锁定吗?
    • 在开始查询之前独占锁定表
    • 检查所有进程是否以相同的顺序访问表
  • 指数是否正确使用?
    • 如果两个表达式的数据类型完全相同,则联接将仅使用索引
    • 只有在索引中的第一个字段与查询匹配
    • 时才会使用索引
    • 是否在适当的地方使用聚集索引?
      • 范围数据
      • value1和value2之间的WHERE字段
  • 小联盟是很好的联盟
    • 默认情况下,优化器一次只会考虑表4。
    • 这意味着在连接超过4个表时,很有可能选择非最佳查询计划
  • 分手加入
    • 你可以分手吗?
    • 将外键预先选择到临时表中
    • 加入一半并将结果放入临时表
  • 您使用的是正确的临时桌吗?
    • #temp表可能比具有大量(数千行)的@table变量执行得更好。
  • 维护汇总表
    • 使用基础表上的触发器构建
    • 每日/每小时/等等。
    • 构建ad-hoc
    • 逐步构建或拆除/重建
  • 查看SET SHOWPLAN ON
  • 的查询计划
  • 查看SET STATS IO ON实际发生的情况
  • 使用pragma :( index:myindex)
  • 强制索引
  • 使用SET FORCEPLAN ON
  • 强制执行表格顺序
  • 参数嗅探:
    • 将存储过程分解为2
    • 从proc1调用proc2
    • 允许优化器在proc2中选择索引,如果proc1更改了@parameter
  • 你可以改进你的硬件吗?
  • 你几点跑?有更安静的时间吗?
  • Replication Server(或其他不间断进程)是否正在运行?你可以暂停吗?运行它,例如。每小时?

答案 1 :(得分:19)

  1. 非常了解在您脑海中运行查询的最佳路径。
  2. 检查查询计划 - 始终。
  3. 打开STATS,以便检查IO和CPU性能。专注于推动这些数字,不一定是查询时间(因为它可能会受到其他活动,缓存等的影响)。
  4. 查看进入运营商的大量行,但会出现少量行。通常,索引可以通过限制进入的行数来帮助(这节省了磁盘读取)。
  5. 首先关注最大的成本子树。更改该子树通常可以更改整个查询计划。
  6. 我见过的常见问题是:
    • 如果有很多连接,有时Sql Server会选择扩展连接,然后应用WHERE子句。您通常可以通过将WHERE条件移动到JOIN子句或具有内联条件的派生表来解决此问题。意见可能会导致同样的问题。
    • 次优联接(LOOP vs HASH vs MERGE)。我的经验法则是当顶行与底部相比只有很少的行时使用LOOP连接,当集合大致相等和有序时,使用MERGE,对其他所有内容使用HASH。添加连接提示可以让您测试理论。
    • 参数嗅探。如果您首先使用不切实际的值运行存储过程(例如,用于测试),则缓存的查询计划可能不是您的生产值的最佳值。再次使用RECOMPILE运行应验证这一点。对于一些存储过程,特别是那些处理不同大小范围的过程(例如,今天和昨天之间的所有日期 - 这将需要一个INDEX SEEK - 或者,去年和今年之间的所有日期 - 这将通过INDEX SCAN更好你可能每次都要用RECOMPILE运行它。
    • 糟糕的缩进...好吧,所以Sql Server没有这个问题 - 但我确实发现在我修复格式化之前无法理解查询。

答案 2 :(得分:18)

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

  • 对于高IO环境,请确保您的磁盘适用于RAID 10或RAID 0 + 1或某些嵌套的raid 1和raid 0实现。
  • 请勿使用低于1500K的驱动器。
  • 确保您的磁盘仅用于您的数据库。 IE没有记录没有操作系统。
  • 关闭自动增长或类似功能。让数据库使用预期的所有存储。不一定是目前正在使用的内容。
  • 设计类型查询的架构和索引。
  • 如果它是一个日志类型表(仅限插入)并且必须在DB中,则不要将其编入索引。
  • 如果您正在进行大量报告(复杂选择有多个联接),那么您应该考虑创建一个带星形或雪花模式的数据仓库。
  • 不要害怕复制数据以换取性能!

答案 3 :(得分:8)

CREATE INDEX

确保您的WHEREJOIN条款有可用的索引。这将极大地加速数据访问。

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

事务环境中,索引的数量应该更低,并且它们的定义更具战略性,以便索引维护不会拖累资源。 (索引维护是指必须更改索引的叶子以反映基础表中的更改,与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)

这是因为更少的汽车会满足价格点而不是颜色选择,这使得查询引擎可以分析更少的数据。

我已经知道有两个非常相似的索引,只是在字段顺序上有所不同,以加快一个中的查询(名字,姓氏)和另一个中的(姓氏,名字)。

答案 4 :(得分: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

答案 5 :(得分:5)

在这里假设MySQL,使用EXPLAIN来查找查询的内容,确保尽可能高效地使用索引并尝试消除文件排序。 High Performance MySQL: Optimization, Backups, Replication, and More是一本关于此主题的好书,MySQL Performance Blog

答案 6 :(得分:5)

@Terrapin isnull和coalesce之间还有一些值得一提的差异(除了ANSI合规性,这对我来说很重要)。

Coalesce vs. IsNull

答案 7 :(得分:3)

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

答案 8 :(得分:2)

使用WITH(NoLock)运行查询是我的标准操作。

。任何人在没有它的情况下捕获了数十GB表上运行的查询。

答案 9 :(得分:2)

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

declare @temp table(
    RowID int not null identity(1,1) primary key,
    SomeUniqueColumn varchar(25) not null,
    SomeNotUniqueColumn varchar(50) null,
    unique(SomeUniqueColumn)
)

答案 10 :(得分:2)

第一步: 查看查询执行计划!
TableScan - >坏
NestedLoop - > meh警告
TableScan背后的NestedLoop - > DOOM!

SET STATISTICS IO ON 设置统计时间

答案 11 :(得分: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

答案 12 :(得分:2)

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

答案 13 :(得分:2)

查看where子句 - 验证索引的使用/验证没有做任何愚蠢的事情

where SomeComplicatedFunctionOf(table.Column) = @param --silly

答案 14 :(得分: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时间,提取的行和从磁盘读取的块等内容。人们常常为错误的事情做出优化。

答案 15 :(得分:1)

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

我的同事使用函数调用(从userid获取lastlogindate作为示例)来返回非常广泛的记录集。

任务优化,我用函数的代码替换了sproc中的函数调用:我从>中获得了很多sprocs的运行时间。 20秒到< 1。

答案 16 :(得分:1)

删除任何不必要的游标。

答案 17 :(得分:1)

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

SELECT * FROM Orders (nolock) where UserName = 'momma'

答案 18 :(得分:1)

SET NOCOUNT ON

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

答案 19 :(得分:1)

我注意:

  • 展开任何CURSOR循环并转换为基于set的UPDATE / INSERT语句。
  • 请注意以下任何应用程序代码:
    • 调用返回大量记录的SP,
    • 然后在应用程序中,浏览每条记录并使用参数调用SP以更新记录。
    • 将其转换为可在一次交易中完成所有工作的SP。
  • 任何执行大量字符串操作的SP。这证明数据的结构不正确/正常化。
  • 重新发明轮子的任何SP。
  • 任何SP,我无法理解它在一分钟内要做的事情!

答案 20 :(得分:1)

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

答案 21 :(得分:1)

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

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

答案 22 :(得分:1)

按您过滤的clm(

)索引表格

答案 23 :(得分:1)

@ DavidM

  

在这里假设MySQL,使用EXPLAIN来查找查询的内容,确保尽可能高效地使用索引......

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

答案 24 :(得分:0)

Dirty reads -

set transaction isolation level read uncommitted

防止死锁,其中事务完整性不是绝对必要的(通常是正确的)

答案 25 :(得分:0)

不要使用“sp_”为存储过程名称添加前缀,因为系统过程都以“sp_”开头,并且SQL Server在调用时必须更难以​​查找过程。

答案 26 :(得分:0)

我喜欢用

isnull(SomeColThatMayBeNull, '')

coalesce(SomeColThatMayBeNull, '')

当我不需要coalesce给你的多参数支持时。

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

答案 27 :(得分:0)

  • 使用dbo前缀所有表格。防止重新编译。
  • 查看查询计划并寻找表/索引扫描。
  • 2005年,搜索缺失索引的管理视图。

答案 28 :(得分:0)

我总是首先使用SQL Profiler(如果它是具有大量嵌套级别的存储过程)或查询执行计划程序(如果它是一些没有嵌套的SQL语句)。有90%的时间可以使用这两种工具中的一种立即找到问题。