写技术类博文,我们需要更严谨的精神--【非常抱歉我扯蛋了】
在5月1号,我写了一篇《软件系统性能优化策略--SQL优化》的博文,该文章的主题我主要是想说明在SQL优化中,首先要考虑索引优化,往往在建立索引之后会给你带来意想不到的效果。但是我在写结论的时候,这样写道:
1、在索引字段中使用OR或者IN
例:Select * from table1 where id in (2,3) 或者 Select * from table1 where id=2 or tid=3
这些写法都会让id索引失效而引起全表历遍,当然当数据没有达到海量的时候,你爱这么写都可以,只要实现功能,一旦达到海量便一个细节决定成败
解决方案一:
Select * from table1 where id = 2 UNION Select * from table1 where id = 3
当然大家也可以用别的写法来实现功能。
其实我在写条结论的时候,自己并没有去测试,因为在之前我看过很多网上的这个方面的技术博文,在IN和OR的阐述中,他们都在结论中写道会影响到索引。当时我也
依样画葫芦,写了这句话。
后来在博文发表之后,一位名叫 喳喳鸟 朋友这样评论:
我很明确的告诉你,这是扯淡,id in(2,3) 以及 or 的写法都能很好的利用索引而不会导致全表扫描。看到这里后面的我就不想看了以免被误导。
当时我看到这样的评论,心情很不爽,五一长假放弃休息,写篇博文和大家分享,却得来这样的评论,但是后来想想,对于这样的结论我是没有测试过,
如果因为我这样的误论而误导大家,我觉得要比我的心情更重要。
当时我就发了邮件给微软数据库方面专家,希望能得到他们的更权威的结论,可惜他们没有理我这个小菜鸟。那只能靠自己了。
第一步:首先创建一张100W数据的表
GO
/****** 对象: Table [dbo].[DepositTran] 脚本日期: 05/06/2010 21:15:34 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[DepositTran](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](20) COLLATE Chinese_PRC_CI_AS NOT NULL,
[Deposit] [float] NOT NULL,
[UpdateTime] [datetime] NOT NULL,
CONSTRAINT [PK_Transaction] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
declare @i int
set @i=1
while @i<=100000
begin
insert into DepositTran(Name,Deposit,UpdateTime) values('周菲菲',500,'2009-08-08')
set @i=@i+1
end
GO
declare @i int
set @i=1
while @i<=100000
begin
insert into DepositTran(Name,Deposit,UpdateTime) values('费佳杰',1100,'2009-09-18')
set @i=@i+1
end
GO
declare @i int
set @i=1
while @i<=100000
begin
insert into DepositTran(Name,Deposit,UpdateTime) values('周建飞',1500,'2009-09-28')
set @i=@i+1
end
GO
declare @i int
set @i=1
while @i<=300000
begin
insert into DepositTran(Name,Deposit,UpdateTime) values('金刚波',2500,'2009-12-08')
set @i=@i+1
end
GO
declare @i int
set @i=1
while @i<=400000
begin
insert into DepositTran(Name,Deposit,UpdateTime) values('马腾',3500,'2010-02-08')
set @i=@i+1
end
GO
DepositTran[存款交易表]
在该表中,ID主键是聚集索引,NAME是非聚集索引,Deposit 表示存款金额,UpdateTime 是交易时间
运行上面的脚本 我们就有了张百万数据的表了,
这时候,我们来介绍我们这次进行这次任务的SQL命名:
set statistics io on
set statistics time on
首先我们运行如下脚本:
首先我们来测试 where 条件 为“=”测试字段为ID聚集索引
dbcc freeproccache
set statistics io on
set statistics time on
Select ID,Name,Deposit,UpdateTime from DepositTran where id = 2
在消息框中显示
(1 行受影响)
表 'DepositTran'。扫描计数 0,逻辑读取 3 次,物理读取 3 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
SQL Server 执行时间:
CPU 时间 = 0 毫秒,占用时间 = 57 毫秒。
然后,我们来测试IN
dbcc freeproccache
set statistics io on
set statistics time on
Select ID,Name,Deposit,UpdateTime from DepositTran where id in(2)
在消息框中我们显示
(1 行受影响)
表 'DepositTran'。扫描计数 0,逻辑读取 3 次,物理读取 3 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
SQL Server 执行时间:
CPU 时间 = 0 毫秒,占用时间 = 43 毫秒。
注:
dbcc dropcleanbuffers
dbcc freeproccache
这二条命令将清除sql server的数据和过程缓冲区,这样能够使我们在每次执行查询时在同一个起点上,否则,每次执行查询得到的结果就不具有可比性了:
这里我先介绍一下:关于set statistics io的一些重要的测试指标
logical reads【逻辑读取】:
这是set statistics io或set statistics time命令提供的最有用的数据。我们知道,sql server在能对所有数据进行操作前,必须首先把数据读取到其数据缓冲区中。此外,我们也知道sql server何时会从数据缓冲区中读取数据,并把数据读取到大小为8k字节的页中。那么logical reads的意义是什么呢?logical reads是指sql server为得到查询中的结果而必须从数据缓冲区读取的页数。在执行查询时,sql server不会读取比实际需求多或少的数据,因此,当在相同的数据集上执行同一个查询,得到的logical reads的数字总是相同的。
为什么说在调节查询性能中知道sql server执行查询时的logical reads值是非常重要的呢?因为在每次执行同一查询时,这个数值是不会变化的。因此,在进行查询性能的调节时,这是个能用来衡量你的调节措施是否成功的一个非常好的标准。在对查询的性能进行调节时,如果logical reads值下降,就表明查询使用的服务器资源减少,查询的性能有所提高。如果logical reads值增加,则表示调节措施降低了查询的性能。在其他条件不变的情况下,一个查询使用的逻辑读越少,其效率就越高,查询的速度就越快。
physical reads【物理读取】
物理读指的是,在执行真正的查询操作前,sql server必须从磁盘上向数据缓冲区中读取他所需要的数据。在sql server开始执行查询前,他要作的第一件事就是检查他所需要的数据是否在数据缓冲区中,如果在,就从中读取,如果不在,sql server必须首先将他需要的数据从磁盘上读到数据缓冲区中。我们能想象得到,sql server在执行物理读时比执行逻辑读需要更多的服务器资源。因此,在最佳情况下,我们应当尽量避免物理读操作。
下面的这一部分听起来让人容易感到糊涂了。在对查询的性能进行调节时,能忽略物理读而只专注于逻辑读。你一定会纳闷儿,刚才不是还说物理读比逻辑读需要更多的服务器资源吗?情况确实是这样,sql server在执行查询时所需要的物理读次数不可能通过性能调节而减少的。减少物理读的次数是dba的一项重要工作,但他涉及到整个服务器性能的调节,而不仅仅是查询性能的调节。在进行查询性能调节时,我们不能控制数据缓冲区的大小或服务器的忙碌程度及完成查询所需要的数据是在数据缓冲区中还是在磁盘上,唯一我们能够控制的数据是得到查询结果所需要执行的逻辑读的次数。
因此,在查询性能的调节中,我们能心安理得地不理会set statistics io命令提供的physical read的值。(减少物理读次数、加快sql server运行速度的一种方式是确保服务器的物理内存足够多。)
3、CPU 时间 = 0 毫秒,占用时间 = 43 毫秒。
上面显示的信息表明,执行这次查询使用了多少cpu运行时间和运行查询使用了多少时间。cpu运行时间是对运行查询所需要的cpu资源的一种相对稳定的测量方法,和cpu的忙闲程度没有关系。不过,每次运行查询时这一数字也会有所不同,只是变化的范围没有总时间变化大。总时间是对查询执行所需要的时间(不计算阻塞或读数据的时间),由于服务器上的负载是在不断变化的,因此这一数据的变化范围有时会相当地大。由于cpu占用时间是相对稳定的,因此能使用这一数据作为衡量你的调节措施是提高了查询性能还是降低了查询的性能的一种方法。
其他的指标,大家可以去网上去查的定义,但是可能对优化没有什么意义,
从上面的解释我们可以看到,按照我之前的结论,IN和OR 会破坏索引的话,那它的逻辑读取应该不仅仅是‘3’
可是结论显示,不管我们是使用 = 还是IN 还是OR ,读取的次数都是一样的,运行的时间也是相差无几,
那就说明IN和OR 不会破快索引,而他们也是应用了索引这样的机制。
到了最后还是证明,我之前写的博文是扯蛋了,在这里我为我上次发表的博文《软件系统性能优化策略--SQL优化》深深的表示道歉,截止2010-05-06 22:58 一共有1202点击量,还有这些看了这篇文章的朋友,希望大家能谅解我的不谨慎。
我会一路既往的写博。