default

Oracle中session audsid的产生原理及含义

Oracle的v$session视图中有一列audsid,一直不知其含义,在metalink上看到一篇文章How Sessions get Their AUDSID Identifier [ID 122230.1],详细讲述了audsid的产生原理和含义,大致内容总结如下:

1、audsid由SYS.AUDSES$这个sequence产生,当有一个新的会话产生,其audsid的值即为这个sequence的nextval

2、10g之前,audsid在会话‘connect internal’ 或为Oracle后台进程的时候为0,’connect internal’即指以as sysdbaas sysoper连接的会话

3、10g及之后,audsid‘connect internal’ 时为UB4MAX(4294967295), Oracle后台进程连接为0

注:UB4=unsigned int,取值范围为0-(2^32-1=4294967295)

 

由此可以看出,audsid与v$session视图中的sid不同的是,audsid主要是用于区分Oracle中的一般用户连接和internal/后台连接的。所以对于唯一的确定一个应用用户会话来说,除了使用sid和serial的组合,还可以使用audsid。

default

2011,忙碌的一年

    出差跑客户,跑了很多地方,常自叹生命的大半浪费在了路上,但也见识了很多不同的城市风貌,到过了淮河边的安徽蚌埠,也去过了美丽的厦门,技术上进步有限,视野确是开阔了不少,所谓有得必有失吧。
    希望来年不用这么忙,有更多的时间沉淀技术,多写点博客和总结,多看书,少扯淡,多动少宅,2012了,祝大家新年快乐!
    

Tags:
default

使用双DDL触发器进行DDL触发器的自我保护

   使用单DDL触发器进行自我保护很难做到对DDL触发器自身的保护,Oracle总是在DDL事件触发之前将此DDL触发器禁用或者DROP,所以单一的DDL触发器无法做到自我保护。可行的方式就是使用两个DDL触发器相互监控的形式,这有点类似与病毒自我保护中的守护进程,A进程守护B进程的同时B进程也在守护A进程,触发器要做的就是通过DDL触发器中捕获的事件属性对有可能破坏对方触发器的DDL语句进行拦截。

 

简单的实例如下:

Trigger A:

create or replace trigger trigger_a before ddl on database 
declare
   droping_trigger exception;
   pragma exception_init(droping_trigger,-1031);
   user_key varchar2(100);
begin
   
   user_key:=sys_context(‘mcctx’,'auth_key’);
  
   if (ora_sysevent in (‘DROP’,'ALTER’) and ora_dict_obj_type=’TRIGGER’ and ora_dict_obj_name = ‘TRIGGER_B’) then
     if ( user_key!=’dropkey’ or user_key is null)  then     
        raise droping_trigger;
     end if;
   end if;
end trigger_a;

Trigger B:

create or replace trigger trigger_b before ddl on database
declare
  droping_trigger exception;
  pragma exception_init(droping_trigger,-1031);
  user_key varchar2(100);
begin

  user_key:=sys_context(‘mcctx’,'auth_key’);

  if (ora_sysevent in (‘DROP’,'ALTER’) and ora_dict_obj_type=’TRIGGER’ and ora_dict_obj_name = ‘TRIGGER_A’) then
    if ( user_key!=’dropkey’ or user_key is null) then
    raise droping_trigger;
  end if;
end if;
end trigger_b;

当然需要留一个接口,出于安全考虑,这个可以用本地的上下文实现,这个接口的作用是临时性地禁用掉Trigger的拦截功能,因为DDL Trigger中无法判断对DDL的ALTER是ENABLE还是DISABLE操作,所以如果不能从外部禁用Trigger的拦截功能,则不可能同时ENABLE两个触发器。同样的,在DISABLE的时候也是一样,没有key机制,触发器将没有方法停止,但这种情况不会发生,因为这样的两个触发器是不能同时被ENABLE的。

default

批量生成statspack报告脚本

以前写过awr的,今天写了一个statspack的作为补充,不过因为statspack没有提供在PLSQL中生成statspack的api,只能通过调用一个SQL脚本来生成,所以只能包装成shell,脚本的功能和使用方法都类似。

使用方法:

1、直接使用:sh sprpt_batch.sh start_snap end_snap,如sh sprpt_batch.sh 88 99

2、交互模式:sh sprpt_batch.sh

statspack报告路径会在输出结果中显示

:<<BLOCK
usage:
1.noninteractive:
sprpt_batch.sh start_snap end_snap
2.interactive:
sprpt_batch.sh
BLOCK

if [ $# -ne 0 -a $# -ne 2 ]
then
echo "Usage: 1.noninteractive:sprpt_batch.sh start_snap end_snap 2.interactive:sprpt_batch.sh"
exit 1
fi

username=perfstat
pass=perfstat
curr_dir=`pwd`
   
s_snap_id=$1
e_snap_id=$2

 

sqlplus -s /nolog<<EOF
conn /as sysdba;
select snap_id,to_char(startup_time,’yyyy-mm-dd hh24:mi:ss’) start_time,to_char(snap_time,’yyyy-mm-dd hh24:mi:ss’) snap_time from stats$snapshot order by snap_id;
exit;
EOF

 

if [ $# -eq 0 ]
then
  echo "Please enter start snap id:"
    read line
    low_snap_id=$line
    echo "Please enter end snap id:"
    read line
    high_snap_id=$line
    for((snap_i_id=$low_snap_id;snap_i_id<$high_snap_id;snap_i_id++))
    do
   
    echo $snap_i_id;
    end_snap=$((snap_i_id+1))
    echo $end_snap;
    read line
    result_files="$curr_dir/sprpt_batch_$((snap_i_id))_$((end_snap)).txt $result_files"
    sqlplus -s /nolog<<EOF
    conn /as sysdba;
    prompt please enter the start snap_id:;
    define begin_snap=${snap_i_id};
    prompt please enter the stop snap_id:;
    define end_snap=${end_snap};
    define dflt_name=sprpt_batch_${snap_i_id}_${end_snap}.txt
    spool sprpt_batch_${snap_i_id}_${end_snap}.txt
    @?/rdbms/admin/spreport
    spool off
    exit
EOF
done

else
  for((snap_i_id=$s_snap_id;snap_i_id<$e_snap_id;snap_i_id++))
    do
    end_snap=$((snap_i_id+1))
    result_files="$curr_dir/sprpt_batch_$((snap_i_id))_$((end_snap)).txt $result_files"
     sqlplus -s /nolog<<EOF
    conn /as sysdba;
    define begin_snap=${snap_i_id};
    define end_snap=${end_snap};
    define dflt_name=sprpt_batch_${snap_i_id}_${end_snap}.txt
    spool sprpt_batch_${snap_i_id}_${end_snap}.txt
    @?/rdbms/admin/spreport
    spool off
    exit
EOF
done
   
fi
    echo ‘result file path:’
    echo $result_files

default

配置一个实例注册多个监听

添加一个listener配置和对应的sid_list,’sid_list_’后跟的就是对应的listener的名字:

LISTENER_247 =
(DESCRIPTION_LIST =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = 172.16.4.247)(PORT = 1522))
)
)

SID_LIST_LISTENER_247 =
(SID_LIST =
(SID_DESC =
(GLOBAL_DBNAME = orcl)
(ORACLE_HOME = C:apporacleproduct11.2.0dbhome_2)
(SID_NAME = orcl)
)
)

启动listener:

lsnrctl start listener_247

查看状态:

C:WindowsSysWOW64>lsnrctl status listener_247

LSNRCTL for 64-bit Windows: Version 11.2.0.1.0 – Production on 27-6月 -2011 10:5
7:38

Copyright (c) 1991, 2010, Oracle.  All rights reserved.

正在连接到 (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=172.16.4.247)(PORT=1522)))
LISTENER 的 STATUS
————————
别名                      listener_247
版本                      TNSLSNR for 64-bit Windows: Version 11.2.0.1.0 – Produ
ction
启动日期                  27-6月 -2011 10:03:00
正常运行时间              0 天 0 小时 54 分 41 秒
跟踪级别                  off
安全性                    ON: Local OS Authentication
SNMP                      OFF
监听程序参数文件          C:apporacleproduct11.2.0dbhome_2networkadminli
stener.ora
监听程序日志文件          c:apporaclediagtnslsnrmzh-PClistener_247alertl
og.xml
监听端点概要…
(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=172.16.4.247)(PORT=1522)))
服务摘要..
服务 “orcl” 包含 1 个实例。
实例 “orcl”, 状态 UNKNOWN, 包含此服务的 1 个处理程序…
命令执行成功

这样做的一个好处就是可以在不同网卡上建立对应的listener,通过客户端访问不同的listener来对网卡流量进行分流,减轻单一网卡的负载,同时一块网卡故障,可以有另外网卡备用,解决网卡的单点故障。

另外,如果你跟我一样,有时候需要将自己机器上的数据库开放给其他人用,你也可以配置多个监听,一个ip地址一个,尤其是ip地址经常变动,比如无线、有线经常切换,这样的配置就十分方便了,需要的时候就启动对应ip地址上的listener即可,而不需要修改对应的listener配置和tns配置,你也可以配置一个local的供自己访问,这样不管ip地址变化了,比如公司切到家里,启动local的listener,保证自己哪里都是可以访问的(配置local主要是为了使用像PLSQL之类的工具进行连接,因为有些工具只能通过tnsname的方式访问)。

default

主机信息抓取脚本

支持AIXHP-UXLinux,自动判断运行中的Oracle用户,支持多Oracle用户,自动判断Listener名称,获取包括主机版本、IP地址、文件系统、物理内存、交换空间、文件缓存配置、主机字符集环境、ORACLE环境变量、Listener状态、Oracle字符集、Oracle用户、Oracle版本、SGA、PGA、数据文件路径等信息。

使用:

root下执行

info.sh >info

info.sh

#!/bin/ksh
typeset -u g_os=`uname`

case $g_os in
   AIX*)
      echo ‘——————————————————’
            echo ‘AIX OS Version:’
            oslevel -r
           
            echo ”
      echo ‘——————————————————’           
            echo ‘IP Configuration:’
            ifconfig -a
            echo ”
      echo ‘——————————————————’
            echo ‘FileSystem:’
            df -g
            echo ”
      echo ‘——————————————————’
            echo ‘Physical Memory:’
            lsattr -El mem0
            echo ”
      echo ‘——————————————————’
            echo ‘Swap Info:’
            lsps -a
            echo ”
      echo ‘——————————————————’
           
            echo ‘maxperm and minperm:’
            vmo -a|grep ‘perm’
    ;;
    HP*)
      echo ‘——————————————————’
            echo ‘HP-UX OS Version:’
            uname -a
           
            echo ”
      echo ‘——————————————————’
            echo ‘IP Configuration:’
            netstat -in
            echo ”
      echo ‘——————————————————’
            echo ‘FileSystem:’
            bdf
            echo ”
      echo ‘——————————————————’
            echo ‘Physical Memory:’
            dmesg|grep Physical
            echo ”
      echo ‘——————————————————’
            echo ‘Swap Info:’
            swapinfo -a
            echo ”
      echo ‘——————————————————’
           
            echo ‘ dbc_max_pct and dbc_min_pct:’
            kctune |grep ‘dbc’
     ;;
     LINUX*)
      echo ‘——————————————————’
            echo ‘Linux OS Version:’
            uname -a
            cat /etc/redhat-release
           
            echo ”
      echo ‘——————————————————’
            echo ‘IP Configuration:’
            ifconfig
            echo ”
      echo ‘——————————————————’
            echo ‘FileSystem:’
            df -h
            echo ”
      echo ‘——————————————————’
            echo ‘Physical Memory:’
            grep MemTotal /proc/meminfo
            echo ”
      echo ‘——————————————————’
            echo ‘Swap Info:’
            free -m

     ;;
esac
echo ”
echo ‘——————————————————’
echo ‘OS Language Environment:’
env |grep LANG
echo ‘NLS_LANG:’$NLS_LANG
echo ”
echo ‘——————————————————’
echo ‘Oracle Process Info:’
ps -ef|grep ora_smon|grep -v ‘grep’
ps -ef|grep tns|grep -v ‘grep’

echo ”
echo ‘——————————————————’

echo ‘vmstat:’
vmstat 1 10

echo ”
echo ‘——————————————————’
echo ‘Oracle User Info:’
ora_username=`ps -ef|grep ora_smon|grep -v grep|awk -F" " ‘{print $1}’|awk ‘!a[$0]++’`
for i in $ora_username
do
    id $i
done

 

 

 

echo ”
echo ‘——————————————————’
echo ‘Oracle User Environment:’

oracle_info=’sqlplus -s "/as sysdba"<<EOF
set feedback off;
set pagesize 300;
prompt ;
prompt ——————————————————;
prompt Oracle SGA:;

show sga;
col name for a15;
col value for a15;
show parameter sga;
prompt ;
prompt ——————————————————;
prompt Oracle PGA:;

show parameter pga;
prompt ;
prompt ——————————————————;
prompt Oracle Version:;
select * from v$version;
prompt ;
prompt ——————————————————;
prompt Oracle Datafile Location:;
col name for a45;
select name,bytes/1024/1024/1024 "size(GB)" from v$datafile;
col parameter for a30;
col value for a30;
prompt ;
prompt ——————————————————;
prompt Oracle NLS Parameters:;
select * from nls_database_parameters;
prompt ;
prompt ——————————————————;
prompt Oracle Users:;
col USERNAME for a30;
col DEFAULT_TABLESPACE for a30;
select username,DEFAULT_TABLESPACE from dba_users;
   exit
    EOF’
   
for i in $ora_username
do
case $g_os in
   AIX*|LINUX*)
    echo ‘————’;
    echo ‘username:’$i;
    echo ‘————’;
    lsnrn=`ps -ef|grep tns|grep -v grep|awk -F" " ‘{print $1,$(NF-1)}’|grep $i|awk -F" " ‘{print $2}’`
        su – $i "-c env |grep -E ‘ORACLE_BASE|ORACLE_HOME|ORACLE_SID’;echo ”;echo ‘——————————————————’;echo ‘Oracle Listener Info:’;lsnrctl status $lsnrn;$oracle_info;";
   ;;
   HP*)
    echo ‘————’;
    echo ‘username:’$i;
    echo ‘————’;
    lsnrn=`ps -ef|grep tns|grep -v grep|awk -F" " ‘{print $1,$(NF-1)}’|grep $i|awk -F" " ‘{print $2}’`
    su – $i -c "env |grep -E ‘ORACLE_BASE|ORACLE_HOME|ORACLE_SID’;echo ”;echo ‘——————————————————’;echo ‘Oracle Listener Info:’;lsnrctl status $lsnrn;$oracle_info;";
   ;;
esac
done

default

使用awk去除重复行

主要是利用了一个hash数组和一个后置的自增符。

如,需要取得运行中的所有Oracle账户,可以使用如下命令:

ps -ef|grep ora_smon|grep -v grep|awk -F" " ‘{print $1}’|awk ‘!a[$0]++’

!a[$0]++是一个pattern,当pattern为true时,输出行。

因为后置自增符的存在,先赋值后自增,而a[$0]的原值是undef,所以pattern表达式在第一次匹配行时为true,当第二次匹配时,a[$0]已赋值,pattern表达式为false。

详细解释参考:

http://hi.baidu.com/sosodream/blog/item/a170c4097ad3038ed1581b26.html

Tags: , ,
default

记一次MAX函数的访问路径优化

一个大表,查询其ID的MAX值,ID列上建有唯一键索引,在没有缓存和清空shared pool的情况下其查询需要耗时1分半:

set timing on;
set autot on;

alter system flush buffer_cache;
alter system flush shared_pool;

SQL> select nvl(max(id),0) from mc$asset_audit_access_summary ;

NVL(MAX(ID),0)
————–
10974726

Elapsed: 00:01:33.75

Execution Plan
———————————————————-
Plan hash value: 55939531

——————————————————————————————————————

| Id  | Operation        | Name             | Rows  | Bytes | Cost(%CPU)| Time     | Pstart| Pstop |

——————————————————————————————————————

|   0 | SELECT STATEMENT    |             |     1 |     6 |  7372   (3)| 00:01:29 |     |     |

|   1 |  SORT AGGREGATE     |             |     1 |     6 |      |      |     |     |

|   2 |   PARTITION RANGE ALL    |             |    10M|    61M|  7372   (3)| 00:01:29 |     1 |    35 |

|   3 |    PARTITION LIST ALL    |             |    10M|    61M|  7372   (3)| 00:01:29 |     1 |     5 |

|   4 |     INDEX FAST FULL SCAN| IDX$ASSET_SUMMARY_TIME |    10M|    61M|  7372   (3)| 00:01:29 |     1 |   175 |

——————————————————————————————————————

Statistics
———————————————————-
19739  recursive calls
0  db block gets
49757  consistent gets
45592  physical reads
0  redo size
524  bytes sent via SQL*Net to client
469  bytes received via SQL*Net from client
2  SQL*Net roundtrips to/from client
139  sorts (memory)
0  sorts (disk)
1  rows processed

可以看到执行计划中索引是走INDEX FAST FULL SCAN的,虽然已经走索引了,但物理读还是很高,因为表格的数据量很大,此时索引的大小也很大,有没有方法提高MAX函数的效率呢,答案是有的,因为首先B树索引是有序的,如果能使索引扫描的时候首先从最大值开始扫描,那不需要读整个索引,最大值就能马上得到了,在Oracle中,可以通过使MAX函数走INDEX FULL SCAN (MIN/MAX)访问路径来实现。

要使得MAX/MIN函数走INDEX FULL SCAN (MIN/MAX),需要满足两个条件:

1、列上有索引

2、列必须是非空

可以看到,第一个条件已经满足,看来问题出在第二条,修改列约束为非空,因为本身列中没有null值,可以直接修改:

alter table MC$ASSET_AUDIT_ACCESS_SUMMARY modify ID not null;

再看执行计划:

set timing on;
set autot on;

alter system flush buffer_cache;
alter system flush shared_pool;

SQL> alter table MC$ASSET_AUDIT_ACCESS_SUMMARY modify ID not null;

Table altered.

Elapsed: 00:00:02.42
SQL> select nvl(max(id),0) from mc$asset_audit_access_summary;

NVL(MAX(ID),0)
————–
10974726

Elapsed: 00:00:00.01

Execution Plan
———————————————————-
Plan hash value: 3287236083

——————————————————————————–
——————-

| Id  | Operation           | Name          | Rows  | Bytes | Cost (%CPU)| Time      |

—————————————————————————————————

|   0 | SELECT STATEMENT       |              |    1 |    6 |3   (0)| 00:00:01 |

|   1 |  SORT AGGREGATE        |              |    1 |    6 |       |      |

|   2 |   INDEX FULL SCAN (MIN/MAX)| UNQ$ASSET_SUMMARY_ID |    10M|    61M|3   (0)| 00:00:01 |

—————————————————————————————————

Statistics
———————————————————-
352  recursive calls
0  db block gets
450  consistent gets
19  physical reads
0  redo size
524  bytes sent via SQL*Net to client
469  bytes received via SQL*Net from client
2  SQL*Net roundtrips to/from client
7  sorts (memory)
0  sorts (disk)
1  rows processed

可以看到现在访问路径变成了INDEX FULL SCAN (MIN/MAX),在同样的清空缓存和shared pool的情况下,物理读降到了只有19次。查询时间减小至几乎为0。

© DbConf.Net
CyberChimps