现在的位置: 首页 > 综合 > 正文

数据库性能调优1

2014年02月09日 ⁄ 综合 ⁄ 共 9236字 ⁄ 字号 评论关闭
 

数据库优化相关介绍
         最近为了解决日志系统性能瓶颈的问题,我查询了一些数据库优化的资料,现对这几天的学习做一个总结。数据库相关的优化是一个非常麻烦而且复杂的事情,他涉及到的方面很多,我在这方面并没有很多的经验,所以我们需要通过不同途径来了解这方面的知识。
 
. 优化SQL语句
         人们在使用SQL时往往会陷入一个误区,即太关注于所得的结果是否正确,而忽略了不同的实现方法之间可能存在的性能差异,这种性能差异在大型的或是复杂的数据库环境中(如联机事务处理OLTP或决策支持系统DSS)中表现得尤为明显。而不良的SQL往往来自于不恰当的索引设计、不充份的连接条件和不可优化的where子句。在对它们进行适当的优化后,其运行速度将会有明显地提高。下面是我收集到的一些关于SQL优化方面的介绍。
1.合理使用索引    
 索引是数据库中重要的数据结构,它的根本目的就是为了提高查询效率。现在大多数的数据库产品都采用IBM最先提出的ISAM索引结构。索引的使用要恰到好处,其使用原则如下:    
 ●在经常进行连接,但是没有指定为外键的列上建立索引,而不经常连接的字段则由优化器自动生成索引。    
 ●在频繁进行排序或分组(即进行group   by或order   by操作)的列上建立索引。    
 ●在条件表达式中经常用到的不同值较多的列上建立检索,在不同值少的列上不要建立索引。比如在雇员表的“性别”列上只有“男”与“女”两个不同值,因此就无必要建立索引。如果建立索引不但不会提高查询效率,反而会严重降低更新速度。    
 ●如果待排序的列有多个,可以在这些列上建立复合索引(compound   index)。    
 ●使用系统工具。如Informix数据库有一个tbcheck工具,可以在可疑的索引上进行检查。在一些数据库服务器上,索引可能失效或者因为频繁操作而使得读取效率降低,如果一个使用索引的查询不明不白地慢下来,可以试着用tbcheck工具检查索引的完整性,必要时进行修复。另外,当数据库表更新大量数据后,删除并重建索引可以提高查询速度。    
   
 2.避免或简化排序    
 应当简化或避免对大型表进行重复的排序。当能够利用索引自动以适当的次序产生输出时,优化器就避免了排序的步骤。以下是一些影响因素:    
 ●索引中不包括一个或几个待排序的列;    
 ●group   by或order   by子句中列的次序与索引的次序不一样;    
 ●排序的列来自不同的表。    
 为了避免不必要的排序,就要正确地增建索引,合理地合并数据库表(尽管有时可能影响表的规范化,但相对于效率的提高是值得的)。如果排序不可避免,那么应当试图简化它,如缩小排序的列的范围等。    
   
 3.消除对大型表行数据的顺序存取    
 在嵌套查询中,对表的顺序存取对查询效率可能产生致命的影响。比如采用顺序存取策略,一个嵌套3层的查询,如果每层都查询1000行,那么这个查询就要查询10亿行数据。避免这种情况的主要方法就是对连接的列进行索引。例如,两个表:学生表(学号、姓名、年龄……)和选课表(学号、课程号、成绩)。如果两个表要做连接,就要在“学号”这个连接字段上建立索引。    
 还可以使用并集来避免顺序存取。尽管在所有的检查列上都有索引,但某些形式的where子句强迫优化器使用顺序存取。下面的查询将强迫对orders表执行顺序操作:    
 SELECT      FROM   orders   WHERE   (customer_num=104   AND   order_num>1001)   OR   order_num=1008    
 虽然在customer_num和order_num上建有索引,但是在上面的语句中优化器还是使用顺序存取路径扫描整个表。因为这个语句要检索的是分离的行的集合,所以应该改为如下语句:    
 SELECT      FROM   orders   WHERE   customer_num=104   AND   order_num>1001    
 UNION    
 SELECT      FROM   orders   WHERE   order_num=1008    
 这样就能利用索引路径处理查询。    
   
 4.避免相关子查询    
 一个列的标签同时在主查询和where子句中的查询中出现,那么很可能当主查询中的列值改变之后,子查询必须重新查询一次。查询嵌套层次越多,效率越低,因此应当尽量避免子查询。如果子查询不可避免,那么要在子查询中过滤掉尽可能多的行。    
   
 5.避免困难的正规表达式    
 MATCHES和LIKE关键字支持通配符匹配,技术上叫正规表达式。但这种匹配特别耗费时间。例如:SELECT      FROM   customer   WHERE   zipcode   LIKE   “98_   _   _    
 即使在zipcode字段上建立了索引,在这种情况下也还是采用顺序扫描的方式。如果把语句改为SELECT      FROM   customer   WHERE   zipcode   >“98000”,在执行查询时就会利用索引来查询,显然会大大提高速度。    
 另外,还要避免非开始的子串。例如语句:SELECT      FROM   customer   WHERE   zipcode[2,3]   >“80”,在where子句中采用了非开始子串,因而这个语句也不会使用索引。  
   
 6.使用临时表加速查询    
 把表的一个子集进行排序并创建临时表,有时能加速查询。它有助于避免多重排序操作,而且在其他方面还能简化优化器的工作。例如:    
 SELECT   cust.name,rcvbles.balance,……other   columns    
 FROM   cust,rcvbles    
 WHERE   cust.customer_id   =   rcvlbes.customer_id    
 AND   rcvblls.balance>0    
 AND   cust.postcode>“98000”    
 ORDER   BY   cust.name    
 如果这个查询要被执行多次而不止一次,可以把所有未付款的客户找出来放在一个临时文件中,并按客户的名字进行排序:    
 SELECT   cust.name,rcvbles.balance,……other   columns    
 FROM   cust,rcvbles    
 WHERE   cust.customer_id   =   rcvlbes.customer_id     
 AND   rcvblls.balance>0    
 ORDER   BY   cust.name    
 INTO   TEMP   cust_with_balance    
 然后以下面的方式在临时表中查询:    
 SELECT      FROM   cust_with_balance    
 WHERE   postcode>“98000”    
 临时表中的行要比主表中的行少,而且物理顺序就是所要求的顺序,减少了磁盘I/O,所以查询工作量可以得到大幅减少。    
 注意:临时表创建后不会反映主表的修改。在主表中数据频繁修改的情况下,注意不要丢失数据。    
   
 7.用排序来取代非顺序存取    
 非顺序磁盘存取是最慢的操作,表现在磁盘存取臂的来回移动。SQL语句隐藏了这一情况,使得我们在写应用程序时很容易写出要求存取大量非顺序页的查询。    
 有些时候,用数据库的排序能力来替代非顺序的存取能改进查询。    
   8. 其它注意的细节
a).尽量避免反复访问同一张或几张表,尤其是数据量较大的表,可以考虑先根据条件提取数据到临时表中,然后再做连接。
b) 尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该改写;如果使用了游标,就要尽量避免在游标循环中再进行表连接的操作。
  C). 注意where字句写法,必须考虑语句顺序,应该根据索引顺序、范围大小来确定条件子句的前后顺序,尽可能的让字段顺序与索引顺序相一致,范围从大到小。
  d). 不要在where子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
  e. 尽量使用exists代替select count(1)来判断是否存在记录,count函数只有在统计表中所有行数时使用,而且count(1)比count(*)更有效率。
  f. 尽量使用“>=”,不要使用“>”。
  h. 注意一些or子句和union子句之间的替换
  i. 注意insert、update操作的数据量,防止与其他应用冲突。如果数据量超过200个数据页面(400k),那么系统将会进行锁升级,页级锁会升级成表级锁。
. 优化MS SQL Server 2000
3. MS SQL Server 2000 相关学习总结
3.1系统表
名称
地址
说明
sysaltfiles
主数据库
保存数据库的文件
syscharsets
主数据库
字符集与排序顺序
sysconfigures
主数据库
配置选项
syscurconfigs
主数据库
当前配置选项
sysdatabases
主数据库
服务器中的数据库
syslanguages
主数据库
语言
sysmessages
主数据库
查看当前@@ERROR的消息
syslogins
主数据库
登陆帐号信息
sysoledbusers
主数据库
链接服务器登陆信息
sysprocesses
主数据库
进程
sysremotelogins
主数据库
远程登录帐号
syscolumns
每个数据库
sysconstrains
每个数据库
限制
sysfilegroups
每个数据库
文件组
sysfiles
每个数据库
文件
sysforeignkeys
每个数据库
外部关键字
sysindexs
每个数据库
索引
sysmenbers
每个数据库
角色成员
sysobjects
每个数据库
所有数据库对象
syspermissions
每个数据库
权限
systypes
每个数据库
用户定义数据类型
sysusers
每个数据库
用户
 
1. 查询当前系统中有那些数据库可以使用
         Select * from master.dbo.sysdatabases
3.2系统存储过程
 
1. sp_help
         报告有关数据库对象(sysobjects 表中列出的任何对象)、用户定义数据类型或 Microsoft® SQL Server™ 所提供的数据类型的信息
示例
A. 返回有关所有对象的信息
下面的示例列出有关 sysobjects 中每个对象的信息。
         USE master
         EXEC sp_help
B. 返回有关单个对象的信息
下面的示例显示有关 publishers 表的信息。
         USE pubs
         EXEC sp_help publishers
2. sp_helpfile
         返回与当前数据库关联的文件的物理名称及特性。使用此存储过程确定附加到服务器或从服务器分离的文件名。
示例
下面的示例返回有关 pubs 中的文件的信息。
         USE pubs
         EXEC sp_helpfile
3.3全局变量
1.@@rowcount
返回受上一语句影响的行数。
示例
下面的示例执行 UPDATE 语句并用 @@ROWCOUNT 来检测是否有发生更改的行。
UPDATE authors SET au_lname = 'Jones'
WHERE au_id = '999-888-7777'
IF @@ROWCOUNT = 0
   print 'Warning: No rows were updated'
2.@@ERROR
返回最后执行的 Transact-SQL 语句的错误代码。
示例:用 @@ERROR 检测几条语句的成功
下面的示例取决于 INSERT 和 DELETE 语句的成功操作。局部变量在两条语句后均被设置为 @@ERROR 的值,并且用于此操作的共享错误处理例程中。
 
USE pubs
GO
DECLARE @del_error int, @ins_error int
-- Start a transaction.
BEGIN TRAN
 
-- Execute the DELETE statement.
DELETE authors
WHERE au_id = '409-56-7088'
 
-- Set a variable to the error value for
-- the DELETE statement.
SELECT @del_error = @@ERROR
 
-- Execute the INSERT statement.
INSERT authors
   VALUES('409-56-7008', 'Bennet', 'Abraham', '415 658-9932',
   '6223 Bateman St.', 'Berkeley', 'CA', '94705', 1)
-- Set a variable to the error value for
-- the INSERT statement.
SELECT @ins_error = @@ERROR
 
-- Test the error values.
IF @del_error = 0 AND @ins_error = 0
BEGIN
   -- Success. Commit the transaction.
   PRINT "The author information has been replaced"   
   COMMIT TRAN
END
ELSE
BEGIN
   -- An error occurred. Indicate which operation(s) failed
   -- and roll back the transaction.
   IF @del_error <> 0
      PRINT "An error occurred during execution of the DELETE
      statement."
 
   IF @ins_error <> 0
      PRINT "An error occurred during execution of the INSERT
      statement."
 
   ROLLBACK TRAN
END
GO
3.@@IDENTITY
返回最后插入的标识值
下面的示例向带有标识列的表中插入一行,并用 @@IDENTITY 显示在新行中使用的标识值。
 
INSERT INTO jobs (job_desc,min_lvl,max_lvl)
VALUES ('Accountant',12,125)
SELECT @@IDENTITY AS 'Identity'
查看当前错误码对应的描述信息。
select [description] from master.dbo.sysmessages where error = @@ERROR
3.4 常用SQL
1.得到数据库中所有用户表
Select [name] from sysObjects Where xtype='U'and [name]<>'dtproperties' Order By [name]
 
2.得到数据库中所有用户视图
Select [name] From sysObjects Where xtype='V' And [name]<>'syssegments' And [name]<>'sysconstraints' Order By [name]
 
3.获得指定表中所有的列
Select c.name As ColumnName, t.name As TypeName From syscolumns c, systypes t, sysobjects o
Where c.xtype = t.xusertype And c.id = o.id And o.name = 't_LogData' Order By c.colorder
 
4.获得表中所有列的详细信息
Select ColOrder = col.colorder, --排序号
 ColumnName = col.name, --列名
 TypeName = type.name,--数据类型名称
 Length = (Case When type.name='nvarchar' Or type.name='nchar' Then col.length/2 Else col.length End), --长度
 [PRECISION] = COLUMNPROPERTY(col.id, col.name, 'PRECISION'), --精度
 Scale = ISNULL(COLUMNPROPERTY(col.id, col.name, 'Scale'), 0), --小数
 IsIdentity = Case When COLUMNPROPERTY(col.id, col.name, 'IsIdentity')=1 Then '√' Else '' End, --是否为自动编号列
 IsPK = Case When Exists(Select 1 From sysobjects Where xtype = 'PK' And name In (
     Select name From sysindexes Where indid In (
      Select indid From sysindexkeys Where id = col.id And colid = col.colid
      )
     )
    ) Then '√' Else '' End, --是否为主键
 AllowNull = Case When col.isnullable=1 Then '√' Else '' End, --是否允许为空
 DefalutValue = isnull(com.text, '') --默认值
From syscolumns col
Left Join systypes type On col.xtype = type.xusertype
Inner Join sysobjects obj On col.id = obj.id And (obj.xtype = 'U' Or obj.xtype = 'V') And obj.name <> 'dtproperties'
Left Join syscomments com On col.cdefault = com.id
Where obj.name = t_LogData
5. 判断是数据库中是否存在某数据库
         If exists (select * from master.dbo.sysdatabases where name =N'db_Logsystem')
                   Print 'exist'
         Else
                   Print 'ok'
 
显示数据库中所有的表名:select * from sysobjects where xtype='u'
显示数据库中的所有用户表名:select * from sysobjects where xtype='u' and status>0
显示数据库中的所有视图名:select * from sysobjects where xtype='v'
显示数据库中的所有用户视图名:select * from sysobjects where xtype='v' and status>0
显示数据库中的所有存储过程名:select * from sysobjects where xtype='p'
显示数据库中的所有用户存储过程名:select * from sysobjects where xtype='p' and status >0 
 
6. Set NOCOUNT ON|OFF
 
7. 在测试一条SQL语句时需要使用下面这条语句显示测试
 
SET STATISTICS IO on
SET STATISTICS TIME on
 
select * from sysobjects
 
 
执行结果:
SQL Server 执行时间:
   CPU 时间 = 0 毫秒,耗费时间 = 0 毫秒。
SQL Server 分析和编译时间:
   CPU 时间 = 0 毫秒,耗费时间 = 0 毫秒。
 
(所影响的行数为 72 行)
 
表 'sysobjects'。扫描计数 1,逻辑读 2 次,物理读 0 次,预读 0 次。
 
SQL Server 执行时间:
   CPU 时间 = 0 毫秒,耗费时间 = 13 毫秒。
 
SQL Server 执行时间:
   CPU 时间 = 0 毫秒,耗费时间 = 47 毫秒。
 
SQL Server 执行时间:
   CPU 时间 = 0 毫秒,耗费时间 = 47 毫秒。
SQL Server 分析和编译时间:
   CPU 时间 = 0 毫秒,耗费时间 = 0 毫秒。
 
SQL Server 执行时间:
   CPU 时间 = 0 毫秒,耗费时间 = 0 毫秒。
 
8.通配符的一些使用方法:
    通配符的一些用法:(关键字:like % [] -)
    select * from tablename where column1 like '[A-M]%'
    这样可以选择出column字段中首字母在A-M之间的记录
    select * from tablename where column1 like '[ABC]%'
    这样可以选择出column字段中首字母是A或者B或者C的记录
    select * from tablename where column1 like '[A-CG]%'
    这样可以选择出column字段中首字母在A-C之间的或者是G的记录
    select * from tablename where column1 like '[^C]%'
    这样可以选择出column字段中首字母不是C的记录
 
    脱字符(关键字:like _)
    通过使用下滑线字符(_),可以匹配任意单个字符
    select * from tablename where column1 like 'M_crosoft'
 
    匹配特殊字符:([ ] _ - %)
    把他们都放到[]中就行了,比如:
    select * from tablename where column1 like '%[%]%'
 
    匹配发音(关键字:SOUNDEX DIFFERENCE)
    如果不知道一个名字确切的发音,但是又多少知道一点,可以考虑使用SOUNDEX DIFFERENCE函数。
    select * from tablename where DIFFERENCE(column1,'Laofei'>3)
    DIFFERENCE返回0-4之间的数字,4是非常接近,0是差异非常大
    要深入了解DIFFERENCE函数的工作原理,使用SOUNDEX函数返回DIFFERENCE函数所使用的音标码
    select column1 as column,SOUNDEX(column1) 'sound like'
 
    注意:
    DIFFERENCE函数比较两个字符串的第一个字母和所有的辅音字母,该函数忽略任何元音字母(包括Y),除非元音字母是该字符串的第一个字母。
    使用这两个函数在where中执行效果并不好,所以尽量少使用。

 

抱歉!评论已关闭.