分享

sql优化

 小窗自纪 2011-05-28

  摘自:http://www./asp/tech_file/xxnr_tech_806.htm


  我们要做到不但会写sql 还要做到写出性能优良的sql 以下为笔者学习、摘录、并汇总部分资料与大家分享!
 (1)      选择最有效率的表名顺序(只在基于规则的优化器中有效):
 oracle 的解析器按照从右到左的顺序处理from子句中的表名,from子句中写在最后的表(基础表 driving table)将被最先处理,在from子句中包含多个表的情况下 你必须选择记录条数最少的表作为基础表。如果有3个以上的表连接查询  那就需要选择交叉表(intersection table)作为基础表  交叉表是指那个被其他表所引用的表.
 (2)      where子句中的连接顺序.:
 oracle采用自下而上的顺序解析where子句 根据这个原理 表之间的连接必须写在其他where条件之前  那些可以过滤掉最大数量记录的条件必须写在where子句的末尾.
 (3)      select子句中避免使用 ‘  ‘:
 oracle在解析的过程中  会将' '  依次转换成所有的列名  这个工作是通过查询数据字典完成的  这意味着将耗费更多的时间
 (4)      减少访问数据库的次数:
 oracle在内部执行了许多工作: 解析sql语句  估算索引的利用率  绑定变量   读数据块等;
 (5)      在sqlplus   sqlforms和proc中重新设置arraysize参数  可以增加每次数据库访问的检索数据量  建议值为200
 (6)      使用decode函数来减少处理时间:
 使用decode函数可以避免重复扫描相同记录或重复连接相同的表.
 (7)      整合简单 无关联的数据库访问:
 如果你有几个简单的数据库查询语句 你可以把它们整合到一个查询中(即使它们之间没有关系)
 (8)      删除重复记录:
 最高效的删除重复记录方法 ( 因为使用了rowid)例子:
 delete  from  emp e  where  e.rowid >  (select min(x.rowid)
 from  emp x  where  x.emp_no = e.emp_no) 
 (9)      用truncate替代delete:
 当删除表中的记录时 在通常情况下  回滚段(rollback segments ) 用来存放可以被恢复的信息. 如果你没有commit事务 oracle会将数据恢复到删除之前的状态(准确地说是恢复到执行删除命令之前的状况) 而当运用truncate时  回滚段不再存放任何可被恢复的信息.当命令运行后 数据不能被恢复.因此很少的资源被调用 执行时间也会很短. (译者按: truncate只在删除全表适用 truncate是ddl不是dml)
 (10) 尽量多使用commit:
 只要有可能 在程序中尽量多使用commit  这样程序的性能得到提高 需求也会因为commit所释放的资源而减少:
 commit所释放的资源:
 a. 回滚段上用于恢复数据的信息.
 b. 被程序语句获得的锁
 c. redo log buffer 中的空间
 d. oracle为管理上述3种资源中的内部花费
 (11) 用where子句替换having子句:
 避免使用having子句  having 只会在检索出所有记录之后才对结果集进行过滤. 这个处理需要排序 总计等操作. 如果能通过where子句限制记录的数目 那就能减少这方面的开销. (非oracle中)on、where、having这三个都可以加条件的子句中,on是最先执行,where次之,having最后,因为on是先把不符合条件的记录过滤后才进行统计,它就可以减少中间运算要处理的数据,按理说应该速度是最快的,where也应该比having快点的,因为它过滤数据后才进行sum,在两个表联接时才用on的,所以在一个表的时候,就剩下where跟having比较了。在这单表查询统计的情况下,如果要过滤的条件没有涉及到要计算字段,那它们的结果是一样的,只是where可以使用rushmore技术,而having就不能,在速度上后者要慢如果要涉及到计算的字段,就表示在没计算之前,这个字段的值是不确定的,根据上篇写的工作流程,where的作用时间是在计算之前就完成的,而having就是在计算后才起作用的,所以在这种情况下,两者的结果会不同。在多表联接查询时,on比where更早起作用。系统首先根据各个表之间的联接条件,把多个表合成一个临时表后,再由where进行过滤,然后再计算,计算完后再由having进行过滤。由此可见,要想过滤条件起到正确的作用,首先要明白这个条件应该在什么时候起作用,然后再决定放在那里
 (12) 减少对表的查询:
 在含有子查询的sql语句中 要特别注意减少对表的查询.例子:
     select  tab_name from tables where (tab_name db_ver) = ( select
 tab_name db_ver from  tab_columns  where  version = 604)
 (13) 通过内部函数提高sql效率.:
 复杂的sql往往牺牲了执行效率. 能够掌握上面的运用函数解决问题的方法在实际工作中是非常有意义的
 (14) 使用表的别名(alias):
 当在sql语句中连接多个表时  请使用表的别名并把别名前缀于每个column上.这样一来 就可以减少解析的时间并减少那些由column歧义引起的语法错误.
 (15) 用exists替代in、用not exists替代not in:
 在许多基于基础表的查询中 为了满足一个条件 往往需要对另一个表进行联接.在这种情况下  使用exists(或not exists)通常将提高查询的效率. 在子查询中 not in子句将执行一个内部的排序和合并. 无论在哪种情况下 not in都是最低效的 (因为它对子查询中的表执行了一个全表遍历). 为了避免使用not in  我们可以把它改写成外连接(outer joins)或not exists.
 例子:
 (高效)select  from  emp (基础表)  where  empno >  0  and  exists (select ‘x'   from dept  where  dept.deptno = emp.deptno  and  loc = ‘melb' )
 (低效)select   from  emp (基础表)  where  empno >  0  and  deptno in(select deptno  from  dept  where  loc = ‘melb' )
 (16) 识别' 低效执行' 的sql语句:
 虽然目前各种关于sql优化的图形化工具层出不穷 但是写出自己的sql工具来解决问题始终是一个最好的方法:
 select   utions   disk_reads  buffer_gets 
 round((buffer_gets-disk_reads)/buffer_gets 2) hit_radio 
 round(disk_reads/ utions 2) reads_per_run 
 sql_text
 from  v$sqlarea
 where   utions> 0
 and  buffer_gets >  0
 and  (buffer_gets-disk_reads)/buffer_gets <  0.8
 order by  4 desc 

 
 (17) 用索引提高效率:
 索引是表的一个概念部分 用来提高检索数据的效率,oracle使用了一个复杂的自平衡b-tree结构. 通常 通过索引查询数据比全表扫描要快. 当oracle找出执行查询和update语句的最佳路径时  oracle优化器将使用索引. 同样在联结多个表时使用索引也可以提高效率. 另一个使用索引的好处是 它提供了主键(primary key)的唯一性验证.。那些long或long raw数据类型  你可以索引几乎所有的列. 通常  在大型表中使用索引特别有效. 当然 你也会发现  在扫描小表时 使用索引同样能提高效率. 虽然使用索引能得到查询效率的提高 但是我们也必须注意到它的代价. 索引需要空间来存储 也需要定期维护  每当有记录在表中增减或索引列被修改时  索引本身也会被修改. 这意味着每条记录的insert   delete   update将为此多付出4   5 次的磁盘i/o . 因为索引需要额外的存储空间和处理 那些不必要的索引反而会使查询反应时间变慢.。定期的重构索引是有必要的.:
 alter  index < indexname>  rebuild < tablespacename> 
 18) 用exists替换distinct:
 当提交一个包含一对多表信息(比如部门表和雇员表)的查询时 避免在select子句中使用distinct. 一般可以考虑用exist替换  exists 使查询更为迅速 因为rdbms核心模块将在子查询的条件一旦满足后 立刻返回结果. 例子:
       (低效):
 select  distinct  dept_no dept_name  from  dept d   emp e
 where  d.dept_no = e.dept_no
 (高效):
 select  dept_no dept_name  from  dept d  where  exists ( select ‘x' 
 from  emp e  where e.dept_no = d.dept_no) 
 (19) sql语句用大写的;因为oracle总是先解析sql语句,把小写的字母转换成大写的再执行
 (20) 在java代码中尽量少用连接符“+”连接字符串!
 (21) 避免在索引列上使用not 通常, 
 我们要避免在索引列上使用not  not会产生在和在索引列上使用函数相同的影响. 当oracle”遇到”not 他就会停止使用索引转而执行全表扫描.
 (22) 避免在索引列上使用计算.
 where子句中,如果索引列是函数的一部分.优化器将不使用索引而使用全表扫描.
 举例:
 低效:
 select … from  dept  where sal  12 >  25000 
 高效:
 select … from dept where sal >  25000/12 
 (23) 用> =替代> 
 高效:
 select  from  emp  where  deptno > =4
 低效:
 select  from emp where deptno > 3
 两者的区别在于  前者dbms将直接跳到第一个dept等于4的记录而后者将首先定位到deptno=3的记录并且向前扫描到第一个dept大于3的记录.
 (24) 用union替换or (适用于索引列)
 通常情况下  用union替换where子句中的or将会起到较好的效果. 对索引列使用or将造成全表扫描. 注意  以上规则只针对多个索引列有效. 如果有column没有被索引  查询效率可能会因为你没有选择or而降低. 在下面的例子中  loc_id 和region上都建有索引.
 高效:
 select loc_id   loc_desc   region
 from location
 where loc_id = 10
 union
 select loc_id   loc_desc   region
 from location
 where region = “melbourne”
 低效:
 select loc_id   loc_desc   region
 from location
 where loc_id = 10 or region = “melbourne”
 如果你坚持要用or  那就需要返回记录最少的索引列写在最前面.
 (25) 用in来替换or 
 这是一条简单易记的规则,但是实际的执行效果还须检验,在oracle8i下,两者的执行路径似乎是相同的. 
 低效:
 select…. from location where loc_id = 10 or loc_id = 20 or loc_id = 30
 高效
 select… from location where loc_in  in (10 20 30) 
 (26) 避免在索引列上使用is null和is not null
 避免在索引中使用任何可以为空的列,oracle将无法使用该索引.对于单列索引,如果列包含空值,索引中将不存在此记录. 对于复合索引,如果每个列都为空,索引中同样不存在此记录. 如果至少有一个列不为空,则记录存在于索引中.举例: 如果唯一性索引建立在表的a列和b列上  并且表中存在一条记录的a b值为(123 null)   oracle将不接受下一条具有相同a b值(123 null)的记录(插入). 然而如果所有的索引列都为空,oracle将认为整个键值为空而空不等于空. 因此你可以插入1000 条具有相同键值的记录 当然它们都是空! 因为空值不存在于索引列中 所以where子句中对索引列进行空值比较将使oracle停用该索引.
 低效: (索引失效)
 select … from  department  where  dept_code is not null 
 高效: (索引有效)
 select … from  department  where  dept_code > =0 
 (27) 总是使用索引的第一个列:
 如果索引是建立在多个列上  只有在它的第一个列(leading column)被where子句引用时 优化器才会选择使用该索引. 这也是一条简单而重要的规则,当仅引用索引的第二个列时 优化器使用了全表扫描而忽略了索引
 28) 用union-all 替换union ( 如果有可能的话):
 当sql 语句需要union两个查询结果集合时 这两个结果集合会以union-all的方式被合并  然后在输出最终结果前进行排序. 如果用union all替代union  这样排序就不是必要了. 效率就会因此得到提高. 需要注意的是,union all 将重复输出两个结果集合中相同记录. 因此各位还是要从业务需求分析使用union all的可行性. union 将对结果集合排序 这个操作会使用到sort_area_size这块内存. 对于这块内存的优化也是相当重要的. 下面的sql可以用来查询排序的消耗量
 低效:
 select  acct_num  balance_amt
 from  debit_transactions
 where tran_date = ' 31-dec-95' 
 union
 select acct_num  balance_amt
 from debit_transactions
 where tran_date = ' 31-dec-95' 
 高效:
 select acct_num  balance_amt
 from debit_transactions
 where tran_date = ' 31-dec-95' 
 union all
 select acct_num  balance_amt
 from debit_transactions
 where tran_date = ' 31-dec-95' 
 (29) 用where替代order by:
 order by 子句只在两种严格的条件下使用索引.
 order by中所有的列必须包含在相同的索引中并保持在索引中的排列顺序.
 order by中所有的列必须定义为非空.
 where子句使用的索引和order by子句中所使用的索引不能并列.
 例如:
 表dept包含以下列:
 dept_code pk not null
 dept_desc not null
 dept_type null
 低效: (索引不被使用)
 select dept_code from  dept  order by  dept_type
 高效: (使用索引)
 select dept_code  from  dept  where  dept_type >  0
 (30) 避免改变索引列的类型.:
 当比较不同数据类型的数据时  oracle自动对列进行简单的类型转换.
 假设 empno是一个数值类型的索引列.
 select …  from emp  where  empno = ‘123' 
 实际上 经过oracle类型转换  语句转化为:
 select …  from emp  where  empno = to_number(‘123' )
 幸运的是 类型转换没有发生在索引列上 索引的用途没有被改变.
 现在 假设emp_type是一个字符类型的索引列.
 select …  from emp  where emp_type = 123
 这个语句被oracle转换为:
 select …  from emp  whereto_number(emp_type)=123
 因为内部发生的类型转换  这个索引将不会被用到! 为了避免oracle对你的sql进行隐式的类型转换  最好把类型转换用显式表现出来. 注意当字符和数值比较时  oracle会优先转换数值类型到字符类型
 (31) 需要当心的where子句:
 某些select 语句中的where子句不使用索引. 这里有一些例子.
 在下面的例子里  (1)‘!='  将不使用索引. 记住  索引只能告诉你什么存在于表中  而不能告诉你什么不存在于表中. (2) ‘||' 是字符连接函数. 就象其他函数那样  停用了索引. (3) ‘+' 是数学函数. 就象其他数学函数那样  停用了索引. (4)相同的索引列不能互相比较 这将会启用全表扫描.
 (32) a. 如果检索数据量超过30的表中记录数.使用索引将没有显著的效率提高.
 b. 在特定情况下  使用索引也许会比全表扫描慢  但这是同一个数量级上的区别. 而通常情况下 使用索引比全表扫描要块几倍乃至几千倍!
 (33) 避免使用耗费资源的操作:
 带有distinct union minus intersect order by的sql语句会启动sql引擎
 执行耗费资源的排序(sort)功能. distinct需要一次排序操作  而其他的至少需要执行两次排序. 通常  带有union  minus   intersect的sql语句都可以用其他方式重写. 如果你的数据库的sort_area_size调配得好  使用union   minus  intersect也是可以考虑的  毕竟它们的可读性很强
 (34) 优化group by:
 提高group by 语句的效率  可以通过将不需要的记录在group by 之前过滤掉.下面两个查询返回相同结果但第二个明显就快了许多.
 低效:
 select job   avg(sal)
 from emp
 group by job
 having job = ‘president' 
 or job = ‘manager' 
 高效:
 select job   avg(sal)
 from emp
 where job = ‘president' 
 or job = ‘manager' 
 group by job
 ===============================================

 本期主题: sql server查询优化.
 查询优化是一个比较常见的主题 
 在实际的生产应用中 大数据量的查询会消耗服务器大量的资源 
 如何合理地优化查询 提高系统性能 
 是每个系统架构人员 系统开发热源 系统维护人员以及数据库管理人员共同关心的话题.
 本期就此做一下讨论.
 欢迎大家积极参与.:)
 先贴几篇作为抛砖引玉.
 如何实现优化查询.
 
http://topic.csdn.net/u/20080416/08/24b62a73-ab5a-4927-97af-81e7e790445d.html
 索引的基本原理,以及数据是如何被访问的
 
http://topic.csdn.net/u/20080521/15/c5ee330e-596d-4957-8032-5bb9a80a9218.html
 100分,求sqlserver 中提高查询性能的方法!越全越好!
 
http://topic.csdn.net/u/20080624/10/0ac05d43-b285-49f0-85b2-73e0d989d2ae.html
 深度探索:clustered index scan vs table scan
 
http://topic.csdn.net/u/20080707/09/3c64cc80-5f7a-4116-8942-81c6deae9c07.html
 五种提高 sql 性能的方法
 
http://dev.csdn.net/author/griefforyou/082b9b29299e4584b78bf6f7ccb57c0b.html
 数据库的查询优化技术
 
http://bbs./viewthread.php?tid=83481

 
 查询优化
 
http://msdn.microsoft.com/zh-cn/library/ms176005.aspx

 分析查询
 
http://msdn.microsoft.com/zh-cn/library/ms191227.aspx

 查找缺失索引
 
http://msdn.microsoft.com/zh-cn/library/ms345417.aspx

 查询优化建议
 
http://msdn.microsoft.com/zh-cn/library/ms188722.aspx

 高级查询优化概念
 
http://msdn.microsoft.com/zh-cn/library/ms191426.aspx

 些blogs和英文资料.
 --------------------------------------
 tips  tricks  and advice from the sql server query optimization team
 
http://blogs./queryoptteam/
 
http://blogs./sqlqueryprocessing/default.aspx

 sql server 2000 query tuning
 
http://msdn.microsoft.com/en-us/library/aa178417(sql.80).aspx
 analyzing a query
 
http://msdn.microsoft.com/en-us/library/aa178303(sql.80).aspx
 query tuning recommendations
 
http://msdn.microsoft.com/en-us/library/aa178391(sql.80).aspx
 advanced query tuning concepts
 
http://msdn.microsoft.com/en-us/library/aa178578(sql.80).aspx

 some " hints"  for mastering sql tuning
 
http://www./dbzone/article/26995

 transact-sql query performance tuning tips
 
http://www./uploadfile/skrishnasamy/sqlperformancetunning03112005044423am/sqlperformancetunning.aspx

 sql server 2005 query tuning
 
http://msdn.microsoft.com/en-us/library/ms176005.aspx

 analyzing a query
 
http://msdn.microsoft.com/en-us/library/ms191227.aspx

 finding missing indexes
 
http://msdn.microsoft.com/en-us/library/ms345417.aspx

 query tuning recommendations
 
http://msdn.microsoft.com/en-us/library/ms188722.aspx

 advanced query tuning concepts
 
http://msdn.microsoft.com/en-us/library/ms191426.aspx

 deeper insight into unused indexes for sql server 2005
 
http://www./tip.asp?tip=1545

 use missing-index groups for query tuning
 
http://www./article/articleid/95220/sql_server_95220.html

 2 stored procedures to tune your indexes
 
http://www./article/articleid/98019/98019.html?feed=articlelink

 what i wish developers knew about sql server (presentation)
 
http://www./article/what-i-wish-developers-knew-about-sql-server-presentation.
 this is the latest version of this presentation including all demonstration scripts. the presentation has been converted to a pdf to make viewing easier. the presentation provides an overview of query plans  query tuning tips  nulls  error handling and transactions.
 the presentation and demo scripts.
 
http://www./downloads/whatiwishpresentation.zip

 avoid enclosing indexed columns in a function in the where clause
 
http://www./article/avoid-enclosing-indexed-columns-in-a-function-in-the-where-clause

 introduction to parameterization in sql server
 
http://www./article/introduction-to-parameterization-in-sql-server

 best practices for query parameterization
 
http://www./article/best-practices-for-query-parameterization

 query parameterization and plan cache
 
http://blogs./sqlprogrammability/archive/2007/01/11/4-0-query-parameterization.aspx
 this comes to us from the sql server programmability and api team blog. they write " using parameters or parameter markers in queries increases the ability of sql server to reuse compiled plans. there are two places where parameterization of queries can be done: on the client side application (or mid tier) or on the server side." 

 analyzing and optimizing t-sql query performance on microsoft sql server using set and dbcc (pdf)
 
http://www./article/analyzing-and-optimizing-t-sql-query-performance-on-microsoft-sql-server-using-set-and-dbcc-pdf

 five ways to rev up your sql performance
 
http://msdn.microsoft.com/en-us/magazine/cc301622.aspx
 这篇文章已经有中译本(五种提高 sql 性能的方法)

 最后推荐一本调优的书
 inside sql server 2005: query tuning and optimization
 
http://download.csdn.net/source/421022

 ====================================================================

 本文首先讨论了基于第三范式的数据库表的基本设计,着重论述了建立主键和索引的策略和方案,然后从数据库表的扩展设计和库表对象的放置等角度概述了数据库管理系统的优化方案。

 关键词: 优化(optimizing) 第三范式(3nf) 冗余数据(redundant data) 索引(index) 数据分割(data partitioning) 对象放置(object placement)

 1 引言
 数据库优化的目标无非是避免磁盘i/o瓶颈、减少cpu利用率和减少资源竞争。为了便于读者阅读和理解,笔者参阅了sybase、informix和oracle等大型数据库系统参考资料,基于多年的工程实践经验,从基本表设计、扩展设计和数据库表对象放置等角度进行讨论,着重讨论了如何避免磁盘i/o瓶颈和减少资源竞争,相信读者会一目了然。

 2 基于第三范式的基本表设计
 在基于表驱动的信息管理系统(mis)中,基本表的设计规范是第三范式(3nf)。第三范式的基本特征是非主键属性只依赖于主键属性。基于第三范式的数据库表设计具有很多优点:一是消除了冗余数据,节省了磁盘存储空间;二是有良好的数据完整性限制,即基于主外键的参照完整限制和基于主键的实体完整性限制,这使得数据容易维护,也容易移植和更新;三是数据的可逆性好,在做连接(join)查询或者合并表时不遗漏、也不重复;四是因消除了冗余数据(冗余列),在查询(select)时每个数据页存的数据行就多,这样就有效地减少了逻辑i/o,每个cash存的页面就多,也减少物理i/o;五是对大多数事务(transaction)而言,运行性能好;六是物理设计(physical design)的机动性较大,能满足日益增长的用户需求。
 在基本表设计中,表的主键、外键、索引设计占有非常重要的地位,但系统设计人员往往只注重于满足用户要求,而没有从系统优化的高度来认识和重视它们。实际上,它们与系统的运行性能密切相关。现在从系统数据库优化角度讨论这些基本概念及其重要意义:

 (1)主键(primary key):主键被用于复杂的sql语句时,频繁地在数据访问中被用到。一个表只有一个主键。主键应该有固定值(不能为null或缺省值,要有相对稳定性),不含代码信息,易访问。把常用(众所周知)的列作为主键才有意义。短主键最佳(小于25bytes),主键的长短影响索引的大小,索引的大小影响索引页的大小,从而影响磁盘i/o。主键分为自然主键和人为主键。自然主键由实体的属性构成,自然主键可以是复合性的,在形成复合主键时,主键列不能太多,复合主键使得join作复杂化、也增加了外键表的大小。人为主键是,在没有合适的自然属性键、或自然属性复杂或灵敏度高时,人为形成的。人为主键一般是整型值(满足最小化要求),没有实际意义,也略微增加了表的大小;但减少了把它作为外键的表的大小。

 (2)外键(foreign key):外键的作用是建立关系型数据库中表之间的关系(参照完整性),主键只能从独立的实体迁移到非独立的实体,成为后者的一个属性,被称为外键。

 (3)索引(index):利用索引优化系统性能是显而易见的,对所有常用于查询中的where子句的列和所有用于排序的列创建索引,可以避免整表扫描或访问,在不改变表的物理结构的情况下,直接访问特定的数据列,这样减少数据存取时间;利用索引可以优化或排除耗时的分类作;把数据分散到不同的页面上,就分散了插入的数据;主键自动建立了唯一索引,因此唯一索引也能确保数据的唯一性(即实体完整性);索引码越小,定位就越直接;新建的索引效能最好,因此定期更新索引非常必要。索引也有代价:有空间开销,建立它也要花费时间,在进行insert、delete和update作时,也有维护代价。索引有两种:聚族索引和非聚族索引。一个表只能有一个聚族索引,可有多个非聚族索引。使用聚族索引查询数据要比使用非聚族索引快。在建索引前,应利用数据库系统函数估算索引的大小。
 ① 聚族索引(clustered index):聚族索引的数据页按物理有序储存,占用空间小。选择策略是,被用于where子句的列:包括范围查询、模糊查询或高度重复的列(连续磁盘扫描);被用于连接join作的列;被用于order by和group by子句的列。聚族索引不利于插入作,另外没有必要用主键建聚族索引。
 ② 非聚族索引(nonclustered index):与聚族索引相比,占用空间大,而且效率低。选择策略是,被用于where子句的列:包括范围查询、模糊查询(在没有聚族索引时)、主键或外键列、点(指针类)或小范围(返回的结果域小于整表数据的20)查询;被用于连接join作的列、主键列(范围查询);被用于order by和group by子句的列;需要被覆盖的列。对只读表建多个非聚族索引有利。索引也有其弊端,一是创建索引要耗费时间,二是索引要占有大量磁盘空间,三是增加了维护代价(在修改带索引的数据列时索引会减缓修改速度)。那么,在哪种情况下不建索引呢?对于小表(数据小于5页)、小到中表(不直接访问单行数据或结果集不用排序)、单值域(返回值密集)、索引列值太长(大于20bitys)、容易变化的列、高度重复的列、null值列,对没有被用于where子语句和join查询的列都不能建索引。另外,对主要用于数据录入的,尽可能少建索引。当然,也要防止建立无效索引,当where语句中多于5个条件时,维护索引的开销大于索引的效益,这时,建立临时表存储有关数据更有效。
 批量导入数据时的注意事项:在实际应用中,大批量的计算(如电信话单计费)用c语言程序做,这种基于主外键关系数据计算而得的批量数据(文本文件),可利用系统的自身功能函数(如sybase的bcp命令)快速批量导入,在导入数据库表时,可先删除相应库表的索引,这有利于加快导入速度,减少导入时间。在导入后再重建索引以便优化查询。

 (4)锁:锁是并行处理的重要机制,能保持数据并发的一致性,即按事务进行处理;系统利用锁,保证数据完整性。因此,我们避免不了死锁,但在设计时可以充分考虑如何避免长事务,减少排它锁时间,减少在事务中与用户的交互,杜绝让用户控制事务的长短;要避免批量数据同时执行,尤其是耗时并用到相同的数据表。锁的征用:一个表同时只能有一个排它锁,一个用户用时,其它用户在等待。若用户数增加,则server的性能下降,出现“假死”现象。如何避免死锁呢?从页级锁到行级锁,减少了锁征用;给小表增加无效记录,从页级锁到行级锁没有影响,若在同一页内竞争有影响,可选择合适的聚族索引把数据分配到不同的页面;创建冗余表;保持事务简短;同一批处理应该没有网络交互。

 (5)查询优化规则:在访问数据库表的数据(access data)时,要尽可能避免排序(sort)、连接(join)和相关子查询作。经验告诉我们,在优化查询时,必须做到:
 ① 尽可能少的行;
 ② 避免排序或为尽可能少的行排序,若要做大量数据排序,最好将相关数据放在临时表中作;用简单的键(列)排序,如整型或短字符串排序;
 ③ 避免表内的相关子查询;
 ④ 避免在where子句中使用复杂的表达式或非起始的子字符串、用长字符串连接;
 ⑤ 在where子句中多使用“与”(and)连接,少使用“或”(or)连接;
 ⑥ 利用临时数据库。在查询多表、有多个连接、查询复杂、数据要过滤时,可以建临时表(索引)以减少i/o。但缺点是增加了空间开销。
 除非每个列都有索引支持,否则在有连接的查询时分别找出两个动态索引,放在工作表中重新排序

 。
 3 基本表扩展设计
 基于第三范式设计的库表虽然有其优越性(见本文第一部分),然而在实际应用中有时不利于系统运行性能的优化:如需要部分数据时而要扫描整表,许多过程同时竞争同一数据,反复用相同行计算相同的结果,过程从多表获取数据时引发大量的连接作,当数据来源于多表时的连接作;这都消耗了磁盘i/o和cpu时间。
 尤其在遇到下列情形时,我们要对基本表进行扩展设计:许多过程要频繁访问一个表、子集数据访问、重复计算和冗余数据,有时用户要求一些过程优先或低的响应时间。
 如何避免这些不利因素呢?根据访问的频繁程度对相关表进行分割处理、存储冗余数据、存储衍生列、合并相关表处理,这些都是克服这些不利因素和优化系统运行的有效途径。

 3.1 分割表或储存冗余数据
 分割表分为水平分割表和垂直分割表两种。分割表增加了维护数据完整性的代价。
 水平分割表:一种是当多个过程频繁访问数据表的不同行时,水平分割表,并消除新表中的冗余数据列;若个别过程要访问整个数据,则要用连接作,这也无妨分割表;典型案例是电信话单按月分割存放。另一种是当主要过程要重复访问部分行时,最好将被重复访问的这些行单独形成子集表(冗余储存),这在不考虑磁盘空间开销时显得十分重要;但在分割表以后,增加了维护难度,要用触发器立即更新、或存储过程或应用代码批量更新,这也会增加额外的磁盘i/o开销。
 垂直分割表(不破坏第三范式),一种是当多个过程频繁访问表的不同列时,可将表垂直分成几个表,减少磁盘i/o(每行的数据列少,每页存的数据行就多,相应占用的页就少),更新时不必考虑锁,没有冗余数据。缺点是要在插入或删除数据时要考虑数据的完整性,用存储过程维护。另一种是当主要过程反复访问部分列时,最好将这部分被频繁访问的列数据单独存为一个子集表(冗余储存),这在不考虑磁盘空间开销时显得十分重要;但这增加了重叠列的维护难度,要用触发器立即更新、或存储过程或应用代码批量更新,这也会增加额外的磁盘i/o开销。垂直分割表可以达到最大化利用cache的目的。
 总之,为主要过程分割表的方法适用于:各个过程需要表的不联结的子集,各个过程需要表的子集,访问频率高的主要过程不需要整表。在主要的、频繁访问的主表需要表的子集而其它主要频繁访问的过程需要整表时则产生冗余子集表。
 注意,在分割表以后,要考虑重新建立索引。

 3.2 存储衍生数据
 对一些要做大量重复性计算的过程而言,若重复计算过程得到的结果相同(源列数据稳定,因此计算结果也不变),或计算牵扯多行数据需额外的磁盘i/o开销,或计算复杂需要大量的cpu时间,就考虑存储计算结果(冗余储存)。现予以分类说明:
 若在一行内重复计算,就在表内增加列存储结果。但若参与计算的列被更新时,必须要用触发器更新这个新列。
 若对表按类进行重复计算,就增加新表(一般而言,存放类和结果两列就可以了)存储相关结果。但若参与计算的列被更新时,就必须要用触发器立即更新、或存储过程或应用代码批量更新这个新表。
 若对多行进行重复性计算(如排名次),就在表内增加列存储结果。但若参与计算的列被更新时,必须要用触发器或存储过程更新这个新列。
 总之,存储冗余数据有利于加快访问速度;但违反了第三范式,这会增加维护数据完整性的代价,必须用触发器立即更新、或存储过程或应用代码批量更新,以维护数据的完整性。

 3.3 消除昂贵结合
 对于频繁同时访问多表的一些主要过程,考虑在主表内存储冗余数据,即存储冗余列或衍生列(它不依赖于主键),但破坏了第三范式,也增加了维护难度。在源表的相关列发生变化时,必须要用触发器或存储过程更新这个冗余列。当主要过程总同时访问两个表时可以合并表,这样可以减少磁盘i/o作,但破坏了第三范式,也增加了维护难度。对父子表和1:1关系表合并方法不同:合并父子表后,产生冗余表;合并1:1关系表后,在表内产生冗余数据。

 4 数据库对象的放置策略
 数据库对象的放置策略是均匀地把数据分布在系统的磁盘中,平衡i/o访问,避免i/o瓶颈。
 ⑴ 访问分散到不同的磁盘,即使用户数据尽可能跨越多个设备,多个i/o运转,避免i/o竞争,克服访问瓶颈;分别放置随机访问和连续访问数据。
 ⑵ 分离系统数据库i/o和应用数据库i/o。把系统审计表和临时库表放在不忙的磁盘上。
 ⑶ 把事务日志放在单独的磁盘上,减少磁盘i/o开销,这还有利于在障碍后恢复,提高了系统的安全性。
 ⑷ 把频繁访问的“活性”表放在不同的磁盘上;把频繁用的表、频繁做join作的表分别放在单独的磁盘上,甚至把把频繁访问的表的字段放在不同的磁盘上,把访问分散到不同的磁盘上,避免i/o争夺;
 ⑸ 利用段分离频繁访问的表及其索引(非聚族的)、分离文本和图像数据。段的目的是平衡i/o,避免瓶颈,增加吞吐量,实现并行扫描,提高并发度,最大化磁盘的吞吐量。利用逻辑段功能,分别放置“活性”表及其非聚族索引以平衡i/o。当然最好利用系统的默认段。另外,利用段可以使备份和恢复数据更加灵活,使系统授权更加灵活。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多