“原谅他们是上帝的事,我们的任务是负责送他们见上帝。”
SQL 语句
查询碎片程度高(实际使用率小于30%)的表
可以收缩的表条件为什么block>100
,因为一些很小的表,只有几行数据实际大小很小,但是block一次性分配就是5个(11g开始默认一次性分配1M的block大小了,见create table storged的NEXT参数),5个block相对于几行小表数据来说就相差太大了。
算法中/0.9
是因为块的pfree
一般为10%
,所以一个块最多只用了90%
,而且一行数据大于8KB
时容易产生行链接,把一行分片存储,一样的一个块连90%
都用不满 ,AVG_ROW_LEN
还是比较准的,比如个人实验情况一表6个字段,一个numbe
r,其他5个都是char(100)
但是实际数据都是’1111111’7位,AVG_ROW_LEN显示依然为513 。
1 | SELECT TABLE_NAME, |
查询索引碎片的比例
索引删除行数除以索引总行数的百分比>30%即认为索引碎片大,也就是需要重建的索引
1 | SELECT name, |
集群因子clustering_factor高的表
集群因子越接近块数越好,接近行数则说明索引列的列值相等的行分布极度散列,可能不走索引扫描而走全表扫描 :
方法一:
1 | SELECT tab.table_name, |
方法二:
1 | SELECT tab.owner, |
根据sid查spid或根据spid查sid
1 | SELECT s.sid, |
根据sid
查看具体的sql语句
,不要加条件v$session.status='ACTIVE'
,比如toad对同一数据库开两个连接会话,都执行了一些语句,其中一个窗口查询select * from v$session
时会发现另一个窗口在v$session.status
是INACTIVE
,并不代表另一个窗口没有执行过sql语句,而当前窗口是active状态,对应的sql_id对应的语句就是select * from v$session
而不是之前执行过的sql语句,ACTIVE表示当前正在执行sql。
一个sid可能执行过很多个sql,所以有时需要的sql通过如下查不到是正常的,比如查询到某死锁源sid,通过如下查询可能只是个select语句
,而真正引起死锁的sql却查不到,是因为可能这个sid持续了很长时间,这个sid之前执行的一些sql在v$sql
可能已经被清除了。
方法一:
1 | SELECT username, |
如果上面语句执行太慢,则按如下两步
1 | SELECT sql_hash_value, |
- XX为上面
sql_hash_value
,如果sql_hash_value为0
,则XX为上面prev_hash_value
根据spid查询具体的sql语句(不要加条件v$session.status=’ ACTIVE’,比如toad对同一数据库开两个连接会话,都执行了一些语句,其中一个窗口查询select from v$session时会发现另一个窗口在v$session.status是INACTIVE,并不代表另一个窗口没有执行过sql语句,而当前窗口是active状态,对应的sql_id对应的语句就是select from v$session而不是之前执行过的sql语句,ACTIVE表示当前正在执行sql。)
1 | SELECT ss.SID, |
查看历史session_id的SQL来自哪个IP
查看trace文件名就可以知道spid,trace文件里面有sid和具体sql,如果trace存在incident,那trace就看不到具体sql,但是可以在incident文件中看到具体的sql,如DW_ora_17751.trc中17751就是spid,里面有这样的内容Incident 115 created, dump file: /XX/incident/incdir_115/DW_ora_17751_i115.trc,那么在DW_ora_17751_i115.trc就可以看到具体的sql语句)
DB_ora_29349.trc中出现
1 | *** SESSION ID:(5057.12807) 2016-10-26 14:45:52.726 |
通过表V$ACTIVE_SESSION_HISTORY
来查
1 | SELECT a.sql_id, |
查询上面的machine的IP
1 | SELECT s.sid, |
通过上面的spid
在oracle服务器上执行netstat -anp |grep spid
即可
1 | [oracle@dwdb trace]$ netstat -anp |grep 17630 |
出现两个,说明来自220,连接了228数据库服务器,但是又通过228服务器的dblink去连接了16服务器
查询死锁堵塞的会话sid
最简单的一个SQL
1 | select * from V$SESSION_BLOCKERS |
最常用的一个SQL
1 | SELECT sid, |
可以把两者SID
放入v$session
,发现LOGON_TIME
字段FINAL_BLOCKING_SESSION
比SID
要早
BLOCKING_SESSION:Session identifier of the blocking session. This column is valid only if BLOCKING_SESSION_STATUS has the value VALID.
FINAL_BLOCKING_SESSION:Session identifier of the blocking session. This column is valid only if FINAL_BLOCKING_SESSION_STATUS has the value VALID.
如果遇到RAC环境
,一定要用gv$
来查,并且执行alter system kill session 'sid,serial#'
要到RAC
对应的实例上去执行
把上面被堵塞会话的sid
代入如下语句,可以发现锁住的对象和对象的哪一行(如果sid
是堵塞源的会话,则 row_wait_obj#=-1
,表示锁持有者,就是死锁源了 )
1 | SELECT s.sid, |
查询锁住的DDL对象
1 | SELECT d.session_id, |
查询超过两个小时的不活动会话
1 | SELECT s.sid, |
查询堵塞别的会话超过30分钟且自身是不活动的会话
1 | SELECT username, |
查询可能存在连接池空闲初始配置过大的连接(来自同一台机器的同一个程序的状态为INACTIVE的连接非常多)
1 | SELECT count(ss.SID), |
查询当前正在执行的sql
1 | SELECT s.sid, |
查询正在执行的SCHEDULER_JOB
1 | SELECT owner, |
查询正在执行的dbms_job
1 | SELECT job, |
查询一个会话session、process平均消耗多少PGA内存,查看下面avg_used_M值
1 | SELECT round(sum(pga_used_mem)/1024/1024, |
TOP 10 执行次数排序
1 | SELECT * |
TOP 10 物理读排序(消耗IO排序,即最差性能SQL、低效SQL排序)
1 | SELECT * |
注意:不要使用DISK_READS/ EXECUTIONS来排序,因为任何一条语句不管执行几次都会耗逻辑读和cpu,可能不会耗物理读(遇到LRU还会耗物理读,LRU规则是执行最不频繁的且最后一次执行时间距离现在最久远的就会被交互出buffer cache),是因为buffer cache存放的是数据块,去数据块里找行一定会消耗cpu和逻辑读的。Shared pool执行存放sql的解析结果,sql执行的时候只是去share pool中找hash value,如果有匹配的就是软解析。所以物理读逻辑读是在buffer cache中,软解析硬解析是在shared pool
TOP 10 逻辑读排序(消耗内存排序)
1 | SELECT * |
注意:不要使用BUFFER_GETS/ EXECUTIONS来排序,因为任何一条语句不管执行几次都会耗逻辑读和cpu,可能不会耗物理读(遇到LRU还会耗物理读,LRU规则是执行最不频繁的且最后一次执行时间距离现在最久远的就会被交互出buffer cache),是因为buffer cache存放的是数据块,去数据块里找行一定会消耗cpu和逻辑读的。Shared pool执行存放sql的解析结果,sql执行的时候只是去share pool中找hash value,如果有匹配的就是软解析。所以物理读逻辑读是在buffer cache中,软解析硬解析是在shared pool)
TOP 10 CPU排序(单位秒=cpu_time/1000000)
1 | SELECT * |
注意:不要使用CPU_TIME/ EXECUTIONS来排序,因为任何一条语句不管执行几次都会耗逻辑读和cpu,可能不会耗物理读(遇到LRU还会耗物理读,LRU规则是执行最不频繁的且最后一次执行时间距离现在最久远的就会被交互出buffer cache),是因为buffer cache存放的是数据块,去数据块里找行一定会消耗cpu和逻辑读的。Shared pool执行存放sql的解析结果,sql执行的时候只是去share pool中找hash value,如果有匹配的就是软解析。所以物理读逻辑读是在buffer cache中,软解析硬解析是在shared pool。
查询等待事件
1 | SELECT event, |
以上sql发现wait_class#=6的是空闲等待
1 | SELECT * |
能查出等待的对象是否来自数据文件(如果以上查到p1text是file#或file number)
1 | SELECT * |
把上面第二个sql结果的p1、p2值代入上述sql的file_id、block_id
通过AWR
的top sql
或v$sql.sql_text
查看是否有该对象的语句,检查该语句的执行计划就可以查出问题所在。
查询当前正在消耗临时空间的sql语句
方法一:
1 | SELECT DISTINCT se.username, |
查询因PGA不足而使用临时表空间的最频繁的10条SQL语句
1 | SELECT * |
查询正在消耗PGA的SQL
1 | SELECT s.sql_text, |
查询需要使用绑定变量的sql,10G以后推荐第二种
注意:任何一条执行过的语句不管执行了几次在V$SQL中都只有一条记录,V$SQL中会记录执行了几次。两条一模一样的语句但是在不同的schema下执行的两种结果,如select from t1.test在sye、system下执行则V$SQL只有一条记录(谁先执行则PARSING_SCHEMA_NAME显示谁)。如在sys和system都执行select from test则V$SQL中有两条记录,两条记录的CHILD_NUMBER和PARSING_SCHEMA_NAME不一样。
同一个用户下执行一样的语句如果大小写不一样或加了hint的话则会出现多个V$SQL记录,说明V$SQL对应的sql语句必须一模一样,如果alter system flush shared_pool(主站慎用)后再执行一样的语句,发现语句在V$SQL中的SQL_ID和HASH_VALUE与之前的一样,说明SQL_ID和HASH_VALUE应该是oracle自己的一套算法来的,只是根据sql语句内容来进行转换,sql语句不变则SQL_ID和HASH_VALUE也不变。
第一种
1 | SELECT * |
第二种
1 | count(1)>10表示类语句运行了10次以上SELECT sql_id, |
查看数据文件可用百分比
dba_free_space
并不会包含所有file_id
,如果该数据文件满了,则 dba_free_space.file_id
没有该数据文件,所以以下sql中 a.file_id=b.file_id
的条件过滤后是不会有所有file_id的
1 | SELECT b.file_id, |
- 如下为标准版
1
2
3
4
5
6
7
8
9
10SELECT b.file_id,
b.tablespace_name,
b.file_name,
b.AUTOEXTENSIBLE,
ROUND(b.MAXBYTES/1024/1024/1024,
2) ||'G' "文件最大可用总容量", ROUND(b.bytes/1024/1024/1024,2) ||'G' "文件当前总容量", ROUND((b.bytes-sum(nvl(a.bytes,0)))/1024/1024/1024,2)||'G' "文件当前已用容量", ROUND((decode(AUTOEXTENSIBLE,'NO',b.BYTES,b.MAXBYTES)+sum(nvl(a.bytes,0))-b.bytes)/1024/1024/1024,2)||'G' "文件可用容量", ROUND((decode(AUTOEXTENSIBLE,'NO',b.BYTES,b.MAXBYTES)+sum(nvl(a.bytes,0))-b.bytes)/(decode(AUTOEXTENSIBLE,'NO',b.BYTES,b.MAXBYTES)),2)*100||'%' "文件可用百分比"
FROM dba_free_space a,dba_data_files b
WHERE a.file_id=b.file_id
GROUP BY b.tablespace_name,b.file_name,b.file_id,b.bytes,b.AUTOEXTENSIBLE,b.MAXBYTES
ORDER BY decode(AUTOEXTENSIBLE,'NO',b.BYTES,b.MAXBYTES)+sum(nvl(a.bytes,0))-b.bytes;
查看数据库文件的实际总量,单位G
1 | SELECT a.datafile_size+b.tempfile_size-c.free_size |
查看表空间可用百分比( dba_free_space不会包含所有tablespace,如果一个表空间的数据文件都满了,则这个表空间不会出现在dba_free_space中 )
1 | SELECT b.tablespace_name, |
- 如下为标准版
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18SELECT b.tablespace_name,
a.maxsize max_M,
a.total total_M,
b.free free_M,
round(((a.maxsize+b.free-a.total)/a.maxsize)*100) "% Free"
FROM
(SELECT tablespace_name,
sum(bytes/(1024*1024)) total ,
sum((decode(AUTOEXTENSIBLE,
'NO',BYTES,MAXBYTES))/(1024*1024)) maxsize
FROM dba_data_files
GROUP BY tablespace_name) a,
(SELECT tablespace_name,
round(sum(bytes/(1024*1024))) free
FROM dba_free_space
GROUP BY tablespace_name) b
WHERE a.tablespace_name = b.tablespace_name
ORDER BY "% Free";
查看临时表空间使用率
方法一
1 | SELECT temp_used.tablespace_name, |
方法二
1 | SELECT a.tablespace_name, |
查询undo表空间使用情况
1 | SELECT tablespace_name, |
查询使用undo比较多的SQL
1 | SELECT *from |
估计undo需要多大
1 | SELECT (UR * (UPS * DBS)) AS "Bytes" |
产生undo的当前活动会话是哪些
方法一
1 | SELECT a.inst_id, |
方法二
1 | SELECT s.sid, |
查看ASM磁盘组使用率
1 | SELECT name, |
统计每个用户使用表空间率
1 | SELECT c.owner "用户", |
查看闪回区\快速恢复区空间使用率
1 | SELECT sum(percent_space_used)||'%' "已使用空间比例" |
查看僵死进程,分两种(一种是会话不在的,另一种是会话标记为killed的但是会话还在的)
alter system kill session
一执行则session
即标记为KILLED
,但是如果会话产生的数据量大则这个kill可能会比较久,在这个过程中session标记为KILLED但是这个会话还在V$session
中,则V$session.paddr
还在,所以可以匹配到V$process.addr
,所以process进程还在;当kill过程执行完毕,则这个会话即不在V$session
中
会话不在的
1 | SELECT * |
会话还在的,但是会话标记为killed
1 | SELECT * |
再根据上述结果中的SPID通过如下命令可以查看到process的启动时间
1 | ps auxw|head -1;ps auxw|grep SPID |
查看行迁移或行链接的表
1 | SELECT * |
数据缓冲区命中率(百分比小于90就要加大db_cache_size)
方法一
1 | SELECT a.VALUE+b.VALUE logical_reads, |
方法二
1 | SELECT DB_BLOCK_GETS+CONSISTENT_GETS Logical_reads, |
共享池命中率(百分比小于90就要加大shared_pool_size)
以下两者可以根据个人理解运用
1 | SELECT sum(pinhits)/sum(pins)*100 |
查询归档日志切换频率
1 | SELECT sequence#, |
查询lgwr进程写日志时每执行一次lgwr需要多少秒,在state是waiting的情况下,某个等待编号seq#下,seconds_in_wait达多少秒,就是lgwr进程写一次IO需要多少秒
1 | SELECT event, |
查询没有索引的表
1 | SELECT table_name |
查询一个AWR周期内的平均session数、OS平均负载、平均db time、平均每秒多少事务
1 | SELECT to_char(max(BEGIN_TIME), |
- Database Time Per Sec对应值的单位是百分一秒/每秒
- (/100)(ceil((max(end_time)-max(BEGIN_TIME))246060))是代表每个snap周期内的总秒数,oracle 两个时间相减默认的是天数,2460*60 为相差的秒数
- 这个SQL查到的DB TIME比较准确,和awr上面的db time比较一致
查询产生热块较多的对象
x$bh .tch(Touch)表示访问次数越高,热点快竞争问题就存在
1 | SELECT e.owner, |
手工创建快照的语句
1 | exec dbms_workload_repository.create_snapshot; |
AWR设置每隔30分钟收集一次报告,保留14天的报告
1 | exec DBMS_WORKLOAD_REPOSITORY.MODIFY_SNAPSHOT_SETTINGS(retention=>14*24*60, interval=>30); |
AWR基线查看和创建
1 | select * from dba_hist_baseline; |
导出AWR报告的SQL语句
1 | SELECT * |
导出最新ADDM的报告(需要sys用户)
1 | SELECT dbms_advisor.get_task_report(task_name) |
查询某个SQL的执行计划
1 | SELECT * |
上面的0表示v$sql.child_number
为0,如果一个sql_id在v$sql中有多行说明有多个child_number,要看哪儿child_number的执行计划,就写哪个的值,比如要看child_number为2的执行计划,就把上面sql的0改为2 。
官方文档对display_cursor这个函数的说明里面没有advanced这个参数值,只有BASIC、TYPICAL、ALL这几个,不过实践中发现advanced这个参数值显示的内容比这几个参数值显示的都多。
1 | SELECT * |
创建xplan包,再执行
1 | SQL> CREATE PUBLIC SYNONYM XPLAN FOR SYS.XPLAN; |
查询Rman的配置信息
1 | SELECT NAME, |
查询Rman备份集详细信息(未过期的,过期并已删除的查不到)
1 | SELECT B.RECID BackupSet_ID, |
查询Rman备份进度
1 | SELECT SID, |
查询执行过全表扫描的sql语句的SQL_ID和sql_fulltext
1 | SELECT s.sid, |
查询死事务需要多长的回滚时间
1 | X$KTUXE:[K]ernel [T]ransaction [U]ndo Transa[x]tion [E]ntry (table)` |
X$KTUXE
表的一个重要功能是,可以获得无法通过v$transaction
来观察的死事务信息,当一个数据库发生异常中断,或者进行延迟事务恢复时,数据库启动后,无法通过V$TRANSACTION来观察事务信息,但是X$KTUXE
可以帮助我们获得这些信息。该表中的KTUXECFL代表了事务的Flag标记,通过这个标记可以找到那些Dead事务:
1 | SQL> select distinct KTUXECFL,count(*) from x$ktuxe group by KTUXECFL; |
KTUXESIZ用来记录事务使用的回滚段块数,可以通过观察这个字段来评估恢复进度,例如如下事务回滚经过测算需要大约3小时
1 | SQL> select ADDR,KTUXEUSN,KTUXESLT,KTUXESQN,KTUXESIZ from x$ktuxe where KTUXECFL ='DEAD'; |
把XXX用户下面的某些YYY表赋权给user,XXX\YYY要大写
- XXX要大写
1
2
3
4
5
6
7
8declare tablename varchar2(200);
begin
for x IN (SELECT * FROM dba_tables where owner='XXX' and table_name like '%YYY%') loop
tablename:=x.table_name;
dbms_output.put_line('GRANT SELECT ON XXX.'||tablename||' to user');
EXECUTE IMMEDIATE 'GRANT SELECT ON XXX.'||tablename||' TO user';
end loop;
end;
Oracle查出一个用户具有的所有系统权限和对象权限
系统权限(和用户自己查询select * from session_privs
的结果一致)
1 | SELECT * |
对象权限(和用户自己查询select * FROM TABLE_PRIVILEGES where GRANTEE='当前用户'
的结果一致)
1 | SELECT * |
查询某个用户拥有的角色
1 | SELECT * |
查询拥有DBA角色权限的用户
1 | SELECT * |
查询某个角色拥有的系统权限
1 | SELECT * |
清除某个SQL的执行计划
1 | Exec DBMS_SHARED_POOL.PURGE('v$sqlarea.ADDRESS,v$sqlarea.HASH_VALUE','c') |
查询密码是否有过期限制,默认是180天,一般修改为unlimited
1 | SELECT * |
查询和修改隐含参数(必须在sysdba权限下操作)
1 | SELECT a.ksppinm name, |
不加sid则说明在默认在RAC的所有实例中修改
需要注意的是一定要加上双引号, 另外引号内不能有空格, 只能包含参数的名字
评估SGA该设置多少
1 | SELECT SGA_SIZE |
查看shared pool还剩多少
1 | SELECT * |
统计所有表的容量大小(含分区字段、LOB字段)
一般先执行select distinct SEGMENT_TYPE from dba_segments where owner<>'SYS' and tablespace_name<>'SYSAUX'
查看到所有的segment_type
1 | SELECT owner, |
查看当前会话的SID
1 | SELECT * |
查询某个SID的某个统计信息,比如consistent gets一致性读
1 | SELECT A.SID, |
V$SYSSTAT
统计整个DB的统计信息,V$SYSSTAT
已经取代了V$STATNAME
,并且多了VALUE这一列V$SESSTAT
统计每个用户的统计信息
查询某个SID的某个等待事件的信息,比如log file sync
1 | SELECT A.SID, |
RAC跨节点杀会话
1 | alter system kill session 'SID,serial#,@1' --杀掉1节点的进程 |
Truncate 分区的SQL
1 | ALTER TABLE table_name TRUNCATE PARTITION p1 DROP STORAGE UPDATE GLOBAL INDEXES; |
Drop分区的SQL
1 | ALTER TABLE table_name DROP PARTITION p1 UPDATE GLOBAL INDEXES; |
DATAGUARD主备延迟多少时间的查询方法
1 | 备 库sqlplus> |
查看某个包或存储过程是否正在被调用,如果如下有结果,则此时不能编译,否则会锁住
1 | SELECT * |
查询数据库打补丁的记录
1 | SELECT * |
查询某表的索引字段的distinct行数和CLUSTERING_FACTOR信息
1 | SELECT a.table_name, |
查询某表的所有字段的distinct行数
1 | SELECT a.table_name, |
查询5G以上空闲空间可以进行收缩的数据文件
1 | SELECT 'alter database datafile ''' || a.file_name || ''' resize ' || round(a.filesize -(a.filesize - c.hwmsize) * 0.8) || 'M;', a.filesize || 'M' AS "数据文件的总大小", c.hwmsize || 'M' AS "数据文件的实用大小" |
参考
- 廖学强,DBA日常维护SQL脚本