zoukankan      html  css  js  c++  java
  • SQL优化

    从8i到10g,Oracle不断进化自己的SQL Tuning智能,一些秘籍级的优化口诀已经失效。
    但我喜欢失效,不用记口诀,操个Toad for Oracle Xpert ,按照大方向舒舒服服的调优才是爱做的事情。

    1.Excution Plan
    Excution Plan是最基本的调优概念,不管你的调优吹得如何天花乱堕,结果还是要由Excution plan来显示Oracle 最终用什么索引、按什么顺序连接各表,Full Table Scan还是Access by Rowid Index,瓶颈在什么地方。如果没有它的指导,一切调优都是蒙的。


    2.Toad for Oracle Xpert
    用它来调优在真的好舒服。Quest 吞并了Lecco后,将它整合到了Toad 的SQL Tunning里面:最清晰的执行计划显示,自动生成N条等价SQL、给出优化建议,不同SQL执行计划的对比,还有实际执行的逻辑读、物理读数据等等一目了然。


    3.索引
    大部分的性能问题其实都是索引应用的问题,Where子句、Order By、Group By 都要用到索引。
    一般开发人员认为将索引建全了就可以下班回家了,实则还有颇多的思量和陷阱。

    3.1 索引列上不要进行计算
    这是最最普遍的失效陷阱,比如where trunc(order_date)=trunc(sysdate), i+2<4。索引失效的原因也简单,索引是针对原值建的二叉树,你将列值*3/4+2折腾一番后,原来的二叉树当然就用不上了。解决的方法:
      1. 换成等价语法,比如trunc(order_date) 换成

    where order_date<trunc(sysdate)-1 and order_date>trunc(sysdate)+1
      2. 特别为计算建立函数索引

    create index I_XXXX on shop_order(trunc(order_date))
    3. 将计算从等号左边移到右边
     这是针对某些无心之失的纠正,把a*2<4 改为a<4/2;把TO_CHAR(zip) = '94002' 改为zip = TO_NUMBER('94002');

    3.2 CBO与索引选择性
    建了索引也不一定会被Oracle用的,就像个挑食的孩子。基于成本的优化器(CBO, Cost-Based Optimizer),会先看看表的大小,还有索引的重复度,再决定用还是不用。表中有100 条记录而其中有80 个不重复的索引键值. 这个索引的选择性就是80/100 = 0.8,留意Toad里显示索引的Selective和Cardinailty。实在不听话时,就要用hints来调教。
    另外,where语句存在多条索引可用时,只会选择其中一条。所以索引也不是越多越好:)

    3.3 索引重建
    传说中数据更新频繁导致有20%的碎片时,Oracle就会放弃这个索引。宁可信其有之下,应该时常alter index >INDEXNAME< rebuild一下。

    3.4 其他要注意的地方
    不要使用Not,如goods_no != 2,要改为

    where goods_no<2 or goods_no>2
    不要使用is null , 如WHERE DEPT_CODE IS NOT NULL 要改为

    WHERE DEPT_CODE <=0;
    3.5 select 的列如果全是索引列时
    又如果没有where 条件,或者where条件全部是索引列时,Oracle 将直接从索引里获取数据而不去读真实的数据表,这样子理论上会快很多,比如

    select order_no,order_time from shop_order where shop_no=4
    当order_no,order_time,shop_no 这三列全为索引列时,你将看到一个和平时完全不同的执行计划。

    3.6 位图索引
    传说中当数据值较少,比如某些表示分类、状态的列,应该建位图索引而不是普通的二叉树索引,否则效率低下。不过看执行计划,这些位图索引鲜有被Oracle临幸的。



    4.减少查询往返和查询的表
    这也是很简单的大道理,程序与Oracle交互的成本极高,所以一个查询能完成的不要分开两次查,如果一个循环执行1万条查询的,怎么都快不到哪里去了。

    4.1 封装PL/SQL存储过程
      最高级的做法是把循环的操作封装到PL/SQL写的存储过程里,因为存储过程都在服务端执行,所以没有数据往返的消耗。


    4.2 封装PL/SQL内部函数
    有机会,将一些查询封装到函数里,而在普通SQL里使用这些函数,同样是很有效的优化。

    4.3 Decode/Case
      但存储过程也麻烦,所以有case/decode把几条条件基本相同的重复查询合并为一条的用法:

    SELECT
    COUNT(CASE WHEN price > 13 THEN 1 ELSE null END) low,
    COUNT(CASE WHEN price BETWEEN 13 AND 15 THEN 1 ELSE null END) med,
    COUNT(CASE WHEN price < 15 THEN 1 ELSE null END) high
    FROM products;
    4.4 一种Where/Update语法

    SELECT TAB_NAME FROM TABLES
    WHERE (TAB_NAME,DB_VER) = (( SELECT TAB_NAME,DB_VER)
    FROM TAB_COLUMNS WHERE VERSION = 604)
    UPDATE EMP
    SET (EMP_CAT, SAL_RANGE)
    = (SELECT MAX(CATEGORY)FROM EMP_CATEGORIES)

    5.其他优化
    5.1RowID和ROWNUM
    连Hibernate 新版也支持ROWID了,证明它非常有用。比如号称删除重复数据的最快写法:

    DELETE FROM EMP E
    WHERE E.ROWID < (SELECT MIN(X.ROWID)
    FROM EMP X
    WHERE X.EMP_NO = E.EMP_NO);

    6.终极秘技 - Hints
    这是Oracle DBA的玩具,也是终极武器,比如Oracle在CBO,RBO中所做的选择总不合自己心水时,可以用它来强力调教一下Oracle,结果经常让人喜出望外。
    如果开发人员没那么多时间来专门学习它,可以依靠Toad SQL opmitzer 来自动生成这些提示,然后对比一下各种提示的实际效果。不过随着10g智能的进化,hints的惊喜少了。

    7. 找出要优化的Top SQL
    磨了这么久的,如果找不到敌人是件郁闷的事情。
    幸亏10g这方面做得非常好。进入Web管理界面,就能看到当前或者任意一天的SQL列表,按性能排序。
    有了它,SQL Trace和TKPROF都可以不用了。

    ------------------------------------------------------------------------------------------------------------------------------------------------------

    1.1.    操作符
    1.1.1    慎用NOT INNOT IN会多次扫描表,使用EXISTS、NOT  EXISTSINLEFTOUTER   JOIN来替代,特别是左连接,而Exists比IN更快,最慢的是NOT操作.

    1.1.2    使用in时,在in后面值的列表中,将出现最频繁的值放在最前面,出现得最少的放在最后面,这样可以减少判断的次数。

    1.1.3    如果使用了IN或者OR等时发现查询没有走索引,使用显式申明指定索引,如:Select*From FA01(INDEX=IX_SEX)  Where AA0107 IN(‘01’,‘02’)。

    1.1.4    用OR的字句可以分解成多个查询,并且通过UNION连接多个查询。他们的速度只同是否使用索引有关,如果查询需要用到联合索引,用UNION   all执行的效率更高.多个OR的字句没有用到索引,改写成UNION的形式再试图与索引匹配。一个关键的问题是否用到索引。 

    1.1.5    Between在某些时候比IN速度更快,Between能够更快地根据索引找到范围。用查询优化器可见到差别。
    select   *   from   chineseresume   where   title   in   ('',''
    Select   *   from   chineseresume   where   between  ''and  ''
    是一样的。由于in会在比较多次,所以有时会慢些。

    1.2.    查询
    1.1.1    注意UNion和UNion   all的区别。UNION比union all多做了一步distinct操作。能用union all的情况下尽量不用union。

    1.1.2    查询时尽量不要返回不需要的行、列。另外在多表连接查询时,尽量使用联接查询代替子查询。。如外联接、内联接等等。。

    1.1.3    没有必要时不要用DISTINCT和ORDER   BY,这些动作可以改在客户端执行。它们增加了额外的开销。这同UNION和UNION   ALL一样的道理。

    1.1.4    一般在GROUP BY和HAVING字句之前就能剔除多余的行,所以尽量不要用它们来做剔除行的工作。他们的执行顺序应该如下最优:
    select的Where字句选择所有合适的行,
    Group   By用来分组个统计行,Having字句用来剔除多余的分组。这样Group   By和Having的开销小,查询快.对于大的数据行进行分组和Having十分消耗资源。如果Group BY的目的不包括计算,只是分组,那么用Distinct更快。

    1.1.5    在大数据量的查询输出SELECT语句中尽量不要使用自定义函数,调用自定义函数的函数时系统调用是一个迭代过程,很影响查询输出性能的。在查询字段时尽可能使用小字段量输出,并在WHERE子句或者使用SELECT TOP10/1 PERCENT来限制返回的记录数,使用SET ROWCOUNT来限制操作的记录数,避免整表扫描。返回不必要的数据,不但浪费了服务器的I/O资源,加重了网络的负担,如果表很大的话,在表扫描期间将表锁住,禁止其他的联接访问,后过很严重的。

    1.1.6    尽量避免反复访问同一张或几张表,尤其是数据量较大的表,可以考虑先根据条件提取数据到临时表中,然后再做连接。

    1.1.7    注意WHERE子句写法,必须考虑语句顺序,应该根据索引顺序、范围大小来确定条件子句的前后顺序,尽可能的让字段顺序与索引顺序一致,范围从大到小。尽量不要在WHERE子句中的“=”左边进行函数、算术或其他表达式运算,否则系统可能无法正确使用索引。尽量使用“>=”,不使用“>”。

    1.1.8    尽量使用EXISTS代替SELECT COUNT1)来判断是否存在记录,COUNT函数只有在统计表中所有行数时使用,而且COUNT(1)比COUNT(*)效率更高。

    1.3.    临时表
    1.1.1    慎用临时表,临时表存储于tempdb库中,操作临时表时,会引起跨库操作。尽量用结果集和表变量来代替它。

    1.1.2    当用SELECT INTO时,它会锁住系统表(sysobjects,sysindexes等等),阻塞其他的连接的存取。创建临时表时用显示声明语句,在另一个连接中SELECT   *   from   sysobjects可以看到  SELECT   INTO   会锁住系统表, Create   table   也会锁系统表(不管是临时表还是系统表)。所以千万不要在事务内使用它!!!这样的话如果是经常要用的临时表请使用实表,或者临时表变量。

    1.1.3    在新建临时表时,如果一次性插入数据量很大,那么可以使用SELECT INTO代替CREATE TABLE避免LOG,提高速度;数据量不大时为了缓和系统表的资源,建议先CREATE TABLE然后INSERT。在使用了临时表后务必将所有的临时表显式删除,先TRUNCATE TABLE然后DROP TABLE,这样可以避免系统表的较长时间锁定。

    1.4.    存储过程、视图、函数
    1.1.1    在存储过程、函数中定义参数时,将参数的大小设置为合适即可,勿设置太大。这样开销很大。另外切忌用游标,效率太低。

    1.1.2    不要在一段SQL或者存储过程中多次使用相同的函数或相同的查询语句,这样比较浪费资源,建议将结果放在变量里再调用。这样更快。

    1.1.3    尽量少用视图和函数,用存储过程代替。存储过程是编译好、优化过,并且被组织到一个执行规划里、且存储在数据库中的 SQL语句,是控制流语言的集合,速度当然快。

    1.5.    索引
    1.1.1    创建合理的索引,对于插入或者修改比较频繁的表,尽量慎用索引。因为如果表中存在索引,插入和修改时也会引起全表扫描。索引一般使用于where后经常用作条件的字段上。

    1.1.2    根据查询条件建立优化的索引、优化访问方式,限制结果集的数据量。注意填充因子要适当(最好是使用默认值0)。索引应该尽量小,使用字节数小的列建里索引(参照索引的创建),不要对有限的几个值的字段建立单一索引(如性别字段)。

    1.1.3    如果使用LIKE进行查询的话,简单的使用INDEX是不行的,全文索引又太耗费空间。LIKE ‘N%’使用索引,LIKE%N’不使用索引。用LIKE‘%N%’查询时,查询耗时和字段值总长度成正比,所以不能用CHAR类型而采用VARCHAR。对于字段的值很长的字段建立全文索引。

    1.1.4    索引的创建要与应用结合考虑,建议大的OLTP表不要超过6个索引;尽可能的使用索引字段作为查询条件,尤其是聚簇索引,必要时可以通过INDEX INDEX_NAMEl来强制指定索引。避免对大表查询时进行 TABLE SCAN,必要时考虑新建索引。

    1.1.5    在使用索引字段作为条件时,如果该索引是联合索引,则必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引。

    ------------------------------------------------------------------------------------------------------------------------------------------------------

    Oracle调优

    1.当一个复杂的查询语句中多次用到同一个子查询时,可以用With语句来定义一个子查询语句块,而在后面的查询中重用这个块,这样可以避免多次重复查询。

    2.使用Merge更新或插入数据。使用Merge可以只执行一次语句就可以对一个表中的数据如果有相同的就更新没有则插入数据。

    3.使用索引,B tree索引、位图索引

    4.表分区。

    5.索引可能产生碎片,因为记录从表中删除时,相应也从表的索引中删除.表释放的空间可以再用,而索引释放的空间却不能再用.频繁进行删除操作的被索引的表, 应当阶段性地重建索引,以避免在索引中造成空间碎片,影响性能.在许可的条件下,也可以阶段性地truncate表,truncate命令删除表中所有记 录,也删除索引碎片.

    1)、freelists 和 freelist 组:他们负责ORACLE的处理表和索引的空间管理;

    2)、pctfree 及 pctused:该参数决定了freelists 和 freelist 组的行为,pctfree 和pctused 参数的唯一目的就是为了控制块如何在 freelists 中进出

      设置好pctfree 及 pctused对块在freelists的移走和读取很重要。

    查询消耗比较大的SQL

    select sql_text  from v$sql where disk_reads > 1000 or (executions > 0 and buffer_gets/executions > 30000); 

    6.使用绑定变量。

    7.列举几种表连接方式:  hash join/merge join/nest loop(cluster join)/index join

    ------------------------------------------------------------------------------------------------------------------------------------------------------

  • 相关阅读:
    [WPF]WPF3.5 SP1 TreeView的Bug
    2010年自我总结
    LINQ to SQL 异常:SQL Server does not handle comparison of NText, Text, Xml, or Image data types
    [翻译] javascript缩进
    【转】疑难解答:调试 Windows 服务
    【翻译】MSDN文档对类DefaultValueAttribute的描述有些混淆
    二叉排序树——算法系列
    索引查找——算法系列
    android软电话开发预备知识
    adb server is out of date.killing
  • 原文地址:https://www.cnblogs.com/heartstage/p/3392841.html
Copyright © 2011-2022 走看看