我们应该怎样安装Oracle数据库?

Posted by 老熊 on 4月 12th, 2010

应该怎么样安装数据库,从安装软件到创建数据库?对于这个问题,或许有的人不屑一顾,因为他们觉得这没有丝毫问题;同时有另一部分人,觉得这是个大问题。

在安装Oracle上,通常会有几种类型的人:

  • 完全抓不着头脑,不知道怎么安装,这通常是初学者,连Linux/Unix都不太熟悉。
  • 很少安装Oracle的人,但是知道对照文档一步一步操作,出了错也知道上google、baidu和metalink查找解决方案。
  • Oracle老手,安装数据库不需要任何文档,对每个步骤也很熟悉。
  • 对Oracle的安装非常熟悉,但是在安装时仍然按文档一步一步操作。

对我个人来说,我是最后一种类型的人。我也自认为安装了不少的数据库,覆盖了大部分的平台和操作系统。但我安装的时候,仍然会不嫌麻烦的一步一步操作按文档操作。在安装数据库软件包括建库,基本上没有遇到过什么问题。因为我也见过
很多朋友,被安装这一问题折腾得焦头烂额,特别是在安装RAC的时候,这里只是写一写我自己的一些做法,仅供参考,毕竟每个人都有自己的习惯和做法。

本文主要描述Linux/Unix下的Oracle安装,不涉及Windows系统下的安装。

我的习惯做法:

  • 平时注意收集安装文档,包括oracle online document(所谓的官方文档),metalink上的,还有IBM、HP这些公司与Oracle合作部门提供的文档。oracle online document中安装部分没事看一遍就可以了,但是metalink上很多文档详细地记录了版本的兼容性,安装时可能出现的问题以及解决办法等等,比如很实用的文档:《Oracle Database on Unix AIX,HP-UX,Linux,Mac OS X,Solaris,Tru64 Unix Operating Systems Installation and Configuration Requirements Quick Reference (8.0.5 to 11.2) [ID 169706.1]》、《Linux OS Requirements Reference List for Database Server [ID 851598.1]》、《Status of Certification of Oracle Clusterware with HACMP [ID 404474.1]》。而其他厂商的文档包括:《CookBook_V3.2_Oracle_9i_RAC_AIX5L》、《COOKBOOK_Oracle CTC RAC10g R2 on HP-UX》、《COOKBOOK-V2.0-10gRAC R2 - ASM - AIX5L - SAN Storage》如此等等,还包括网上一些朋友自己撰写的安装文档。在参考这些文档时,需要注意的是,一定要明白每一个步骤其目的,有什么作用。
  • 根据以上提到的文档,进行整理,形成自己的文档。我在前面说到,我安装时一步一步按文档操作,是指的按我自己的文档,而不是去参考前面提到的若干文档。在自己的文档中,甚至提供了详细的命令,这样在安装的时候对某些不熟悉的命令不至于现查资料。
  • 深入理解文档中提到的各个参数、各个命令的作用。这不光是对安装,而对于Oracle数据库的更深入理解也是大有好处的。

安装Oracle数据库时,在安装软件之前,通常有下面的操作:

  • 检查操作系统版本、相应组件是否安装,是否有安装好文档中指定的补丁,也包括c编译器或c语行环境,这些对Linux下的安装来说犹为重要。
  • 检查文件系统空间,特别是/tmp临时文件系统
  • 检查memory大小,特别是swap的大小。特别是在HP-UX下,swap的管理方式与其他系统有些不一样(此处不再细述),最
  • 好能够达到物理内存大小,对于特别大的物理内存,至少也要达到一半。
  • 检查主机时区,时间设置。这一步通常被很多人忽略。
  • 检查主机名设置,有的安装系统相当不负责任,直接将主机取名localhost。
  • 检查异步IO设置。
  • 检查网络设置,包括/etc/hosts文件的设置,特别是对RAC数据库犹为重要。
  • 检查内核参数,特别是共享内存、信号量、用户最多可运行进程数这些参数。
  • oracle用户创建后,注意编辑profile文件,设置相应的环境变量,注意不同的平台,相同意义的环境变量却有不同的名字,比如linux下的LD_LIBRARY_PATH和AIX下的LIBPATH。经常见到有的系统,oracle的PATH都没有设置,这样登录后操作相当不方便。
  • 对Oracle用户设置limit,通常是直接编辑/etc/security/limits.conf文件。
  • 给Oracle用户一些特定的权限,比如HP-UX下修改/etc/privgroup文件,10.2.0.4 for AIX下给用户CAP_NUMA_ATTACH, CAP_BYPASS_RAC_VMM, CAP_PROPAGATE 这样的权限等等。
  • 对于RAC,还需要设置rsh或ssh

至于安装的其他步骤,不是本文所要讲的主要内容,在此略过。

还要提及一点,安装的时候对于目录的选择,可以按照OFA的标准做法,也可以按照使用部门的习惯,建议使用OFA。经常有见到一些乱七八糟的目录,让人好找,这种做法不太好。

说到安装,不能不提到打补丁。在安装完成后,最好是打上较新的补丁包以及metlink上提到的一些建议打的补丁。而等到系统正式使用,发现问题再打补丁,其代价就昂贵得多。

由于安装Oracle软件以及升级版本和打补丁,比较耗时,有的朋友就喜欢下面的做法:安装好软件,打上补丁,然后tar成一个包,保存在自己的存储介质上,下次在其他主机上安装时,直接用这个tar包解开。这种做法可以省一些安装软件的时间,但是需要tar包的环境是否一致。我曾遇到过下面的问题:

某套新装9208的库,报ORA-600[504]错误,通过在metalink上搜索发现其最符合的一个BUG对应的补丁已经打上。其他类似环境下,包括有同样的补丁,却没有这样的错误。我找了一台操作系统完全一样的测试主机,安装与有问题库的版本完全一样的版本和补丁,却也没有这样的错误;接下来我将出问题的Oracle home 复制到测试主机上,结果问题重现了,看起来问题在Oracle软件上。我尝试执行relink操作,居然失败。发现这份Oracle在relink包括有HA代码,实际上这是一个单机的环境,也没有安装HACMP。最后,使用rac_off选项,再重新relink,新生成的oracle,不再出现这样的问题。在有问题的生产主机上重新relink后,问题解决。

出现问题的Oracle,就是通过解tar文件来安装的,在主机上我们也发现了以前安装时保留的tar文件。因此通过这种方式安装的,建议进行relink。

下面再谈谈创建数据库的一些个人经验:

  • 尽量使用new database(9i)或者custom database(10g)这样的选项来创建库,建库时只安装必须的组件,这种做法有3个主要好处:更安全、更稳定、以后升级时所花的时间更少。
  • 创建数据库时注意选择正确的字符集。
  • 如果是选用模板创建数据库,注意模板有可能不与Oracle软件软件版本相匹配。同时在建库完成后需要运行相应的脚本,比如在安装了PSU的情况下,那么使用模板建库,得需要运行PSU带的脚本(具体参考PSU的README)。
  • 在打完补丁之后,在创建数据库,避免在升级软件之后还要升级数据库。
  • 建完库后,建议设置一下大体上合理的数据库参数。

希望本文能够对Oracle数据库的安装不太熟悉的朋友一些帮助。

一些数据迁移的技巧

Posted by 老熊 on 3月 1st, 2010

去年年底做了不少系统的数据迁移,大部分系统由于平台和版本的原因,做的是逻辑迁移,少部分做的是物理迁移,有一些心得体会,与大家分享。

首先说说迁移流程,在迁移之前,写好方案,特别是实施的方案步骤一定要写清楚,然后进行完整的测试。我们在迁移时,有的系统测试了四五次,通过测试来完善方案和流程。

针对物理迁移,也即通过RMAN备份来进行还原并应用归档的方式(这里不讨论通过dd方式进行的冷迁移),虽然注意的是要将数据库设为force logging的方式,在用RMAN做全备之前,一定要执行:

alter database force logging;

否则可能会产生坏块。

对于逻辑迁移,在job_processes设置为>0的数值之前,注意job的下次执行时间和job所属用户。比如job的定义在之前已经导入,但是在迁移之时,job已经运行过,那么迁移完成之后,job的下次时间还是原来的时间,这样可能会重复运行。另外,job通过IMP导入后,job所属用户会变成导入用户的名称,显然job原来的用户就不能对JOB进行管理了,可以通过下面的sql进行修改:

update sys.job$ set lowner=cowner , powner=cowner;

在迁移之前,应该禁止对系统进行结构上的修改和发布,比如表结构,索引,存储过程包等。

如果是用exp/imp导入的对象,包括存储过程等,应该检查对象是否与原生产库一致,比如由于dblink的原因,imp之后,存储过程不能创建,导致有部分存储过程丢失,尽管这些存储过程可能没有被使用。

下面是一些加快迁移速度的技巧:

  • 通过dblink,使用append insert的方式,同时利用并行,这种方式比exp/imp更快
  • 对于有LONG类型的列,insert..select的方式显然是不行的,可以通过exp/imp的方式,但是这种方式速度非常慢,其原因在于imp时一行一行地插入表。有另外一种方式,即sqlplus的copy命令,下面是一个示例:
    spool copy_long_table_1.log
    conn / as sysdba
    set copycommit=2000
    set arraysize 30
    set long 10485760
    
    copy from system/xxxx@source_db append username.table_name using select * from username.table_name;
    
    spool off
    exit
    

    不过,sqlpus的copy命令不支持有timestamp和lob列类型的表。如果有timestamp类型的表,可以通过在exp时,加上rowid的条件,将一个表分成多个部分同时操作,对于有lob类型的表,也可以同样处理(因为insert …select方式下,有lob类型列时,也同样是一行一行地插入)。注意在这种方式下,就不能使用direct的方式exp/imp。下面是exp导出时parfile示例:

    query="where rowid>=dbms_rowid.rowid_create(1,71224,52,9,0) and rowid<=dbms_rowid.rowid_create(1,71224,55,1038344,10000)"
    file=/dumpdata/n1.dmp
    tables=username.table1
    constraints=n
    grants=no
    indexes=no
    buffer=104857600
    ...
    ...
    query="where rowid>=dbms_rowid.rowid_create(1,71224,423,137,0) and rowid<=dbms_rowid.rowid_create(1,71224,432,59272,10000)"
    file=/dumpdata/n6.dmp
    tables=username.table1
    constraints=n
    grants=no
    indexes=no
    buffer=104857600
    

    将表分成几部分同时操作,不仅仅可以利用rowid,也可以利用表上的列,比如说,表上有一个created_date的列,并且保证是递增插入数据,那么这种情况下,也可以使用这个字段将表分成不同的范围同时进行导出和导入。不过使用ROWID通常具有更高的效率。
    当然对于有lob列的表,可以按上述方式,拆成多个insert方式同时插入,不需要exp/imp。

  • 对于特别大的分区表,虽然使用并行可以提高速度,但是受限于单个进程(不能跨DB LINK进行并行事务,只能并行查询,也即insert..select只能是SELECT部分才能进行并行)的处理能力,这种方式下速度仍然有限。可以并行将数据插入多个中间表,然后通过exchange partition without validation 的方式,交换分区,这种方式将会大大提高了速度。
    有朋友可能会问,为什么不并行直接插入分区表,当然如果是非direct path(append)方式,则是没问题的,但是这种方式插入的性能较低。而direct path的方式,会在表上持有mode=6(互斥)的TM锁,不能多个会话同时插入。(update: 在insert 时使用这样的语句:insert into tablename partition (partname) select * from tablename where ….,更简单更有效率。)
  • 迁移时,将数据分成两部分,一部分是历史表,第二部分是动态变化的表,在迁移之前,先导入历史表,并在历史表上建好索引,这无疑会大大减少迁移时业务系统中断时间。
  • 迁移之前,考虑清理掉垃圾数据。
  • 迁移时,应保证表上没有任何索引,约束(NOT NULL除外)和触发器,数据导入完成后,再建索引。建索引时同样,同时使用多个进程跑脚本。索引创建无成后,应去掉索引的PARALLEL属性
  • 在创建约束时,应按先创建CHECK约束,主键,唯一键,再创建外键约束的顺序。约束状态为 ENABLE NOVALIDATE,这将大大减少约束创建时间。而在迁移完成后,再考虑设回为ENABLE VALIDATE。
  • 通过使用dbms_stats.export_schame_stats和dbms_stats.import_schame_stats导入原库上的统计信息,而不用重新收集统计使用。

朋友们可以看到,以上均是针对9i的,实际上在10g甚至11g环境下,也仍然很多借鉴意义。当然这些技巧不仅仅用于完整的数据库迁移,也可以应用到将个别表复制到其他数据库上。

这里没有提到的是利用物化视图或高级复制、触发器之类的技术,因为这些技术,毕竟要修改生产库,对生产库的运行有比较大的影响,因此,只有在停机时间要求特别严格,而在这个时间内又不能完成迁移时才应该考虑。

从迁移的经验来说,只有完善的流程,完整的测试才可以保证成功。这里只是列举了一些小技巧,如果对整个迁移过程有兴趣,可以针对这个话题再进行讨论。

一次ORA-01410故障的解决

Posted by 老熊 on 1月 27th, 2010

在一个表上建索引时,报ORA-01410错误,我们查询这个表来重现这个错误:

Connected to:
Oracle9i Enterprise Edition Release 9.2.0.6.0 - 64bit Production
With the Partitioning option
JServer Release 9.2.0.6.0 - Production

SQL> set timing on
SQL> set time on
14:20:03 SQL> select /*+ full(a) no_index(a) */ count(*) from crm.cust_order a;
select /*+ full(a) no_index(a) */ count(*) from crm.cust_order a
                                                    *
ERROR at line 1:
ORA-01410: invalid ROWID

ORA-01410错误通常见于通过索引访问表,而索引或表由逻辑上的损坏。而这里显示没有通过索引访问表?那问题出在哪里呢?在这种情况下,这个错误与ORA-08103极其类似,参照《记一次ORA-8103错误的处理》

14:27:00 SQL> alter session set max_dump_file_size=unlimited;

Session altered.

Elapsed: 00:00:00.01
14:27:18 SQL> alter session set db_file_multiblock_read_count=1;

Session altered.

Elapsed: 00:00:00.00
14:27:18 SQL> alter session set events 'immediate trace name trace_buffer_on level 1048576';

Session altered.

Elapsed: 00:00:00.00
14:27:18 SQL> alter session set events '10200 trace name context forever, level 1';

Session altered.

Elapsed: 00:00:00.00
14:27:18 SQL> select /*+ full(a) no_index(a) */ count(*) from crm.cust_order a;
ERROR at line 1:
ORA-01410: invalid ROWID

Elapsed: 00:05:50.82
14:33:09 SQL> 14:33:09 SQL> alter session set events 'immediate trace name trace_buffer_off';

Session altered.

在trace文件的最后,我们可以看到:

Consistent read started for block 10 : 2489c394
  env: (scn: 0x0a0d.690ff414  xid: 0x0000.000.00000000  uba: 0x00000000.0000.00  statement num=0  parent xid: xid: 0x0000.000.000000
00  scn: 0x0000.00000000 0sch: scn: 0x0000.00000000)

这里只有”start“,而没有finish,表明在读2489c394这个块出了问题。
用ODU工具的rdba查看文件号和坏号:

ODU> rdba 2489c394

  rdba   : 0x2489c394=613008276
  rfile# : 146
  block# : 639892

通过”alter sytem dump datafile 146 block 639892”命令发现块中的object_id与CUST_ORDER表的data object id不同,看起来这就是问题所在(此处不再列出数据)。
看起来有坏块了。不过这个库是个查询库,把表TRUNCATE之后重新从生产库同步过来,发现问题仍然存在,甚至把表DROP之后重建也是如此,均是发生在146/639892这个块上。

而TRUNCATE/DROP表都不能解决问题,显然这个块还在内存中,看起来需要刷新buffer cache了:

14:37:07 SQL> alter session set events 'immediate trace name flush_cache level 1';
Session altered.

刷新buffer cache后,问题解决。

总结:这个问题,与ORA-8103类似,都是出现了逻辑坏块,只不过这次的坏块是发生在内存中的块。至于坏块是怎么进入到内存中,为什么在重建表后还在内存中,这就是个谜了,或者是ORACLE的BUG,或者跟用的同步软件DSG有关。在这个案例中,块的object_id与段的实际的data object id不一致。而object_id不一致有时也会报ORA-600错误。

2010新年快乐

Posted by 老熊 on 1月 1st, 2010

此时此刻,我迎来了2010年的第一个加班。在这么一个杯具性时刻,还是要祝所有的朋友们,新年快乐!

回顾2009年,用一句简单的话来说就是,“这一年没有白过”。

看看这一年,有些什么样的收获:

自己开发的ODU软件,走向成熟,得到了许多朋友的认可,也帮助很多朋友解决了燃眉之急。从年初发布v2.0.1版,到支持压缩表,中间相隔了3个月的时间。从那之后,就是一些功能的完善和BUG的修复,到如今已经相对比较稳定。通过开发这个软件,我自己对ORACLE的块格式有了相当清楚地了解,同时这个软件也发挥了他独特的作用,帮助一些朋友恢复了数据。

与盖国强、杨廷锟、段林仲、邹德平合作的《Oracle DBA手记》,即将面世。这完全是一个意外。通过这样的写作,收获是巨大的。我也希望除BLOG之外方式的知识分享,能够让更多的从事Oracle数据库相关工作的朋友受益。

技术方面,理论知识方面进展不大,但是实战经验值大幅提升。学习,从来都是实践与理论相结合。新的一年,争取有更进一步的突破。

作了房奴,收了房,也有了代步的工具。压力是越来越大了。争取明年七八月时能住进新房。

这一年,也是辛苦的一年。特别是最近2个月,加班与通宵已经成为十分常见的活动,无奈的是,绝大多数时候没办法控制这样的事情的发生。

2010年,我希望也有更多的收获:

  • 争取把OCM给过了。谁叫这玩意儿商业需求是如此的旺盛。
  • 多写一些有价值的技术文章。
  • 接触接触Oracle之外的东西。

招聘Oracle DBA

Posted by 老熊 on 12月 25th, 2009

成都东方龙马公司(就是老熊所在的公司),招聘1-2名Oracle技术支持工程师

成都东方龙马有优秀的技术团队,主要服务于各省级运营商,因业务发展需要,需招聘1-2名Oracle技术支持工程师。

基本要求:
1、有4年或以上数据库维护经验,对Oracle架构、机理及概念非常清晰。
2、有丰富的Oracle故障处理以及优化经验。
3、熟悉1种以上UNIX操作系统(AIX, HP-UX, Linux, Solaris),对存储及网络有一定了解。
4、良好的沟通能力。
5、很好的文档编写习惯。
6、对Oracle数据库有浓厚的兴趣。
7、有很好的职业道德及团队精神。
8、能够适应长期出差。

待遇,视能力而定,面议!

有兴趣的朋友,请将简历发送到我的邮箱 xj@olm.com.cn
简历请用doc或pdf附件形式,文件名内包含姓名。

《Oracle DBA手记》即将出版

Posted by 老熊 on 12月 23rd, 2009

与eygle,yangtingkun等几位合作的《Oracle DBA手记--数据库诊断案例与性能优化实践》即将出版,预计在1月份上架。在此感谢各位的去持。下面是本文的章节目录(来自eygle的网站):

├─第一篇 DBA工作手记
│      01.Eygle的DBA工作手记-Eygle
│      02.Yangtingkun的DBA工作手记-Yangtingkun
│      03.老熊的DBA手记
│      04.BanPing的DBA工作手记-Banping

├─第二篇 诊断案例篇
│      01.ASM案例分析与诊断
│      02.监听故障的诊断与分析
│      03.ORA系列错误与诊断
│      04.ORA-01200错误裸设备恢复
│      05.Oracle数据库无响应故障的处理
│      06.RAC环境诊断案例一则
├─第三篇 SQL调优篇
│      01.合理利用索引解决性能问题
│      02.SQL优化与调整实践
│      03.索引访问与数据读取
│      04.SQL优化之Everything is possible

└─第四篇 性能优化篇
        01.CBO、执行计划与统计信息案例
        02.Oracle数据库性能与统计信息
        03.聚簇因子、柱状图与执行计划
        04.表碎片及分页查询优化
        05.一次排序的调整与优化

本书在豆瓣上的条目:http://www.douban.com/subject/4209919/

再次感谢朋友们的关注。

统计信息与子分区

Posted by 老熊 on 12月 19th, 2009

在以前的一篇文章《DBMS_STATS、ANALYZE以及Global Statistics》中,提到使用10g数据库dbms_stats收集统计信息时,granularity缺省值为“AUTO”,其含义是“Auto -- Table + Partition + Subpartition (10g,表+分区,当子分区是list分区时还包括子分区)”。本文就这个问题再深入地探讨一下。

大家都知道,子分区有两种,一种是分区为RANGE,子分区为HASH,另一种是分区为RANGE,子分区为LIST。在10g数据库中,如果在使用dbms_stats收集统计信息时,如果没有显式指定granularity(粒度),那么granularity就会取自dbms_stats配置:
而其缺省值是“AUTO”,而不再是9i下的”DEFAULT”:

SQL> select dbms_stats.get_param('granularity') param from dual;

PARAM
------------------------------
AUTO

而10g自带的自动收集统计信息的任务“GATHER_STATS_JOB”,其granularity同样是取自granularity param。当然可以通过下面的SQL来更改其值:

SQL> exec dbms_stats.set_param('granularity','global and partition');

这样更改后,dbms_stats默认就会收集表以及分区级统计信息,不收集子分区级统计信息。

那么,granularity=auto时,到底是怎么样的呢?前面说到了子分区是以list方式分区时,那么就会收集子分区级统计信息,其言外之意就是如果子分区是以hash方式分区时就不会收集子分区统计信息了。到底是不是这样呢?下面做个测试,测试环境是Oracle 10.2.0.4 for Linux AS4:

QL> create table t1
  2  partition by range(object_id)
  3  subpartition by hash(data_object_id)
  4  subpartitions 4
  5  ( partition p1 values less than(10000),
  6    partition p2 values less than(20000),
  7    partition p3 values less than (maxvalue)
  8  )
  9  as select * from dba_objects;  

Table created.

SQL> create table t2
  2  partition by range(object_id)
  3  subpartition by list(object_type)
  4  subpartition template(
  5    subpartition sp1 values ('TABLE'),
  6    subpartition sp2 values ('INDEX'),
  7    subpartition sp3 values ('VIEW'),  
  8    subpartition sp4 values (DEFAULT)
  9  )  
 10  ( partition p1 values less than(10000),
 11    partition p2 values less than(20000),
 12    partition p3 values less than (maxvalue)
 13  )
 14  as select * from dba_objects; 

Table created.

我们先建再从个测试表,表T1是RANGE+HASH方式的复合(组合)分区表,表T2是RANGE+LIST方式的复合分区表。
下面将”granularity” param重新设回为”auto“,然后收集T1和T2的统计信息:
Read the rest of this entry »

一次共享内存段异常以及处理

Posted by 老熊 on 12月 8th, 2009

说起来汗颜,我这个BLOG主要写Oracle相关的文章,也附带写点UNIX,可惜从来没正经写过UNIX方面的东西。毕竟不是专业的SA,水平不够恐怕误导读者朋友。这次的故障,主要是从OS层进行处理的,稍微算是沾上一点UNIX的边。闲话少扯了,说正事吧。

事情的起因,是系统的最终用户反映某些查询功能比较慢。简单地看了一下主机的负载以及数据库的性能状况,没发现什么异常,甚至可以说系统相当地轻闲。

那问题出在哪?我首先观察到内存的使用率相当地高,达到99%。但是从操作上看,速度还没受到影响。不过很快想到,这个系统某些模块,用了短连接,难道是监听太慢引起的?这个库启了6个监听(详见《一切皆有可能》),分别TNSPING这几个监听,有个别监听非常慢,重启监听后,查询功能比较慢的问题得到解决。

不过之前观察到的内存的异常使用引起了我极大的注意。这套系统,平时一般都会有几十G的空闲内存,不会达到这么高的。第一反应是用ipcs命令检查一下共享内存,发现有一个异常的共享内存段,占了60多G。

[oracle@hostname%/oracle]ipcs -ma
IPC status from /dev/kmem as of Mon Dec  7 10:58:53 2009
T         ID     KEY        MODE        OWNER     GROUP   CREATOR    CGROUP NATTCH      SEGSZ  CPID  LPID   ATIME    DTIME    CTIME 
Shared Memory:
m          0 0x41180809 --rw-rw-rw-      root      root      root      root      0        348  2725  2725  2:38:57  2:38:57  2:38:50
m          1 0x4e0c0002 --rw-rw-rw-      root      root      root      root      2      61760  2725  2727 12:27:19 18:19:39  2:38:50
m          2 0x411c0de1 --rw-rw-rw-      root      root      root      root      2       8192  2725  2727 12:27:19  2:38:50  2:38:50
m          3 0x00a5c581 --rw-------     sfmdb     users     sfmdb     users     11   10469376  3362  3398  2:39:38  2:39:39  2:39:38
m          4 0x4118043d --rw-------      root      root      root      root      1       4096  3410  4745  2:40:12 no-entry  2:40:12
m          5 0x06347849 --rw-rw-rw-      root      root      root      root      1      65544  3535  6722 17:53:03 17:53:03  2:39:47
m    1015814 0x0c6629c9 --rw-r-----      root       dba      root       dba      0   35921048  6722  6722 17:53:03 no-entry 17:53:03
m     819207 0x491002d0 --rw-r--r--      root      root      root      root      0      22908  3674  3674  2:39:54  2:39:54  2:39:54
m    5472264 0x00000000 D-rw-r-----    oracle       dba    oracle       dba      6 66640334848  5508 23604 17:58:00 17:58:00 17:58:00
m   95387657 0x0000cace --rw-rw-rw-      root       sys      root       sys      0          2 21306 21306 20:24:33 20:24:33 20:24:29
m   35520522 0xa57bccf8 --rw-r-----    oracle       dba    oracle       dba  12231 66640334848  3231 26942 10:58:53 10:58:53 18:10:36

ID为”5472264″的共享内存段就是异常的共享内存段。
为什么会出现这种情况?数据库可以确定是被重启过,询问客户这套系统的DBA,的确是在头一天出现了异常然后进行了重启。至于出现了什么样的异常,为什么要重启,这里不再深入。本文只讨论怎么样来清除这个异常的共享内存段。

由于这个内存段的NTATTCH(number of attach)为6,在HP-UX下是清理不掉的:

[oracle@hostname%/oracle]ipcrm -m 5472264
ipcrm: shmid(5472264): not found

这是由于还有进程attach(理解为连接吧)到这个共享内存段上。只要找到这个进程被KILL之,就会解决问题。一种简单的方法是使用lsof来找到这些进程:

[oracle@hostname%/oracle]lsof | egrep "COMMAND|5472264"

不过简单的方法,不一定效率就高。这个系统光oracle server process就有5000个以上,lsof实在很慢。所以运行几分钟就直接放弃(因为以前在这套系统上运行过lsof命令,知道要输出完结果时间比较“漫长”)。

OK, 手工找一下吧。从上面的ipcs输出的CTIME字段看到,正常的共享内存段是18:10左右创建的,而异常的是17:58左右创建的,那么attach到这个异常共享内存段的进程应该是在18点之前创建,而在17:58左右。首先使用”ps -ef | grep defunct“,没有发现僵死进程。然后根据这样的条件,并且经过一系列筛选,得到下面的结果:

[oracle@hostname%/oracle]ps -ef | grep oraclesidname | grep "17:" | grep -v "18:17" | grep -v "11:17"
  oracle 22586     1  1 07:17:43 ?         0:31 oraclesidname (LOCAL=NO)
  oracle 28403     1  0 09:17:38 ?         0:02 oraclesidname (LOCAL=NO)
  oracle 22618     1  0 07:17:59 ?         0:00 oraclesidname (LOCAL=NO)
  oracle  7539     1  0 08:17:42 ?         0:10 oraclesidname (LOCAL=NO)
  oracle  7419     1  0 08:17:05 ?         0:00 oraclesidname (LOCAL=NO)
  oracle 22580     1  0 07:17:42 ?         0:36 oraclesidname (LOCAL=NO)
  oracle  7421     1  0 08:17:06 ?         0:06 oraclesidname (LOCAL=NO)
  oracle  7537     1  0 08:17:42 ?         0:02 oraclesidname (LOCAL=NO)
  oracle  7535     1  0 08:17:41 ?         0:00 oraclesidname (LOCAL=NO)
  oracle 21395     1  0 17:56:49 ?         0:01 oraclesidname (LOCAL=NO)
  oracle 22616     1  0 07:17:59 ?         0:00 oraclesidname (LOCAL=NO)
  oracle 20786     1  0 17:54:24 ?         0:10 oraclesidname (LOCAL=NO)
  oracle 22614     1  0 07:17:58 ?         0:00 oraclesidname (LOCAL=NO)
  oracle  7423     1  0 08:17:06 ?         0:18 oraclesidname (LOCAL=NO)

看上去进程号为21395和20786的进程,正好满足前面提到的条件。KILL这两个进程,检查共享内存段,发现这个异常的共享内存段自动被清除。再检查内存的使用,内存的使用率也大幅下降,回到正常状态。

今天也算是幸运的,在没有监控系统的情况下,人为的较早发现了这个问题,避免了全系统范围内的系统问题。如果没有及时发现这个问题,内存的使用一上去,开始大量使用交换页,那就头疼多了。

drop table与db cache

Posted by 老熊 on 11月 30th, 2009

我们都知道drop table, truncate table时都会先做一次checkpoint,将被删除对象的脏块写入磁盘。

客户有一套系统,Oracle 9.2.0.8,需要做数据迁移,由于种种原因,采用的是逻辑迁移的方式。由于库比较大,超过了1.5T,而停机时间又有限,因此在正式迁移之前需要做大量的测试,测试的目的,一方面是看迁移流程上是否存在问题,另一方面是看迁移的时候,哪个地方会存在性能瓶颈,并进行优化,同时估算实施迁移时间。

第一次测试后,需要把测试产生的大量用户及其对象全部删除,删除用的是drop user username cascade。不幸的是这种方式删除得相当地慢。一个9000多个表的用户,删除了1个半小时才删除了4000多个表。为什么这么慢?有没有办法提高速度?

drop table既然要做checkpoint,那么在db cache非常大的情况下,这需要消耗的时间是比较长的。如果能够减少这个时间无疑将大幅提高速度。首先尝试做一次checkpoint,将buffer cache全部刷新出去:

alter system checkpoint;
alter session set events 'immediate trace name flush_cache level 1';

发现没什么效果。

由于db_cache_size有50GB左右,db_keep_cache_size有6G左右。重新设置参数,将db_keep_cache_size设为0,将db_cache_size设为200M,重启一下数据库,重新执行删除用户的操作,操作很快完成。

在另一次同样的过程中,采用同样的修改参数的方式,效果同样非常明显。

这是个简单的案例,与君共享。

视图与权限

Posted by 老熊 on 11月 14th, 2009

前几天客户遇上这样一个问题,某个用户A将视图的SELECT给予另一个用户B,但是用户B查询这个视图时,仍然报错:ORA-01031: 权限不足。这是怎么一回事呢?下面来模拟一下这个过程:

有三个用户test1,test2,test3, 三个用户都具有DBA色色权限。

用TEST1用户创建一个表T1,并将其查询权限授予TEST2:

SQL> create table t1 as select * from all_objects;

表已创建。

SQL> grant select on t1 to test2;

授权成功。

用TEST2用户创建一个视图,视图的基表是TEST1.T1,并将查询权限授予TEST3:

SQL> create view v_t1 as select * from test1.t1;

视图已建立。

SQL> grant select on v_t1 to test3;

授权成功。

TEST3用户查询视图TEST2.V_T1:

SQL> select * from test2.v_t1 where rownum<1;
select * from test2.v_t1 where rownum<1
                    *
ERROR 位于第 1 行:
ORA-01031: 权限不足

可以看到报了权限不足的错误,就算这里TEST3用户有DBA权限。
这到底是怎么回事呢?
其实视图的权限,有两点需要引起注意:

1. 视图中,类似于定义者权限的存储过程,是屏蔽了角色权限的。比如如果TEST1没有显式地将T1表的SELECT权限给予TEST2,那么TEST2在创建视图V_T1时也会报ORA-01031错误,即使TEST2用户拥有DBA角色权限。

2.如果在用户A的视图中,引用了其他用户B的表,用户A将视图的访问权限给予用户C,那么就变相地将用户B的表的访问权限给予了用户C,因此,用户A必须有将用户B的表的访问权限转授用户C的权限,也就是用户B在授予A权限时,必须使用with grant option。

显然这里正是由于第2点的原因,导致用户TEST3不能访问视图。用户TEST1执行下面的操作,将解决这个问题:

SQL> grant select on t1 to test2 with grant option;

授权成功。

对于视图的UPDATE,DELETE权限,同样是如此。

在测试时,有一个现象,有点意思。就是如果用户TEST2没有显式地把V_T1的SELECT权限授予TEST3,而TEST3在有SELECT ANY TABLE或DBA权限时,则查询这个视图时不会报权限不足的错误。由于有SELECT ANY TABLE权限的存在,所有的用户表都可以被访问。但是显式授予表的权限时,似乎表的权限有更高的优先级,并且没有跟系统权限和角色权限进行结合。或者版本不同,表现得不一样,在我的测试中,是Oracle 9.2.0.8 for Windows。


Copyright © 2007 老熊的三分地-Oracle、UNIX、数据恢复. All rights reserved.