服务器 频道

ORACLE学习笔记--性能优化FAQ

11.怎么样创建使用虚拟索引
  可以使用nosegment选项,如
  create index virtual_index_name on table_name(col_name) nosegment;

  如果在哪个session需要测试虚拟索引,可以利用隐含参数来处理
  alter session set "_use_nosegment_indexes" = true;

  就可以利用explain plan for select ……来看虚拟索引的效果,利用@$ORACLE_HOME/rdbms/admin/utlxpls查看执行计划,最后,根据需要,我们可以删除虚拟索引,如普通索引一样
  drop index virtual_index_name;

  注意:虚拟索引并不是物理存在的,所以虚拟索引并不等同于物理索引,不要用自动跟踪去测试虚拟索引,因为那是实际执行的效果,是用不到虚拟索引的。

12.怎样监控无用的索引
  Oracle 9i以上,可以监控索引的使用情况,如果一段时间内没有使用的索引,一般就是无用的索引
  语法为:
  开始监控:alter index index_name monitoring usage;
  检查使用状态:select * from v$object_usage;
  停止监控:alter index index_name nomonitoring usage;

  当然,如果想监控整个用户下的索引,可以采用如下的脚本:
  set heading off
  set echo off
  set feedback off
  set pages 10000
  spool start_index_monitor.sql
  SELECT ''alter index ''||owner||''.''||index_name||'' monitoring usage;''
  FROM dba_indexes
  WHERE owner = USER;
  spool off
  set heading on
  set echo on
  set feedback on
  ------------------------------------------------
  set heading off
  set echo off
  set feedback off
  set pages 10000
  spool stop_index_monitor.sql
  SELECT ''alter index ''||owner||''.''||index_name||'' nomonitoring usage;''
  FROM dba_indexes
  WHERE owner = USER;
  spool off
  set heading on
  set echo on
  set feedback on


13.怎么样能固定我的执行计划
  可以使用OUTLINE来固定SQL语句的执行计划,用如下语句可以创建一个OUTLINE
  Create oe replace outline OutLn_Name on
  Select Col1,Col2 from Table
  where .......

  如果要删除Outline,可以采用
  Drop Outline OutLn_Name;

  对于已经创建了的OutLine,存放在OUTLN用户的OL$HINTS表下面,对于有些语句,你可以使用update outln.ol$hints来更新outline,如

  update outln.ol$hints(ol_name,''TEST1'',''TEST2'',''TEST2'',''TEST1)
  where ol_name in (''TEST1'',''TEST2'');

  这样,你就把Test1 OUTLINE与Test2 OUTLINE互换了,如果想利用已经存在的OUTLINE,需要设置以下参数
  Alter system/session set Query_rewrite_enabled = true
  Alter system/session set use_stored_outlines = true

14.v$sysstat中的class分别代表什么
  统计类别
  1 代表事例活动
  2 代表Redo buffer活动
  4 代表锁
  8 代表数据缓冲活动
  16 代表OS活动
  32 代表并行活动
  64 代表表访问
  128 代表调试信息

15.怎么杀掉特定的数据库会话
  Alter system kill session ''sid,serial#'';
  或者
  alter system disconnect session ''sid,serial#'' immediate;

  在win上,还可以采用oracle提供的orakill杀掉一个线程(其实就是一个Oracle进程)
  在Linux/Unix上,可以直接利用kill杀掉数据库进程对应的OS进程

16.怎么快速查找锁与锁等待
  数据库的锁是比较耗费资源的,特别是发生锁等待的时候,我们必须找到发生等待的锁,有可能的话,杀掉该进程。
  这个语句将查找到数据库中所有的DML语句产生的锁,还可以发现,任何DML语句其实产生了两个锁,一个是表锁,一个是行锁。
  可以通过alter system kill session ‘sid,serial#’来杀掉会话

  SELECT /*+ rule */ s.username,
  decode(l.type,''TM'',''TABLE LOCK'',''TX'',''ROW LOCK'',NULL) LOCK_LEVEL,
  o.owner,o.object_name,o.object_type,
  s.sid,s.serial#,s.terminal,s.machine,s.program,s.osuser
  FROM v$session s,v$lock l,dba_objects o
  WHERE l.sid = s.sid
  AND l.id1 = o.object_id(+)
  AND s.username is NOT NULL

  如果发生了锁等待,我们可能更想知道是谁锁了表而引起谁的等待,以下的语句可以查询到谁锁了表,而谁在等待。
  SELECT /*+ rule */ lpad('' '',decode(l.xidusn ,0,3,0))||l.oracle_username User_name,
  o.owner,o.object_name,o.object_type,s.sid,s.serial#
  FROM v$locked_object l,dba_objects o,v$session s
  WHERE l.object_id=o.object_id
  AND l.session_id=s.sid
  ORDER BY o.object_id,xidusn DESC

  以上查询结果是一个树状结构,如果有子节点,则表示有等待发生。如果想知道锁用了哪个回滚段,还可以关联到V$rollname,其中xidusn就是回滚段的USN

  [Q] 如何有效的删除一个大表(extent数很多的表)
  [A] 一个有很多(100k)extent的表,如果只是简单地用drop table的话,会很大量消耗CPU(Oracle要对fet$、uet$数据字典进行操作),可能会用上几天的时间,较好的方法是分多次删除extent,以减轻这种消耗:
  1. truncate table big-table reuse storage;
  2. alter table big-table deallocate unused keep 2000m ( 原来大小的n-1/n);
  3. alter table big-table deallocate unused keep 1500m ;
  ....
  4. drop table big-table;

17.如何收缩临时数据文件的大小
  9i以下版本采用
  ALTER DATABASE DATAFILE ''file name'' RESIZE 100M类似的语句
  9i以上版本采用
  ALTER DATABASE TEMPFILE ''file name'' RESIZE 100M
  注意,临时数据文件在使用时,一般不能收缩,除非关闭数据库或断开所有会话,停止对临时数据文件的使用。


18.怎么清理临时段
  可以使用如下办法
  <1>、 使用如下语句查看一下认谁在用临时段
  SELECT username,sid,serial#,sql_address,machine,program,
  tablespace,segtype, contents
  FROM v$session se,v$sort_usage su
  WHERE se.saddr=su.session_addr

   <2>、 那些正在使用临时段的进程
  SQL>Alter system kill session ''sid,serial#'';

  <3>、把TEMP表空间回缩一下
  SQL>Alter tablespace TEMP coalesce;


  还可以使用诊断事件
  <1>、 确定TEMP表空间的ts#
  SQL> select ts#, name FROM v$tablespace;
  TS# NAME
  -----------------------
  0 SYSYEM
  1 RBS
  2 USERS
  3* TEMP
  ...

  <2>、 执行清理操作
  alter session set events ''immediate trace name DROP_SEGMENTS level TS#+1''
  说明:
  temp表空间的TS# 为 3*, So TS#+ 1= 4,如果想清除所有表空间的临时段,则,TS# = 2147483647

19.怎么样dump数据库内部结构,如上面显示的控制文件的结构
  常见的有
  1、分析数据文件块,转储数据文件n的块m
  alter system dump datafile n block m

  2、分析日志文件
  alter system dump logfile logfilename;

  3、分析控制文件的内容
  alter session set events ''immediate trace name CONTROLF level 10''

  4、分析所有数据文件头
  alter session set events ''immediate trace name FILE_HDRS level 10''

  5、分析日志文件头
  alter session set events ''immediate trace name REDOHDR level 10''

  6、分析系统状态,最好每10分钟一次,做三次对比
  alter session set events ''immediate trace name SYSTEMSTATE level 10''

  7、分析进程状态
  alter session set events ''immediate trace name PROCESSSTATE level 10''

  8、分析Library Cache的详细情况
  alter session set events ''immediate trace name library_cache level 10''


20.如何获得所有的事件代码
  事件代码范围一般从10000 to 10999,以下列出了这个范围的事件代码与信息
  SET SERVEROUTPUT ON
  DECLARE
  err_msg VARCHAR2(120);
  BEGIN
  dbms_output.enable (1000000);
  FOR err_num IN 10000..10999
  LOOP
  err_msg := SQLERRM (-err_num);
  IF err_msg NOT LIKE ''%Message ''||err_num||'' not found%'' THEN
  dbms_output.put_line (err_msg);
  END IF;
  END LOOP;
  END;
  /

  在Unix系统上,事件信息放在一个文本文件里
  $ORACLE_HOME/rdbms/mesg/oraus.msg
  可以用如下脚本查看事件信息
  event=10000
  while [ $event -ne 10999 ]
  do
  event=`expr $event + 1`
  oerr ora $event
  done

  对于已经确保的/正在跟踪的事件,可以用如下脚本获得
  SET SERVEROUTPUT ON
  DECLARE
  l_level NUMBER;
  BEGIN
  FOR l_event IN 10000..10999
  LOOP
  dbms_system.read_ev (l_event,l_level);
  IF l_level > 0 THEN
  dbms_output.put_line (''Event ''||TO_CHAR (l_event)||
  '' is set at level ''||TO_CHAR (l_level));
  END IF;
  END LOOP;
  END;
  /


21.什么是STATSPACK,我怎么使用它?
  Statspack是Oracle 8i以上提供的一个非常好的性能监控与诊断工具,基本上全部包含了BSTAT/ESTAT的功能,更多的信息,可以参考附带文档$ORACLE_HOME/rdbms/admin/spdoc.txt。

  安装Statspack:
  cd $ORACLE_HOME/rdbms/admin
  sqlplus "/ as sysdba" @spdrop.sql -- 卸载,第一次可以不需要
  sqlplus "/ as sysdba" @spcreate.sql -- 需要根据提示输入表空间名

  使用Statspack:
  sqlplus perfstat/perfstat
  exec statspack.snap; -- 进行信息收集统计,每次运行都将产生一个快照号
  -- 获得快照号,必须要有两个以上的快照,才能生成报表
  select SNAP_ID, SNAP_TIME from STATS$SNAPSHOT;
  @spreport.sql -- 输入需要查看的开始快照号与结束快照号

  其他相关脚本s:
  spauto.sql - 利用dbms_job提交一个作业,自动的进行STATPACK的信息收集统计
  sppurge.sql - 清除一段范围内的统计信息,需要提供开始快照与结束快照号
  sptrunc.sql - 清除(truncate)所有统计信息


22. SQL语句的优化方法
  <1> /*+ALL_ROWS*/
  表明对语句块选择基于开销的优化方法,并获得非常好的吞吐量,使资源消耗最小化.
  例如:
  SELECT /*+ALL+_ROWS*/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO=''CCBZZP'';

  <2>. /*+FIRST_ROWS*/
  表明对语句块选择基于开销的优化方法,并获得非常好的响应时间,使资源消耗最小化.
  例如:
  SELECT /*+FIRST_ROWS*/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE    EMP_NO=''CCBZZP'';

  <3>. /*+CHOOSE*/
  表明如果数据字典中有访问表的统计信息,将基于开销的优化方法,并获得非常好的的吞吐量;
  表明如果数据字典中没有访问表的统计信息,将基于规则开销的优化方法;
  例如:
  SELECT /*+CHOOSE*/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO=''CCBZZP'';

  <4>. /*+RULE*/
  表明对语句块选择基于规则的优化方法.
  例如:
  SELECT /*+ RULE */ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO=''CCBZZP'';  

  <5>. /*+FULL(TABLE)*/
  表明对表选择全局扫描的方法.
  例如:
  SELECT /*+FULL(A)*/ EMP_NO,EMP_NAM FROM BSEMPMS A WHERE EMP_NO=''CCBZZP'';

  <6>. /*+ROWID(TABLE)*/
  提示明确表明对指定表根据ROWID进行访问.
  例如:
  SELECT /*+ROWID(BSEMPMS)*/ * FROM BSEMPMS WHERE ROWID>=''AAAAAAAAAAAAAA''
   AND EMP_NO=''CCBZZP'';

  <7>. /*+CLUSTER(TABLE)*/
  提示明确表明对指定表选择簇扫描的访问方法,它只对簇对象有效.
  例如:
  SELECT  /*+CLUSTER */ BSEMPMS.EMP_NO,DPT_NO FROM BSEMPMS,BSDPTMS
  WHERE DPT_NO=''TEC304'' AND BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;

  <8>. /*+INDEX(TABLE INDEX_NAME)*/
  表明对表选择索引的扫描方法.
  例如:
  SELECT /*+INDEX(BSEMPMS SEX_INDEX) USE SEX_INDEX BECAUSE THERE ARE FEWMALE    BSEMPMS */  FROM BSEMPMS WHERE SEX=''M'';

  <9>. /*+INDEX_ASC(TABLE INDEX_NAME)*/
  表明对表选择索引升序的扫描方法.
  例如:
  SELECT /*+INDEX_ASC(BSEMPMS PK_BSEMPMS) */  FROM BSEMPMS WHERE DPT_NO=''CCBZZP'';

  <10>. /*+INDEX_COMBINE*/
  为指定表选择位图访问路经,如果INDEX_COMBINE中没有提供作为参数的索引,将选择出位图索引的
  布尔组合方式.
  例如:
  SELECT /*+INDEX_COMBINE(BSEMPMS SAL_BMI HIREDATE_BMI)*/ * FROM BSEMPMS
  WHERE SAL<5000000 AND HIREDATE<SYSDATE;

  <11>. /*+INDEX_JOIN(TABLE INDEX_NAME)*/
  提示明确命令优化器使用索引作为访问路径.
  例如:
  SELECT /*+INDEX_JOIN(BSEMPMS SAL_HMI HIREDATE_BMI)*/ SAL,HIREDATE
  FROM BSEMPMS WHERE SAL<60000;

  <12>. /*+INDEX_DESC(TABLE INDEX_NAME)*/
  表明对表选择索引降序的扫描方法.
  例如:
  SELECT /*+INDEX_DESC(BSEMPMS PK_BSEMPMS) */  FROM BSEMPMS WHERE    DPT_NO=''CCBZZP'';

  <13>. /*+INDEX_FFS(TABLE INDEX_NAME)*/
  对指定的表执行快速全索引扫描,而不是全表扫描的办法.
  例如:
  SELECT /*+INDEX_FFS(BSEMPMS IN_EMPNAM)*/ * FROM BSEMPMS WHERE DPT_NO=''TEC305'';


  <14>. /*+ADD_EQUAL TABLE INDEX_NAM1,INDEX_NAM2,...*/
  提示明确进行执行规划的选择,将几个单列索引的扫描合起来.
  例如:
  SELECT /*+INDEX_FFS(BSEMPMS IN_DPTNO,IN_EMPNO,IN_SEX)*/ * FROM BSEMPMS WHERE EMP_NO=''CCBZZP'' AND DPT_NO=''TDC306'';

  <15>. /*+USE_CONCAT*/
  对查询中的WHERE后面的OR条件进行转换为UNION ALL的组合查询.
  例如:
  SELECT /*+USE_CONCAT*/ * FROM BSEMPMS WHERE DPT_NO=''TDC506'' AND SEX=''M'';

  <16>. /*+NO_EXPAND*/
  对于WHERE后面的OR 或者IN-LIST的查询语句,NO_EXPAND将阻止其基于优化器对其进行扩展.
  例如:
  SELECT /*+NO_EXPAND*/ * FROM BSEMPMS WHERE  DPT_NO=''TDC506'' AND SEX=''M'';

  <17>. /*+NOWRITE*/
  禁止对查询块的查询重写操作.

  <18>. /*+REWRITE*/
  可以将视图作为参数.

  <19>. /*+MERGE(TABLE)*/
  能够对视图的各个查询进行相应的合并.
  例如:
  SELECT /*+MERGE(V) */ A.EMP_NO,A.EMP_NAM,B.DPT_NO FROM BSEMPMS A (SELET DPT_NO,AVG(SAL) AS AVG_SAL FROM BSEMPMS B GROUP BY DPT_NO) V WHERE A.DPT_NO=V.DPT_NO AND A.SAL>V.AVG_SAL;

  <20>. /*+NO_MERGE(TABLE)*/
  对于有可合并的视图不再合并.
  例如:
  SELECT /*+NO_MERGE(V) */ A.EMP_NO,A.EMP_NAM,B.DPT_NO FROM BSEMPMS A (SELET DPT_NO,AVG(SAL) AS AVG_SAL FROM BSEMPMS B GROUP BY DPT_NO) V WHERE A.DPT_NO=V.DPT_NO AND A.SAL>V.AVG_SAL;
  

0
相关文章