如何根据rowid获取extent_id

我们知道,rowid是由四部分组成的,分别是data_object_id,file_id,block_number和row_number,通过oracle提供的dbms_rowid包可以很方便的将一串rowid解析出上述四部分的内容。然后根据这些信息,则可以获取其extent_id。

SYS@datac>declare
  2  v_block_id number;
  3  v_file_id number;
  4  v_object_id number;
  5  v_extent_id number;
  6  v_object_name varchar2(30);
  7  v_owner varchar2(30);
  8  v_rowid varchar2(20):='AAACrKAAXAAAAzUAAH';
  9  begin
 10  select dbms_rowid.ROWID_BLOCK_NUMBER(v_rowid),
 11         dbms_rowid.ROWID_RELATIVE_FNO(v_rowid),
 12         dbms_rowid.ROWID_OBJECT(v_rowid)
 13   into v_block_id,v_file_id,v_object_id
 14  from dual;
 15
 16  select owner,object_name
 17    into v_owner,v_object_name
 18  from dba_objects
 19  where data_object_id=v_object_id;
 20
 21  select extent_id into v_extent_id
 22  from dba_extents
 23  where owner=v_owner
 24  and segment_name=v_object_name
 25  and file_id=v_file_id
 26  and v_block_id between block_id and block_id+blocks-1;
 27
 28  dbms_output.put_line('         rowid: '||v_rowid);
 29  dbms_output.put_line('       file_id: '||v_file_id);
 30  dbms_output.put_line('      block_id: '||v_block_id);
 31  dbms_output.put_line('data_object_id: '||v_object_id);
 32  dbms_output.put_line('         owner: '||v_owner);
 33  dbms_output.put_line('   object_name: '||v_object_name);
 34  dbms_output.put_line('     extent_id: '||v_extent_id);
 35  end;
 36  /
         rowid: AAACrKAAXAAAAzUAAH
       file_id: 23
      block_id: 3284
data_object_id: 10954
         owner: NINGOO
   object_name: TEST
     extent_id: 5

将上述代码打包到一个shell脚本中,rowid通过参数传入,则可以更方便日常环境中使用。工欲善其事,必先利其器,将经验转化为工具,利用工具提升效率,才能做一个Lazy DBA

$ tbsql rowid AAACrKAAXAAAAzUAAH
         rowid: AAACrKAAXAAAAzUAAH
       file_id: 23
      block_id: 3284
data_object_id: 10954
         owner: NINGOO
   object_name: TEST
     extent_id: 5

$ tbsql rowid AAACrKAAZAAABiiAAR
         rowid: AAACrKAAZAAABiiAAR
       file_id: 25
      block_id: 6306
data_object_id: 10954
         owner: NINGOO
   object_name: TEST
     extent_id: 7

用jQuery+Tablesorter实现客户端分页与排序

最近花了比较多的时间在自己开发的一个数据库监控系统上。监控系统的架构很简单,主要用shell+perl+SQL采集数据到一个MySQL数据库中,然后使用php来做web展示。对于仅仅定位于团队内部使用的小系统来说,这样的架构足够了,简单有效。

为了将数据库中的一些详细数据展示到web页面,采用了PEAR::HTML_Table,可以将SQL查询的结果直接转换成html表格。HTML_Table功能比较单一,如果要实现分页与排序的功能,还需要借助SQL语句。在MySQL中,如果要根据选择的任何一列都能用来排序的话,只要该列没有索引,就必须filesort进行实际的排序。由于系统中没有引入cache层,每次页面刷新都要到数据库中执行排序的SQL,性能是比较差的。

因此便想用javascript在客户端实现分页与排序,数据库中只要一次性选出需要的数据即可。jQuery是目前比较流行的一个javascript框架,以前也是久闻其名,但一直没有用过。Google了一把,发现其用法还是非常简单的,短短几行代码,就可以实现我所想要的效果。jQuery功能强大,但其实就是一个120K左右的js文件,并且还支持插件,分页功能就是通过一个叫tablesorter的插件实现的。

使用jQuery+Tablesorter实现客户端分页与排序,只需要10几行代码足矣。唯一的要求,就是html的table标签内要包含thead和tbody部分,这可以调用HTML_Table::getHeader()生成thead,调用HTML_Table::getBody()生成tbody部分。然后在需要展示数据的页面引入jQuery和Tablesorter的js库文件,再加上几行代码即可实现客户端javascript对数据table的分页与排序,并且还支持动态修改每页数据行数,支持多列排序等。具体的例子和代码,都可以在本文的链接中找到,就不在这里多说了。

Web开发我是外行,几年前学了点php的皮毛,知识已经多年没有更新过了。Ajax和web2.0的年代,javascript确实重新焕发了青春,像jQuery这样的框架,真的是不可多得的好东西,对于像我这样的初学者,也能很快上手做出相当不错的页面效果,这才是技术的生产力啊。

当然,在客户端做分页和排序有利也有弊。因为所有数据是一开始就全部从数据库中选取并返回客户端的,如果数据量比较大,那么第一次加载页面的时间就会比较长,并且可能占用客户端过多的资源。所以必须仔细权衡,在合适的场景用合适的技术。

Oracle环境中使用NFS的mount选项

在oracle环境中使用NFS,在mount的时候需要修改一些选项,否则可能导致各种问题,比如ORA-27086和ORA-27054错误。不管你是将Oracle安装在NFS设备也好,是将datafile放置在NFS设备也好,是备份到NFS设备也好,如果出现相关的错误,则需要检查下是否mount的选项有问题。一般只要用hard方式来mount的,都不会有太多问题。

以下内容引自Metalink(Doc ID:359515.1),适用于10.1.0.2以上版本,供参考:

RAC 

In the table below

  • Binaries is the shared mount points where the Oracle Home and CRS_HOME is installed.
  • Datafiles includes Online Logs, Controlfile and Datafiles

Operating System

Mount options for Binaries Mount options for Oracle Datafiles Mount options for CRS Voting Disk and OCR
Sun Solaris *

rw,bg,hard,nointr,rsize=32768,
wsize=32768,proto=tcp,
noac,vers=3,suid

rw,bg,hard,nointr,rsize=32768,
wsize=32768,proto=tcp,noac,
forcedirectio, vers=3,suid
rw,bg,hard,nointr,rsize=32768,
wsize=32768,proto=tcp,vers=3,
noac,forcedirectio
AIX (5L) **

rw,bg,hard,nointr,rsize=32768,
wsize=32768,proto=tcp,vers=3,
timeo=600

cio,rw,bg,hard,nointr,rsize=32768,
wsize=32768,proto=tcp,noac,
vers=3,timeo=600

cio,rw,bg,hard,intr,rsize=32768,
wsize=32768,tcp,noac,
vers=3,timeo=600

HPUX 11.23 ****  – rw,bg,vers=3,proto=tcp,noac,
hard,nointr,timeo=600,
rsize=32768,wsize=32768,suid
rw,bg,vers=3,proto=tcp,noac,
forcedirectio,hard,nointr,timeo=600,
rsize=32768,wsize=32768,suid
rw,bg,vers=3,proto=tcp,noac,
forcedirectio,hard,nointr,timeo=600
,rsize=32768,wsize=32768,suid
Linux x86
#

rw,bg,hard,nointr,rsize=32768,
wsize=32768,tcp, vers=3,
timeo=600, actimeo=0

rw,bg,hard,nointr,rsize=32768,
wsize=32768,tcp,actimeo=0,
vers=3,timeo=600

rw,bg,hard,nointr,rsize=32768,
wsize=32768,tcp,noac,vers=3,
timeo=600

Linux x86-64 # rw,bg,hard,nointr,rsize=32768,
 wsize=32768,tcp,vers=3,
timeo=600, actimeo=0
rw,bg,hard,nointr,rsize=32768,
wsize=32768,tcp,actimeo=0,
vers=3,timeo=600
rw,bg,hard,nointr,rsize=32768,
wsize=32768,tcp,vers=3,
timeo=600,noac
Linux – Itanium rw,bg,hard,nointr,rsize=32768,
wsize=32768,tcp,vers=3,
timeo=600, actimeo=0
rw,bg,hard,nointr,rsize=32768,
wsize=32768,tcp,actimeo=0,
vers=3,timeo=600
rw,bg,hard,nointr,rsize=32768,
wsize=32768,tcp,noac,vers=3,
timeo=600

* NFS mount option “forcedirectio” is required on Solaris platforms when mounting the OCR/CRS files when using Oracle 10.1.0.4 or 10.2.0.2 or later (Oracle unpublished bug 4466428)
** AIX is only supported with NAS on AIX 5.3 TL04 and higher with Oracle 10.2.0.1 and later (NetApp)
*** NAS devices are only supported with HPUX 11.23 or higher ONLY 

# These mount options are for Linux kernels 2.6 and above for older kernels please check Note 279393.1

Due to Unpublished bug 5856342, it is necessary to use the following init.ora parameter when using NAS
with all versions of RAC on Linux (x86 & X86-64 platforms) until 10.2.0.4. This bug is fixed and included in 10.2.0.4 patchset.
filesystemio_options = DIRECTIO

 Single Instance

Operating System

Mount options for Binaries Mount options for Oracle Datafiles
Sun Solaris *
(8, 9, 10)

rw,bg,hard,rsize=32768,
wsize=32768,vers=3,nointr,
proto=tcp,suid

rw,bg,hard,rsize=32768,
wsize=32768,vers=3,[forcedirectio or llock],
nointr,proto=tcp,suid
AIX (5L) **

rw,bg,hard,rsize=32768,
wsize=32768,vers=3,intr,
timeo=600,proto=tcp

rw,bg,hard,rsize=32768,
wsize=32768,vers=3,cio,intr,
timeo=600,proto=tcp

HPUX 11.23 **** rw,bg,hard,rsize=32768,
wsize=32768,vers=3,nointr,
timeo=600,proto=tcp,suid
rw,bg,hard,rsize=32768,
wsize=32768,vers=3,nointr,
timeo=600,proto=tcp,suid
Linux x86
#
rw,bg,hard,rsize=32768,
wsize=32768,vers=3,nointr,
timeo=600,tcp
rw,bg,hard,rsize=32768,
wsize=32768,vers=3,nointr,
timeo=600,tcp,actime=0*
Linux x86-64 # rw,bg,hard,rsize=32768,
wsize=32768,vers=3,nointr,
timeo=600,tcp
rw,bg,hard,rsize=32768,
wsize=32768,vers=3,nointr,
timeo=600,tcp,actime=0*
Linux – Itanium rw,bg,hard,rsize=32768,
wsize=32768,vers=3,nointr,
timeo=600,tcp
rw,bg,hard,rsize=32768,
wsize=32768,vers=3,nointr,
timeo=600,tcp

* actime=0 or noac can be used

如何收集及删除列的统计信息

本文只涉及使用dbm_stats来收集或删除列的统计信息的一些命令,以备查询。

测试表如下(实验环境为10.2.0.4):

SQL> create table test(i int,a varchar2(30));
Table created.

SQL> insert into test select rownum,object_name from all_objects;
9907 rows created.

简单的说,列的统计信息,主要包括两种类型:

  • 只有基本信息:收集的统计信息只有1个桶(bucket)
  • 包含柱状图信息:收集的统计信息包含2到254个桶

也就是说,如果想收集列的基本信息,同时不希望收集柱状图,则需要指定bucket的size为1:

SQL> exec dbms_stats.gather_table_stats(user, 'TEST',
cascade=>false, method_opt=>'for columns i size 1');

PL/SQL procedure successfully completed.                                                                             

SQL> select column_name,num_distinct,num_buckets,low_value,high_value,density,avg_col_len,histogram
  2  from user_tab_columns where table_name='TEST';

COLUMN_NAM NUM_DISTINCT NUM_BUCKETS LOW_VALUE  HIGH_VALUE    DENSITY AVG_COL_LEN HISTOGRAM
---------- ------------ ----------- ---------- ---------- ---------- ----------- ---------------
I                  9907           1 C102       C26408     .000100939           4 NONE
A                                                                                NONE

如果要收集列的柱状图信息,则bucket的个数必须大于等于2(最多不超过254)

SQL> exec dbms_stats.gather_table_stats(user, 'TEST',
cascade=>false, method_opt=>'for columns i size 2');

PL/SQL procedure successfully completed.

SQL> select column_name,num_distinct,num_buckets,low_value,high_value,density,avg_col_len,histogram
  2  from user_tab_columns where table_name='TEST';

COLUMN_NAM NUM_DISTINCT NUM_BUCKETS LOW_VALUE  HIGH_VALUE    DENSITY AVG_COL_LEN HISTOGRAM
---------- ------------ ----------- ---------- ---------- ---------- ----------- ---------------
I                  9907           2 C102       C26408     .000100939           4 HEIGHT BALANCED
A                                                                                NONE

如果要删除列已有的柱状图信息而保留列的基本统计信息,则需要重新收集bucket为1的统计信息

SQL> exec dbms_stats.gather_table_stats(user, 'TEST',
cascade=>false, method_opt=>'for columns i size 1');

PL/SQL procedure successfully completed.

SQL> select column_name,num_distinct,num_buckets,low_value,high_value,density,avg_col_len,histogram
  2  from user_tab_columns where table_name='TEST';

COLUMN_NAM NUM_DISTINCT NUM_BUCKETS LOW_VALUE  HIGH_VALUE    DENSITY AVG_COL_LEN HISTOGRAM
---------- ------------ ----------- ---------- ---------- ---------- ----------- ---------------
I                  9907           1 C102       C26408     .000100939           4 NONE
A                                                                                NONE

这个操作明显不太合理,重新收集统计信息的代价有时候是很大的,所以Oracle11g对此做出了改进,允许只删除柱状图而保留基本统计信息,命令语法如下:

exec dbms_stats.delete_column_stats('', '
', '
', col_stat_type=>'HISTOGRAM');

而要彻底删除整个列的统计信息,则需要调用delete_column_stats过程

SQL> exec dbms_stats.delete_column_stats(user, 'TEST', 'I');

PL/SQL procedure successfully completed.

SQL> select column_name,num_distinct,num_buckets,low_value,high_value,density,avg_col_len,histogram
  2  from user_tab_columns where table_name='TEST';

COLUMN_NAM NUM_DISTINCT NUM_BUCKETS LOW_VALUE  HIGH_VALUE    DENSITY  AVG_COL_LEN HISTOGRAM
---------- ------------ ----------- ---------- ---------- ----------  ----------- ---------------
I                                                                                 NONE
A                                                                                 NONE

可以在同一个过程中收集多个列的统计信息,并且可以为不同的列指定不同的bucket个数:

SQL> exec dbms_stats.gather_table_stats(user, 'TEST',
cascade=>false, method_opt => 'for columns size 1 T for columns size 2 A');

PL/SQL procedure successfully completed.

SQL> select column_name,num_distinct,num_buckets,low_value,high_value,density,avg_col_len,histogram
  2  from user_tab_columns where table_name='TEST';

COLUMN_NAM NUM_DISTINCT NUM_BUCKETS LOW_VALUE  HIGH_VALUE              DENSITY AVG_COL_LEN HISTOGRAM
---------- ------------ ----------- ---------- -------------------- ---------- ----------- ---------------
I                  9907           1 C102       C26408               .000100939           4 NONE
A                  7376           2 41         5F75746C245F6C6E635F .000185239          18 HEIGHT BALANCED
                                               696E645F7061727473

值得注意的是,从上一篇文章我们知道,9i的dbms_stats中,method_opt的默认值是FOR ALL COLUMNS SIZE 1,也就是收集列的基本统计信息而不收集柱状图信息,而10g的默认值则变成了FOR ALL COLUMNS SIZE AUTO,则Oracle在收集列的基本信息之外,还会根据情况收集某些列的柱状图。