最喜欢的性能调整技巧
当您需要性能调优的查询或存储过程时,您尝试的第一件事是什么?
这里是我总是给某人问我优化的东西的方便清单。
我们主要使用Sybase,但大多数build议都将适用于所有的情况。
例如,SQL Server带有大量的性能监视/调整位,但是如果你没有这样的事情(甚至可能是这样),那么我会考虑以下几点:
我看到的99%的问题是由于在连接中放置太多的表而造成的。 解决这个问题的办法是做一半的连接(包括一些表),并将结果caching在临时表中。 然后执行join该临时表的其余查询。
查询优化清单
- 在基础表上运行UPDATE STATISTICS
- 许多系统将其作为计划的每周工作来运行
- 从基础表中删除logging(可能归档删除的logging)
- 考虑每天一次或每周一次自动执行此操作。
- 重build索引
- 重build表(bcp数据输出/input)
- 转储/重新加载数据库(激烈,但可能会修复腐败)
- build立新的,更合适的索引
- 运行DBCC以查看数据库中是否存在可能的损坏
- locking/死锁
- 确保没有其他进程在数据库中运行
- 特别是DBCC
- 你正在使用行或页面级locking?
- 在开始查询之前locking表格
- 检查所有进程是否以相同的顺序访问表
- 确保没有其他进程在数据库中运行
- 指数是否被恰当地使用?
- 如果两个expression式都是完全相同的数据types,联接将只使用索引
- 只有索引中的第一个字段在查询中匹配时才会使用索引
- 是否在适当的情况下使用聚集索引?
- 范围数据
- 值1和值2之间的字段
- 小联接是很好的join
- 默认情况下,优化器一次只考虑表4。
- 这意味着在连接超过4个表的情况下,它有很好的select非最优查询计划的机会
- 分解join
- 你能分手吗?
- 预先select外键到临时表中
- 做一半的连接,并把结果放在一个临时表中
- 你使用正确的临时表?
-
#temp
表可能会比大容量(数千行)的@table
variables执行得更好。
-
- 维护汇总表
- 在底层表上使用触发器构build
- build立每日/每小时/等
- build立特别的
- 逐步构build或拆卸/重build
- 使用SET SHOWPLAN ON查看查询计划的内容
- 看看SET STATS IO ON实际发生了什么
- 使用附注强制索引:(索引:myindex)
- 使用SET FORCEPLAN ON强制表格顺序
- 参数嗅探:
- 将存储过程分解为2
- 从proc1调用proc2
- 允许优化器在proc2中select索引,如果@参数已被proc1改变
- 你能改善你的硬件吗?
- 你什么时候跑? 有没有更安静的时间?
- Replication Server(或其他不间断进程)是否正在运行? 你可以暂停吗? 运行它,例如。 每小时?
- 有一个相当不错的主意运行查询的最佳path。
- 检查查询计划 – 总是。
- 打开STATS,以便您可以检查IO和CPU的性能。 专注于驱动这些数字,不一定是查询时间(因为这可能会受到其他活动,caching等的影响)。
- 寻找大量的行进入运营商,但小数字出来。 通常,索引可以通过限制进入的行数来节省磁盘读取数量。
- 首先关注最大的成本子树。 更改该子树通常可以更改整个查询计划。
- 我见过的常见问题有:
- 如果有很多连接,有时Sql Server会select扩展连接,然后应用WHERE子句。 您通常可以通过将WHERE条件移入JOIN子句或具有内联条件的派生表来解决此问题。 视图可能会导致相同的问题。
- 次最佳连接(LOOP vs HASH vs MERGE)。 我的经验法则是当顶部行与底部相比有很less的行时使用LOOP连接,当集合大致相等并且有序时为MERGE,对于其他所有部分都是HASH。 添加联接提示可以让你testing你的理论。
- 参数嗅探。 如果您首先运行带有不切实际值的存储过程(例如,用于testing),那么caching的查询计划对于您的生产值可能不是最理想的。 再次运行WITH RECOMPILE应该validation这一点。 对于一些存储过程,特别是那些处理不同大小范围的数据(比如今天和昨天之间的所有date – 这需要一个INDEX SEEK – 或者去年和今年之间的所有date),INDEX SCAN )你可能不得不每次都用RECOMPILE来运行它。
- 不好的缩进…好的,所以Sql Server没有这个问题 – 但我确定发现不可能理解一个查询,直到我修正了格式。
稍微偏离主题,但如果你有这些问题的控制权…
高水平和高影响。
- 对于高IO环境,请确保您的磁盘适用于RAID 10或RAID 0 + 1或一些嵌套的RAID 1和RAID 0。
- 请勿使用小于1500K的驱动器。
- 确保你的磁盘只用于你的数据库。 IE不logging没有操作系统。
- closures自动增长或类似的function。 让数据库使用所有预期的存储。 不一定是目前正在使用的。
- 为types查询devise模式和索引。
- 如果它是一个日志types表(只能插入),并且必须在数据库中不索引它。
- 如果你在分配报表(复杂select多个连接),那么你应该看看创build一个星形或雪花模式的数据仓库。
- 不要害怕复制数据来换取性能!
CREATE INDEX
确保你的WHERE
和JOIN
子句有可用的索引。 这将大大加快数据访问速度。
如果您的环境是数据集市或仓库,那么索引应该包含几乎所有可能的查询。
在事务环境中 ,索引的数量应该更低,定义更具战略性,以便索引维护不会拖拽资源。 (索引维护是在索引的叶子必须更改以反映基础表中的更改的情况下(与INSERT, UPDATE,
和DELETE
操作一样)。
另外,请注意索引中字段的顺序 – 字段越有select性(基数越高),索引越早出现。 例如,假设您正在查询二手车:
SELECT i.make, i.model, i.price FROM dbo.inventory i WHERE i.color = 'red' AND i.price BETWEEN 15000 AND 18000
价格普遍具有较高的基数。 可能只有几十种颜色,但很可能有数千种不同的要价。
在这些索引选项中, idx01
提供了更快的path来满足查询:
CREATE INDEX idx01 ON dbo.inventory (price, color) CREATE INDEX idx02 ON dbo.inventory (color, price)
这是因为更less的汽车将会比颜色select满足价格点,使查询引擎的数据分析的数量less得多。
我知道有两个非常相似的索引只有在字段顺序上有所不同,才能加快查询(名字,姓氏)的速度,而在另一个名字(姓氏,名字)中加快查询速度。
我最近学到的一个技巧是SQL Server可以在更新语句中更新本地variables以及字段。
UPDATE table SET @variable = column = @variable + otherColumn
或更可读的版本:
UPDATE table SET @variable = @variable + otherColumn, column = @variable
在实现recursion计算时,我用它来replace复杂的游标/连接,并且在性能上也获得了很多。
以下是在性能方面进行了很大改进的详细信息和示例代码: http : //geekswithblogs.net/Rhames/archive/2008/10/28/calculating-running-totals-in-sql-server-2005–the-optimal。 ASPX
假设MySQL在这里,使用EXPLAIN来查明查询是怎么回事,确保尽可能有效地使用索引,并尽量消除文件sorting。 高性能MySQL:与 MySQL性能博客一样,优化,备份,复制等等是关于这个主题的一本很好的书。
@Terrapin isnull和coalesce之间还有一些其他的区别值得一提(除了ANSI合规性,这对我来说是一个很大的区别)。
合并与IsNull
有时在SQL Server中,如果在where子句中使用OR,它将会真正起到性能的作用。 而不是使用OR只是做两个select,并把它们结合在一起。 你以1000倍的速度得到相同的结果。
看看where子句 – validation索引的使用/确认没有什么愚蠢的事情正在完成
where SomeComplicatedFunctionOf(table.Column) = @param --silly
我通常会从连接开始 – 我会一次一个敲出查询中的每一个,然后重新运行查询以获得一个想法,如果有一个特定的连接,我遇到了问题。
在我所有的临时表中,我喜欢添加唯一的约束(在适当的情况下)来创build索引和主键(几乎总是)。
declare @temp table( RowID int not null identity(1,1) primary key, SomeUniqueColumn varchar(25) not null, SomeNotUniqueColumn varchar(50) null, unique(SomeUniqueColumn) )
我已经习惯于总是使用绑定variables。 如果RDBMS不cachingSQL语句,则可能绑定variables将无济于事。 但是,如果您不使用绑定variables,那么RDBMS就没有机会重用查询执行计划和分析的SQL语句。 节省可以是巨大的: http : //www.akadia.com/services/ora_bind_variables.html 。 我主要使用Oracle,但Microsoft SQL Server的工作方式几乎相同。
根据我的经验,如果你不知道你是否使用绑定variables,你可能不是。 如果你的应用程序语言不支持它们,找一个。 有时,您可以通过对查询B使用绑定variables来修复查询A.
之后,我和我们的DBA交谈,以找出导致RDBMS最为痛苦的原因。 请注意,您不应该问“为什么这个查询很慢?” 这就像要求你的医生拿出你的附录。 确定你的问题可能是问题,但是其他问题也可能是错误的。 作为开发人员,我们倾向于根据代码行来思考。 如果一条线很慢,请修复该线。 但是RDBMS是一个非常复杂的系统,你的缓慢的查询可能是一个更大的问题的症状。
太多的SQL调整技巧是货运崇拜的偶像。 大多数情况下,问题与您使用的语法无关或最小程度相关,所以通常最好使用最简洁的语法。 然后你可以开始寻找调整数据库的方式(而不是查询)。 只有在失败时才调整语法。
像任何性能调整一样,总是收集有意义的统计数据 除非是您正在调整的用户体验,否则不要使用挂钟时间。 而是看看CPU时间,读取的行和从磁盘读取的块。 人们经常为错误的事情而优化。
第一步:查看查询执行计划!
桌面扫描 – >坏
NestedLoop – > meh警告
桌面背后的一个NestedLoop – > DOOM!
SET STATISTICS IO ON
设置统计时间
使用WITH(NoLock)运行查询在我的地方是非常标准的操作。 任何人在没有它的数十吉字节表上运行查询被取出并且被射击。
如果可能,将NOT IN查询转换为左外连接。 例如,如果要查找表1中未被表2中的外键使用的所有行,可以这样做:
SELECT * FROM Table1 WHERE Table1.ID NOT IN ( SELECT Table1ID FROM Table2)
但你用这个得到更好的performance:
SELECT Table1.* FROM Table1 LEFT OUTER JOIN Table2 ON Table1.ID = Table2.Table1ID WHERE Table2.ID is null
@ DavidM
假设MySQL在这里,使用EXPLAIN来查明查询是怎么回事,确保尽可能有效地使用索引…
在SQL Server中,执行计划可以得到同样的结果 – 它告诉你哪些索引正在被命中等等。
通过您筛选的clm(s)索引表(s)
不一定是SQL性能技巧本身,但肯定相关:
一个好主意是在可能的情况下使用memcached,因为直接从内存中获取预编译的数据比从数据库中获取要快得多。 还有一种内置memcached的MySQL(第三方)。
确保你的索引长度尽可能小。 这允许数据库一次从文件系统读取更多的密钥,从而加快了连接速度。 我认为这适用于所有数据库,但是我知道这是MySQL的一个具体build议。
我期待:
- 展开任何CURSOR循环并转换成基于集合的UPDATE / INSERT语句。
- 注意任何应用程序代码:
- 调用一个返回大量logging的SP,
- 然后在应用程序中,遍历每条logging并调用带有参数的SP来更新logging。
- 将其转换成一个SP,完成一个事务中的所有工作。
- 任何执行大量string操作的SP。 这是数据结构不正确/规范化的证据。
- 任何SP重新发明车轮。
- 任何SP,我不明白它在一分钟内试图做什么!
SET NOCOUNT ON
通常我的存储过程里面的第一行,除非我真的需要使用@@ROWCOUNT
。
在SQL Server中,使用nolock指令。 它允许select命令完成而无需等待 – 通常是其他事务完成。
SELECT * FROM Orders (nolock) where UserName = 'momma'
将游标移除到不需要的地方。
删除Sprocs中的很多行将调用该函数的函数调用。
我的同事使用函数调用(从userid获取lastlogindate作为例子)返回非常宽的logging集。
在优化任务中,我使用函数的代码replace了sproc中的函数调用:我将许多sprocs的运行时间从大于20秒降低到<1。
- 用dbo前缀所有表。 以防止重新编译。
- 查看查询计划并search表/索引扫描。
- 2005年,查清缺失指标的pipe理意见。
我喜欢用
isnull(SomeColThatMayBeNull, '')
过度
coalesce(SomeColThatMayBeNull, '')
当我不需要coalesce给你的多重参数支持时。
http://blog.falafel.com/2006/04/05/SQLServerArcanaISNULLVsCOALESCE.aspx
请勿将存储过程名称作为“sp_”的前缀,因为系统过程全部以“sp_”开头,而SQL Server在被调用时必须更难search。
脏读 –
set transaction isolation level read uncommitted
防止事务完整性不是绝对必要的死锁(通常是这样)
我总是先去SQL Profiler(如果它是一个有很多嵌套级别的存储过程),或者查询执行规划器(如果它是一些没有嵌套的SQL语句)。 90%的时间你可以立即用这两个工具之一find问题。