每日一得

不求多得,只求一得 about java,hibernate,spring,design,database,Ror,ruby,快速开发
最近关心的内容:SSH,seam,flex,敏捷,TDD
本站的官方站点是:颠覆软件

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  220 随笔 :: 9 文章 :: 421 评论 :: 0 Trackbacks

#

key words: oracle优化 优化oracle  表设计优化 powerdesigner


前言

绝 大多数的Oracle数据库性能问题都是由于数据库设计不合理造成的,只有少部分问题根植于Database Buffer、Share Pool、Redo Log Buffer等内存模块配置不合理,I/O争用,CPU争用等DBA职责范围上。所以除非是面对一个业已完成不可变更的系统,否则我们不应过多地将关注点 投向内存、I/O、CPU等性能调整项目上,而应关注数据库表本身的设计是否合理,库表设计的合理性才是程序性能的真正执牛耳者。

合理的数据库设计需要考虑以下的方面:

·业务数据以何种方式表达。如一个员工有多个Email,你可以在T_EMPLOYEE表中建立多个Email字段如email_1、email_2、 email_3,也可以创建一个T_EMAIL子表来存储,甚至可以用逗号分隔开多个Email地址存放在一个字段中。

·数据以何种方式物理存储。如大表的分区,表空间的合理设计等。

·如何建立合理的数据表索引。表索引几乎是提高数据表查询性能最有效的方法,Oracle拥有类型丰富的数据表索引类型,如何取舍选择显得特别重要。

本 文我们将目光主要聚焦于数据表的索引上,同时也将提及其他两点的内容。通过对一个简单的库表设计实例的分析引出设计中的不足,并逐一改正。考虑到手工编写 库表的SQL脚本原始且低效,我们将用目前最流行的库表设计工具PowerDesigner 10来讲述表设计的过程,所以在本文中你还会了解到一些相关的PowerDesigner的使用技巧。

一个简单的例子

某个开发人员着手设计一个订单的系统,这个系统中有两个主要的业务表,分别是订单基本信息表和订单条目表,这两张表具有主从关系的表,其中T_ORDER是订单主表,而T_ORDER_ITEM是订单条目表。数据库设计人员的设计成果如图 1所示:

合理设计优化Oracle库表设计的若干方法

图 1 订单主从表


ORDER_ID 是订单号,为T_ORDER的主键,通过名为SEQ_ORDER_ID的序列产生键值,而ITEM_ID是T_ORDER_ITEM表的主键,通过名为 SEQ_ORDER_ITEM的序列产生键值,T_ORDER_ITEM通过ORDER_ID外键关联到T_ORDER表。

需求文档指出订单记录将通过以下两种方式来查询数据:

·CLIENT + ORDER_DATE+IS_SHPPED:根据"客户+订货日期+是否发货"条件查询订单及订单条目。

·ORDER_DATE+IS_SHIPPED:根据"订货日期+是否发货"条件查询订单及订单条目。

数 据库设计人员根据这个要求,在T_ORDER表的CLIENT、 ORDER_DATE及IS_SHPPED三字段上建立了一个复合索引IDX_ORDER_COMPOSITE;在T_ORDER_ITEM为外键 ORDER_ID建立IDX_ORDER_ITEM_ORDER_ID索引。

让我们看一下该份设计的最终SQL脚本:

/*订单表*/

create table T_ORDER (

ORDER_ID NUMBER(10) not null,

ADDRESS VARCHAR2(100),

CLIENT VARCHAR2(60),

ORDER_DATE CHAR(8),

IS_SHIPPED CHAR(1),

constraint PK_T_ORDER primary key (ORDER_ID)

);

create index IDX_CLIENT on T_ORDER (

 CLIENT ASC,

 ORDER_DATE ASC,

 IS_SHIPPED ASC);

/*订单条目子表*/

create table T_ORDER_ITEM (

 ITEM_ID NUMBER(10) not null,

 ORDER_ID NUMBER(10),

 ITEM VARCHAR2(20),

 COUNT NUMBER(10),

 constraint PK_T_ORDER_ITEM primary key (ITEM_ID)

);

create index IDX_ORDER_ITEM_ORDER_ID on T_ORDER_ITEM (

 ORDER_ID ASC);

 alter table T_ORDER_ITEM add constraint FK_T_ORDER__REFERENCE_T_ORDER foreign key (ORDER_ID) references T_ORDER (ORDER_ID);



我们承认在ER关系上,这份设计并不存在的缺陷,但却存在以下有待优化的地方:

·没有将表数据和索引数据存储到不同的表空间中,而不加区别地将它们存储到同一表空间里。这样,不但会造成I/O竞争,也为数据库的维护工作带来不便。

·ORACLE会自动为表的主键列创建一个普通B-Tree索引,但由于这两张表的主键值都通过序列提供,具有严格的顺序性(升序或降序),此时手工为其指定一个反键索引(reverse key index)将更加合理。

· 在子表T_ORDER_ITEM外键列ORDER_ID上建立的IDX_ORDER_ITEM_ORDER_ID的普通B-Tree索引非常适合设置为压 缩型索引,即建立一个压缩型的B-Tree索引。因为一份订单会对应多个订单条目,这就意味着T_ORDER_ITEM表存在许多同值的 ORDER_ID列值,通过将其索引指定为压缩型的B-Tree索引,不但可以减少IDX_ORDER_ITEM_ORDER_ID所需的存储空间,还将 提高表操作的性能。

·企图仅通过建立一个包含3字段IDX_ORDER_COMPOSITE复合索引满足如前所述的两种查询条件方式的索引是有问题的,事实上使用ORDER_DATE+IS_SHIPPED复合条件的查询将利用不到IDX_ORDER_COMPOSITE索引。

 优化设计

1、将表数据和索引数据分开表空间存储

1.1 表数据和索引为何需要使用独立的表空间

Oracle 强烈建立,任何一个应用程序的库表至少需要创建两个表空间,其中之一用于存储表数据,而另一个用于存储表索引数据。因为如果将表数据和索引数据放在一起, 表数据的I/O操作和索引的I/O操作将产生影响系统性能的I/O竞争,降低系统的响应效率。将表数据和索引数据存放在不同的表空间中(如一个为 APP_DATA,另一个为APP_IDX),并在物理层面将这两个表空间的数据文件放在不同的物理磁盘上,就可以避免这种竞争了。

拥有独立的表空间,就意味着可以独立地为表数据和索引数据提供独立的物理存储参数,而不会发生相互影响,毕竟表数据和索引数据拥有不同的特性,而这些特性又直接影响了物理存储参数的设定。

此外,表数据和索引数据独立存储,还会带来数据管理和维护上的方面。如你在迁移一个业务数据库时,为了降低数据大小,可以只迁出表数据的表空间,在目标数据库中通过重建索引的方式就可以生成索引数据了。

1.2 表数据和索引使用不同表空间的SQL语法

指定表数据及索引数据存储表空间语句最简单的形式如下。

将表数据存储在APP_DATA表空间里:

create table T_ORDER ( ORDER_ID NUMBER(10) not null, …)tablespace APP_DATA;

将索引数据存储在APP_IDX表空间里:

create index IDX_ORDER_ITEM_ORDER_ID on T_ORDER_ITEM ( ORDER_ID ASC)tablespace APP_IDX;

1.3 PowerDesigner中如何操作

1) 首先,必须创建两个表空间。通过Model->Tablespace...在List of Tablespaces中创建两个表空间:

合理设计优化Oracle库表设计的若干方法(2)

图 2 创建表空间



2) 为每张表指定表数据存储的表空间。在设计区中双击表,打开Table Properties设计窗口,切换到options 页,按图 3所示指定表数据的存储表空间。

合理设计优化Oracle库表设计的若干方法(2)

图 3 指定表数据的存储表空间



3) 为每个索引指定索引数据的存储表空间。在Table Properties中切换到Indexes页,在这里列出了表的所有索引,双击需设置表空间的索引,在弹出的Index Properties窗口中切换到Options页,按如下方式指定索引的存储表空间。



合理设计优化Oracle库表设计的若干方法(2)

图 4 指定索引数据的存储表空间



将表空间的问题延展一下:一个应用系统库表的表空间可以进行更精细的划分。

首先,如果表中存在LOB类型的字段,有为其指定一个特定的表空间,因为LOB类型的数据在物理存储结构的管理上和一般数据的策略有很大的不同,将其放在一个独立的表空间中,就可方便地设置其物理存储参数了。

其 次,需要考虑库表数据的DML操作特性:根据DML(INSERT,UPDATE,DELETE)操作频繁程度,将几乎不发生任何DML操作的数据放在独 立的表空间中,因为极少DML操作的表可设置符合其特性的物理参数:如PCTFREE可置为0,其BUFFER_POOL指定为KEEP,以便将数据缓存 在KEEP数据缓存区中等等,不一而足。

此外,还可以考虑按业务需要将不同的业务模块分开存放,这主要是考虑到备份问题。假设我们有一部分业务数据重要性很强,而其他的业务数据重要性相对较弱,这样就可以将两者分开存储,以便设置不同的备份策略。

当然,无节制的细化表空间也将带来管理上和部署上的复杂,根据业务需求合理地规划表空间以达到管理和性能上的最佳往往需要更多的权衡。

 2、显式为主键列建立反向键索引

2.1 反向键索引的原理和用途

我 们知道Oracle会自动为表的主键列建立索引,这个默认的索引是普通的B-Tree索引。对于主键值是按顺序(递增或递减)加入的情况,默认的B- Tree索引并不理想。这是因为如果索引列的值具有严格顺序时,随着数据行的插入,索引树的层级增长很快。搜索索引发生的I/O读写次数和索引树的层级数 成正比,也就是说,一棵具有5个层级的B-Tree索引,在最终读取到索引数据时最多可能发生多达5次I/O操作。因而,减少索引的层级数是索引性能调整 的一个重要方法。

如果索引列的数据以严格的有序的方式插入,那么B-Tree索引树将变成一棵不对称的"歪树",如图 5所示:

合理设计优化Oracle库表设计的若干方法(3)

图 5不对称的B-Tree索引


而如果索引列的数据以随机值的方式插入,我们将得到一棵趋向对称的索引树,如图 6所示:

合理设计优化Oracle库表设计的若干方法(3)

图 6对称的B-Tree索引


比较图 5和图 6,在图 5中搜索到A块需要进行5次I/O操作,而图 6仅需要3次I/O操作。

既 然索引列数据从序列中获取,其有序性无法规避,但在建立索引时,Oracle允许对索引列的值进行反向,即预先对列值进行比特位的反向,如 1000,10001,10011,10111,1100经过反向后的值将是0001,1001,1101,0011。显然经过位反向处理的有序数据变得 比较随机了,这样所得到的索引树就比较对称,从而提高表的查询性能。

但反向键索引也有它局限性:如果在WHERE语句中,需要对索引列的 值进行范围性的搜索,如BETWEEN、<、>等,其反向键索引无法使用,此时,Oracle将执行全表扫描;只有对反向键索引列进行 <> 和 = 的比较操作时,其反向键索引才会得到使用。

2.2 反向键索引的SQL语句

回到我们上面的例子,由于T_ORDER和T_ORDER_ITEM的主键值来源于序列,主键值是有严格顺序的,所以我们应该摒弃默认的Oracle所提供的索引,而采取显式为主键指定一个反向键索引的方式。

ORDER_ID为T_ORDER表的主键,主键名为PK_ORDER,我们为ORDER_ID列上建立一个反向键索引IDX_ORDER_ID,并使PK_ORDER_ID使用这个索引,其SQL语句如下:

create table T_ORDER (

 ORDER_ID NUMBER(10) not null,

 CLIENT VARCHAR2(60),

 ADDRESS VARCHAR2(100),

 ORDER_DATE CHAR(8));

create unique index IDX_ORDER_ID on T_ORDER ( ORDER_ID ASC) reverse;alter table T_ORDER add constraint PK_ORDER primary key (ORDER_ID) using index IDX_ORDER_ID;


要保证创建IDX_ORDER_ID的SQL语句在创建PK_ORDER主键的SQL语句之前,因为主键需要引用到这个反向键索引。

由于主键列的数据是唯一的,所以为IDX_ORDER_ID加上unique限定,使其成为唯一型的索引。

2.3 PowerdDesigner如何操作

1) 首先,需要为ORDER_ID列建立一个反向键索引。打开T_ORDER的Table Properties的窗口,切换到Indexes页,新建一个名为IDX_ORDER_ID的索引。填写完索引的名称后,双击这个索引,弹出Index Properties窗口,在这个窗口的Columns中选择ORDER_ID列。然后,切换到Options页,按图 7的方式将其设置为反向键索引。

合理设计优化Oracle库表设计的若干方法(3)

图 7 设置反向键索引
 

2) 显式指定主键PK_ORDER使用这个索引。在Table Properties窗口中切换到Keys页,默认情况下,PowerDesigner为T_ORDER所指定的主键名为Key1,我们将其更名为 PK_ORDER,双击这个主键,弹出Key Properties窗口,切换到Options页,按图 8的方式为PK_ORDER指定IDX_ORDER_ID。

合理设计优化Oracle库表设计的若干方法(4)

图 8 为主键指定特定的索引


不可否认PowerDesigner确实是目前业界最强大易用的数据库设计工具,但很遗憾,当我们为表主键指定一个索引时,其产生的语句在顺序上有问题:即创建主键的语句位于创建索引语句之前:

create table T_ORDER (…);alter table T_ORDER add constraint PK_T_ORDER primary key (ORDER_ID) using index IDX_ORDER_ID;create unique index IDX_ORDER_ID on T_ORDER ( ORDER_ID ASC) reverse;


我们可以通过对PowerDesigner生成SQL语句的设置进行调整,先生成创建表和索引的SQL语句,再创建为表添加主键和外键的SQL语句来达到曲线救国的目的,请看下一步。

3)通过菜单Database->Generate Database...调出Database Configuration窗口,切换到Keys&Indexes页,按图 9设置:



合理设计优化Oracle库表设计的若干方法(4)

图 9 设置生成键和索引SQL的选项


这里,我们将Primary Keys和Foreign keys的选项都取消,而将Indexes勾选,以达到只生成表的索引SQL语句的目的。

点击"确定"后,生成创建数据库表及其索引的SQL语句,运行该SQL创建数据库后,再按图 10设置生成为表添加主键和外键的SQL语句:

合理设计优化Oracle库表设计的若干方法(4)

图 10 生成创建表主键和外键的SQL语句



除此设置外,还必须切换到Tables & Views页下,取消所有选项,避免重新生成创建表的语句。

 3、将子表的外键列的索引改为压缩型

3.1 压缩型索引的原理和用途

在前面的例子中,由于一条订单会对应多条订单条目,所以T_ORDER_ITEM的ORDER_ID字段总会出现重复的值,如:

ITEM_ID ORDER_ID ITEM COUNT

1 100 101 1

2 100 104 2

3 100 201 3

4 200 301 2

5 200 401 1

6 200 205 3


在ORDER_ID列上创建一个普通未压缩的B-Tree索引,则索引数据的物理上的存储形式如下:

合理设计优化Oracle库表设计的若干方法(5)

图 11 未进行压缩的索引存储


ORDER_ID的重复值在索引块中重复出现,这样不但增加了存储空间的需求,而且因为查询时需要读取更多的索引数据块,所以查询性能也会降低=。让我们来看一下经过压缩后索引数据的存储方式:

合理设计优化Oracle库表设计的若干方法(5)

图 12 进行压缩的索引存储


压缩型的索引消除了重复的索引值,将相同索引列值所关联的ROWID存储在一起。这样,不但节省了存储空间,查询效率也提高了,真可谓两全齐美了。

对象T_ORDER和T_ORDER_ITEM这样的主从表进行查询时,一般情况下,我们都必须通过外键查询出子表所有关联的记录,所以在子表的外键上建立压缩型的索引是非常适合的。

3.2 压缩型索引的SQL语句

创建压缩型索引的SQL语句非常简单,在T_ORDER_ITEM的ORDER_ID上创建压缩型索引的SQL如下所示:

create index IDX_ORDER_ITEM_ORDER_ID on T_ORDER_ITEM ( ORDER_ID ASC) compress;


需要在创建索引的语句后附上compress关键字就可以了。

3.3 PowerDesigner如何创建压缩型索引

1) 打开T_ORDER_ITEM表的Table Properties的窗口,切换到Indexes页,为ORDER_ID列创建一个名为IDX_ORDER_ITEM_ORDER_ID的索引。

2) 双击IDX_ORDER_ITEM_ORDER_ID弹出Index Properties窗口,切换到Options页,按图 13将索引设置为压缩型:

合理设计优化Oracle库表设计的若干方法(5)

图 13 将索引指定为压缩型


4、建立满足需求的复合键索引

设计人员希望通过T_ORDER表上的IDX_ORDER_COMPOSITE复合索引满足以下两种组合条件的查询:

·CLIENT + ORDER_DATE + IS_SHIPPED

·ORDER_DATE + IS_SHIPPED

为方便阐述,我们特地将IDX_ORDER_COMPOSITE的创建SQL语句再次列出:

create index IDX_ORDER_COMPOSITE on T_ORDER ( CLIENT ASC, ORDER_DATE ASC, IS_SHIPPED ASC);


事实上,在CLIENT + ORDER_DATE + IS_SHIPPED 三列上所执行的复合条件查询会应用到这个索引,而在ORDER_DATE + IS_SHIPPED列上所执行的复合查询不会使用这个索引,因而将导致一个全表扫描的操作。

可以用许多工具来了解查询语句的执行计划,通过SET AUTOTRACE ON来查询以上两个复合查询的执行计划:

打开SQL/Plus,输入以下的语句:

SQL> set autotrace on

SQL> select * from t_order where CLIENT = '1' and ORDER_DATE='1' and IS_SHIPPED='1';


分析得到的执行计划为:

SELECT STATEMENT Optimizer=CHOOSETABLE ACCESS (BY INDEX ROWID) OF 'T_ORDER' INDEX (RANGE SCAN) OF 'IDX_ORDER_COMPOSITE' (NON-UNIQUE)


可见Oracle先利用IDX_ORDER_COMPOSITE得到满足条件的记录ROWID,再通过ROWID返回记录。

而下面查询语句:

SQL> select * from t_order where ORDER_DATE='1' and IS_SHIPPED='1'


的执行计划则为:

SELECT STATEMENT Optimizer=CHOOSE TABLE ACCESS (FULL) OF 'T_ORDER'


很明显,Oracle在T_ORDER表上执行了一个全表扫描的操作,没有用到IDX_ORDER_COMPOSITE索引。

对复合列索引,我们得出这个结论:

假设在COL_1,COL_2,…,COL_n这些列上建立了一个复合索引:

create index IDX _COMPOSITE on TABLE1

{

COL_1,

COL_2,

…,

COL_n

}


则只有WHERE语句上包含COL_1(复合索引的第一个字段)的查询才会使用这个复合索引,而未包含COL_1的查询则不会使用这个复合索引。

回到我们的例子,如何建立满足CLIENT + ORDER_DATE + IS_SHIPPED和ORDER_DATE + IS_SHIPPED两种查询的索引呢?

考 虑到IS_SHIPPED列基数很小,只有两个可能的值:0,1。在这种情况下,有两种方案:第一,分别为CLIENT + ORDER_DATE + IS_SHIPPED和ORDER_DATE + IS_SHIPPED建立一个复合索引;第二,分别在CLIENT和ORDER_DATE列上建立一个索引,而IS_SHIPEED列不建立索引。

第一种方案的查询效率最快,但因为CLIENT和ORDER_DATE在索引中会重复出现两次,占用较大的存储空间。第二种方案CLIENT和ORDER_DATE不会在索引存储出现两次,较为节省空间,查询效率比之于第一种方案会稍低一些,但影响不大。

我们采用第二种方案为CLIENT和ORDER_DATE分别创建索引IDX_CLIENT和IDX_ORDER_DATE,组合查询条件为CLIENT + ORDER_DATE + IS_SHIPPED时的执行计划为:

SELECT STATEMENT Optimizer=CHOOSE TABLE ACCESS (BY INDEX ROWID) OF 'T_ORDER' AND-EQUAL INDEX (RANGE SCAN) OF 'IDX_CLIENT' (NON-UNIQUE) INDEX (RANGE SCAN) OF 'IDX_ORDER_DATE' (NON-UNIQUE)


而组合条件为ORDER_DATE + IS_SHIPPED时的执行计划为:

SELECT STATEMENT Optimizer=CHOOSE TABLE ACCESS (BY INDEX ROWID) OF 'T_ORDER' INDEX (RANGE SCAN) OF 'IDX_ORDER_DATE' (NON-UNIQUE)


通过这样的改造,我们得到了一个满足两种组合查询的执行计划。

总结

贯穿本文的订单主从表实例结构上很简单,但是其粗糙的设计包含了许多问题,这也是许多对Oracle物理存储结构没有很好理解的数据库设计师容易忽视的地方。

在一般情况下,这样的设计并不会导致严重系统的性能问题,但是精益求精是每一位优秀软件设计师的品质,此外,对于设计师,一定要清楚这样一条规律:对于等质的性能提升,在编码层面往往需要比设计层面付出更多的艰辛。

在Oracle中提高数据库的性能需要考虑的问题,注意的误区还很多,本文涵盖是一些最常见的问题。下面,我们将提高数据库操作性能方法及一些误区作个小结:

·对于大表,可以考虑创建分区表,分区表有范围分区、散列分区、列表分区和散列分区几种,通过它可以达到化大表为小表的目的。

·考虑适量的数据冗余,如一个业务表有一个审批状态,审批需要经过多步,每一步对应审批表的一条记录,最后审批的那条记录决定了业务的状态。我们大可在业务表中存放一个审批状态的标志,以取消每次需要通过关联审批表获取业务审批状态的复杂的关联表查询。

· 不要做太多的关联表查询,一些几乎不发生数据变动的表码表,如性别,学历,婚姻状态等表码表,可以考虑在应用程序启动时一次性地下载到应用程序的内存中缓 存起来,在从数据库获取结果集后,再由程序利用这些缓存的表码表数据来翻译这些表码字段,而不要在数据库中通过表间的关联查询方式来翻译这些字段。

· 常看到一些令我瞠目的设计:在需要进行频繁DML(INSERT,UPDATE,DELETE)操作的表的某些基数低的字段(如性别,婚姻状态)上创建位 图索引。位图索引是好东西,但它是有使用范围的,在OLTP系统中,需要进行频繁DML操作的表中不应该出现位图索引,位图索引只适用于几乎不进行DML 操作,只进行查询的DSS系统中。此外,聚簇和索引组织表也都更适合DSS系统,而非OLTP系统。
(完)

posted @ 2006-07-19 18:16 Alex 阅读(373) | 评论 (0)编辑 收藏

key words: Oracle监控  shell脚本

前言

这篇文章介绍了DBA每天在监控Oracle数 据库方面的职责,讲述了如何通过shell脚本来完成这些重复的监控工作。本文首先回顾了一些DBA常用的Unix命令,以及解释了如何通过Unix Cron来定时执行DBA脚本。同时文章还介绍了8个重要的脚本来监控Oracle数据库:

检查实例的可用性

检查监听器的可用性

检查alert日志文件中的错误信息

在存放log文件的地方满以前清空旧的log文件

分析table和index以获得更好的性能

检查表空间的使用情况

找出无效的对象

监控用户和事务

DBA需要的Unix基本知识

基本的UNIX命令

以下是一些常用的Unix命令:

ps--显示进程 grep--搜索文件中的某种文本模式 mailx--读取或者发送mail cat--连接文件或者显示它们 cut--选择显示的列 awk--模式匹配语言 df--显示剩余的磁盘空间

以下是DBA如何使用这些命令的一些例子:

显示服务器上的可用实例:

$ ps -ef | grep smon

oracle 21832 1 0 Feb 24 ? 19:05 ora_smon_oradb1

oracle 898 1 0 Feb 15 ? 0:00 ora_smon_oradb2

dliu 25199 19038 0 10:48:57 pts/6 0:00 grep smon

oracle 27798 1 0 05:43:54 ? 0:00 ora_smon_oradb3

oracle 28781 1 0 Mar 03 ? 0:01 ora_smon_oradb4、
显示服务器上的可用监听器:

$ ps -ef | grep listener | grep -v grep

(译者注:grep命令应该加上-i参数,即grep -i listener,

该参数的作用是忽略大小写,因为有些时候listener是大写的,这时就会看不到结果)

oracle 23879 1 0 Feb 24 ? 33:36 /8.1.7/bin/tnslsnr listener_db1 -inherit

oracle 27939 1 0 05:44:02 ? 0:00 /8.1.7/bin/tnslsnr listener_db2 -inherit

oracle 23536 1 0 Feb 12 ? 4:19 /8.1.7/bin/tnslsnr listener_db3 -inherit

oracle 28891 1 0 Mar 03 ? 0:01 /8.1.7/bin/tnslsnr listener_db4 -inherit
查看Oracle存档目录的文件系统使用情况

$ df -k | grep oraarch

/dev/vx/dsk/proddg/oraarch 71123968 4754872 65850768 7% /u09/oraarch
统计alter.log文件中的行数:

$ cat alert.log | wc -l

2984

列出alert.log文件中的全部Oracle错误信息:

$ grep ORA- alert.log

ORA-00600: internal error code, arguments: [kcrrrfswda.1], [], [], [], [], []

ORA-00600: internal error code, arguments: [1881], [25860496], [25857716], []

CRONTAB基本

一个crontab文件中包含有六个字段:

分钟 0-59

小时 0-23

月中的第几天 1-31

月份 1 - 12

星期几 0 - 6, with 0 = Sunday

Unix命令或者Shell脚本

要编辑一个crontab文件,输入:Crontab -e

要查看一个crontab文件,输入:

Crontab -l

0 4 * * 5 /dba/admin/analyze_table.ksh

30 3 * * 3,6 /dba/admin/hotbackup.ksh /dev/null 2>&1

在上面的例子中,第一行显示了一个分析表的脚本在每个星期5的4:00am运行。第二行显示了一个执行热备份的脚本在每个周三和周六的3:00a.m.运行。

监控数据库的常用Shell脚本

以下提供的8个shell脚本覆盖了DBA每日监控工作的90%,你可能还需要修改UNIX的环境变量。

检查Oracle实例的可用性

oratab文件中列出了服务器上的所有数据库

$ cat /var/opt/oracle/oratab


############################################################


## /var/opt/oracle/oratab##


############################################################

oradb1:/u01/app/oracle/product/8.1.7:Y

oradb2:/u01/app/oracle/product/8.1.7:Y

oradb3:/u01/app/oracle/product/8.1.7:N

oradb4:/u01/app/oracle/product/8.1.7:Y
以下的脚本检查oratab文件中列出的所有数据库,并且找出该数据库的状态(启动还是关闭)

##############################################################


## ckinstance.ksh ## ###################################################################

ORATAB=/var/opt/oracle/oratab

echo `date`

echo Oracle Database(s) Status `hostname` :

db=`egrep -i :Y|:N $ORATAB | cut -d: -f1 | grep -v # | grep -v *`

pslist=`ps -ef | grep pmon`

for i in $db ; do

echo $pslist | grep ora_pmon_$i > /dev/null 2>$1

if (( $? )); then

echo Oracle Instance - $i: Down

else

echo Oracle Instance - $i: Up

fi

done

使用以下的命令来确认该脚本是可以执行的:

$ chmod 744 ckinstance.ksh

$ ls -l ckinstance.ksh

-rwxr--r-- 1 oracle dba 657 Mar 5 22:59 ckinstance.ksh*
以下是实例可用性的报表:

$ ckinstance.ksh

Mon Mar 4 10:44:12 PST 2002

Oracle Database(s) Status for DBHOST server:

Oracle Instance - oradb1: Up

Oracle Instance - oradb2: Up

Oracle Instance - oradb3: Down

Oracle Instance - oradb4: Up

检查Oracle监听器的可用性

以下有一个类似的脚本检查Oracle监听器。如果监听器停了,该脚本将会重新启动监听器:

#######################################################################

## cklsnr.sh ##

#######################################################################

#!/bin/ksh

DBALIST=primary.dba@company.com,another.dba@company.com;export DBALIST

cd /var/opt/oracle

rm -f lsnr.exist

ps -ef | grep mylsnr | grep -v grep > lsnr.exist

if [ -s lsnr.exist ]

then

echo

else

echo Alert | mailx -s Listener ‘mylsnr‘ on `hostname` is down $DBALIST

TNS_ADMIN=/var/opt/oracle; export TNS_ADMIN

ORACLE_SID=db1; export ORACLE_SID

ORAENV_ASK=NO; export ORAENV_ASK

PATH=$PATH:/bin:/usr/local/bin; export PATH

. oraenv

LD_LIBRARY_PATH=${ORACLE_HOME}/lib;export LD_LIBRARY_PATH

lsnrctl start mylsnr

fi

检查Alert日志(ORA-XXXXX)

每个脚本所使用的一些环境变量可以放到一个profile中:

####################################################################### 

## oracle.profile ##

#######################################################################

EDITOR=vi;export EDITOR ORACLE_BASE=/u01/app/oracle; export

ORACLE_BASE ORACLE_HOME=$ORACLE_BASE/product/8.1.7; export

ORACLE_HOME LD_LIBRARY_PATH=$ORACLE_HOME/lib; export

LD_LIBRARY_PATH TNS_ADMIN=/var/opt/oracle;export

TNS_ADMIN NLS_LANG=american; export

NLS_LANG NLS_DATE_FORMAT=‘Mon DD YYYY HH24:MI:SS‘; export

NLS_DATE_FORMAT ORATAB=/var/opt/oracle/oratab;export

ORATAB PATH=$PATH:$ORACLE_HOME:$ORACLE_HOME/bin:/usr/ccs/bin:/bin:/usr/bin:/usr/sbin:/

sbin:/usr/openwin/bin:/opt/bin:.; export

PATH DBALIST=primary.dba@company.com,another.dba@company.com;export

DBALIST
以下的脚本首先调用oracle.profile来设置全部的环境变量。如果发现任何的Oracle错误,该脚本还会给DBA发送一个警告的email。

####################################################################

## ckalertlog.sh ##

####################################################################

#!/bin/ksh

.. /etc/oracle.profile

for SID in `cat $ORACLE_HOME/sidlist`

do

cd $ORACLE_BASE/admin/$SID/bdump

if [ -f alert_${SID}.log ]

then

mv alert_${SID}.log alert_work.log

touch alert_${SID}.log

cat alert_work.log >> alert_${SID}.hist

grep ORA- alert_work.log > alert.err

fi

if [ `cat alert.err|wc -l` -gt 0 ]

then

mailx -s ${SID} ORACLE ALERT ERRORS $DBALIST < alert.err

fi

rm -f alert.err

rm -f alert_work.log

done

清除旧的归档文件

以下的脚本将会在log文件达到90%容量的时候清空旧的归档文件:

$ df -k | grep arch

Filesystem kbytes used avail capacity Mounted on

/dev/vx/dsk/proddg/archive 71123968 30210248 40594232 43% /u08/archive

#######################################################################

## clean_arch.ksh ##

#######################################################################

#!/bin/ksh

df -k | grep arch > dfk.result

archive_filesystem=`awk -F ‘{ print $6 }‘ dfk.result`

archive_capacity=`awk -F ‘{ print $5 }‘ dfk.result`

if [[ $archive_capacity > 90% ]]

then

echo Filesystem ${archive_filesystem} is ${archive_capacity} filled

# try one of the following option depend on your need

find $archive_filesystem -type f -mtime +2 -exec rm -r {} ;

tar

rman

fi

分析表和索引(以得到更好的性能)

以下我将展示如果传送参数到一个脚本中:

#################################################################### 

## analyze_table.sh ##

####################################################################

#!/bin/ksh

# input parameter: 1: password # 2: SID

if (($#<1)) then echo "Please enter oracle user password as the first parameter !" exit 0

fi

if (($#<2)) then echo "Please enter instance name as the second parameter!" exit 0

fi

要传入参数以执行该脚本,输入:

$ analyze_table.sh manager oradb1 
脚本的第一部分产生了一个analyze.sql文件,里面包含了分析表用的语句。脚本的第二部分分析全部的表:

#####################################################################

## analyze_table.sh ##

#####################################################################

sqlplus -s <
oracle/$1@$2

set heading off

set feed off

set pagesize 200

set linesize 100

spool analyze_table.sql

select ANALYZE TABLE || owner || . || segment_name ||

ESTIMATE STATISTICS SAMPLE 10 PERCENT;

from dba_segments

where segment_type = TABLE

and owner not in (SYS, SYSTEM);

spool off

exit

!

sqlplus -s <
oracle/$1@$2

@./analyze_table.sql

exit

!
以下是analyze.sql的一个例子:

$ cat analyze.sql

ANALYZE TABLE HIRWIN.JANUSAGE_SUMMARY ESTIMATE STATISTICS SAMPLE 10 PERCENT;

ANALYZE TABLE HIRWIN.JANUSER_PROFILE ESTIMATE STATISTICS SAMPLE 10 PERCENT;

ANALYZE TABLE APPSSYS.HIST_SYSTEM_ACTIVITY ESTIMATE STATISTICS SAMPLE 10 PERCENT;

ANALYZE TABLE HTOMEH.QUEST_IM_VERSION ESTIMATE STATISTICS SAMPLE 10 PERCENT;

ANALYZE TABLE JSTENZEL.HIST_SYS_ACT_0615 ESTIMATE STATISTICS SAMPLE 10 PERCENT;

ANALYZE TABLE JSTENZEL.HISTORY_SYSTEM_0614 ESTIMATE STATISTICS SAMPLE 10 PERCENT;

ANALYZE TABLE JSTENZEL.CALC_SUMMARY3 ESTIMATE STATISTICS SAMPLE 10 PERCENT;

ANALYZE TABLE IMON.QUEST_IM_LOCK_TREE ESTIMATE STATISTICS SAMPLE 10 PERCENT;

ANALYZE TABLE APPSSYS.HIST_USAGE_SUMMARY ESTIMATE STATISTICS SAMPLE 10 PERCENT;

ANALYZE TABLE PATROL.P$LOCKCONFLICTTX ESTIMATE STATISTICS SAMPLE 10 PERCENT;

检查表空间的使用

以下的脚本检测表空间的使用。如果表空间只剩下10%,它将会发送一个警告email。

#####################################################################

## ck_tbsp.sh ##

#####################################################################

#!/bin/ksh

sqlplus -s <
oracle/$1@$2

set feed off

set linesize 100

set pagesize 200

spool tablespace.alert

SELECT F.TABLESPACE_NAME,

TO_CHAR ((T.TOTAL_SPACE - F.FREE_SPACE),999,999) "USED (MB)",

TO_CHAR (F.FREE_SPACE, 999,999) "FREE (MB)",

TO_CHAR (T.TOTAL_SPACE, 999,999) "TOTAL (MB)",

TO_CHAR ((ROUND ((F.FREE_SPACE/T.TOTAL_SPACE)*100)),999)|| % PER_FREE

FROM (

SELECT TABLESPACE_NAME,

ROUND (SUM (BLOCKS*(SELECT VALUE/1024

FROM V\$PARAMETER

WHERE NAME = db_block_size)/1024)

) FREE_SPACE

FROM DBA_FREE_SPACE

GROUP BY TABLESPACE_NAME

) F,

(

SELECT TABLESPACE_NAME,

ROUND (SUM (BYTES/1048576)) TOTAL_SPACE

FROM DBA_DATA_FILES

GROUP BY TABLESPACE_NAME

) T

WHERE F.TABLESPACE_NAME = T.TABLESPACE_NAME

AND (ROUND ((F.FREE_SPACE/T.TOTAL_SPACE)*100)) < 10;

spool off

exit

!

if [ `cat tablespace.alert|wc -l` -gt 0 ]

then

cat tablespace.alert -l tablespace.alert > tablespace.tmp

mailx -s "TABLESPACE ALERT for ${2}" $DBALIST < tablespace.tmp

fi

警告email输出的例子如下:

TABLESPACE_NAME USED (MB) FREE (MB) TOTAL (MB) PER_FREE 

------------------- --------- ----------- ------------------- ------------------

SYSTEM 2,047 203 2,250 9 %

STBS01 302 25 327 8 %

STBS02 241 11 252 4 %

STBS03 233 19 252 8 %
查找出无效的数据库对象

以下查找出无效的数据库对象:

#####################################################################
## invalid_object_alert.sh ##
##################################################################### 
#!/bin/ksh . /etc/oracle.profile 

sqlplus -s <
oracle/$1@$2

set feed off

set heading off column object_name format a30

spool invalid_object.alert

SELECT OWNER, OBJECT_NAME, OBJECT_TYPE,
STATUS FROM DBA_OBJECTS WHERE STATUS = 
INVALID ORDER BY OWNER, OBJECT_TYPE, OBJECT_NAME;

spool off

exit ! if [ `cat invalid_object.alert|wc -l` -gt 0 ] then

mailx -s "INVALID OBJECTS for ${2}" $DBALIST < invalid_object.alert

fi$ cat invalid_object.alert

OWNER OBJECT_NAME OBJECT_TYPE STATUS

--------------------------------------------
HTOMEH DBMS_SHARED_POOL PACKAGE BODY INVALID

HTOMEH X_$KCBFWAIT VIEW INVALID

IMON IW_MON PACKAGE INVALID

IMON IW_MON PACKAGE BODY INVALID

IMON IW_ARCHIVED_LOG VIEW INVALID

IMON IW_FILESTAT VIEW INVALID

IMON IW_SQL_FULL_TEXT VIEW INVALID

IMON IW_SYSTEM_EVENT1 VIEW INVALID

IMON IW_SYSTEM_EVENT_CAT VIEW INVALIDLBAILEY CHECK_TABLESPACE_USAGE PROCEDURE INVALID

PATROL P$AUTO_EXTEND_TBSP VIEW INVALID

SYS DBMS_CRYPTO_TOOLKIT PACKAGE INVALID

SYS DBMS_CRYPTO_TOOLKIT PACKAGE BODY INVALID

SYS UPGRADE_SYSTEM_TYPES_TO_816 PROCEDURE INVALID

SYS AQ$_DEQUEUE_HISTORY_T TYPE INVALID

SYS HS_CLASS_CAPS VIEW INVALID SYS HS_CLASS_DD VIEW INVALID
监视用户和事务(死锁等)

以下的脚本在死锁发生的时候发送一个警告e-mail:

###################################################################

## deadlock_alert.sh ##

##################################################################
##!/bin/ksh

.. /etc/oracle.profile

sqlplus -s <
oracle/$1@$2

set feed off

set heading off

spool deadlock.alert

SELECT SID, DECODE(BLOCK, 0, NO, YES ) BLOCKER,

DECODE(REQUEST, 0, NO,YES ) WAITER

FROM V$LOCK

WHERE REQUEST > 0 OR BLOCK > 0

ORDER BY block DESC;

spool off

exit

!

if [ `cat deadlock.alert|wc -l` -gt 0 ]

then

mailx -s "DEADLOCK ALERT for ${2}" $DBALIST < deadlock.alert

fi


结论

0,20,40 7-17 * * 1-5 /dba/scripts/ckinstance.sh > /dev/null 2>&1

0,20,40 7-17 * * 1-5 /dba/scripts/cklsnr.sh > /dev/null 2>&1

0,20,40 7-17 * * 1-5 /dba/scripts/ckalertlog.sh > /dev/null 2>&1

30 * * * 0-6 /dba/scripts/clean_arch.sh > /dev/null 2>&1

* 5 * * 1,3 /dba/scripts/analyze_table.sh > /dev/null 2>&1

* 5 * * 0-6 /dba/scripts/ck_tbsp.sh > /dev/null 2>&1

* 5 * * 0-6 /dba/scripts/invalid_object_alert.sh > /dev/null 2>&1

0,20,40 7-17 * * 1-5 /dba/scripts/deadlock_alert.sh > /dev/null 2>&1

通过以上的脚本,可大大减轻你的工作。你可以使用这些是来做更重要的工作,例如性能调整。

posted @ 2006-07-18 16:08 Alex 阅读(667) | 评论 (0)编辑 收藏


									2003-05 余枫

ORACLE客户端连服务器的注意事项:

1. 通过SQL*NET协议,ORACLE客户端连服务器时一般需要配置sqlnet.ora和tnsnames.ora。
它们默认的目录在$ORACLE_HOME/network/admin 目录下

也可以设置环境变量TNS_ADMIN指向你想用的sqlnet.ora和tnsnames.ora目录
例如:
TNS_ADMIN=/home/oracle/config/9.0.1;export TNS_ADMIN

sqlnet.ora文件决定找数据库服务器别名的方式

默认的参数有
NAMES.DEFAULT_DOMAIN = WORLD
NAMES.DIRECTORY_PATH = (TNSNAMES, ONAMES, HOSTNAME)

如果你的ORACLE客户端和服务器默认的域名不一样,需要用#号注释第一行
#NAMES.DEFAULT_DOMAIN = WORLD
使它不起作用。

NAMES.DIRECTORY_PATH指定找服务器别名的顺序 (本地的tnsnames.ora文件, 命名服务器, 主机名方式)

服务器的sqlnet.ora里可以设置检查客户端是否alive的时间间隔
sqlnet.expire_time = 10

tnsnames.ora文件里写数据库服务器别名的详细内容,有以下几种写法:

# 一般的写法
APPDB =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.35)(PORT = 1521))
)
(CONNECT_DATA =
(SERVICE_NAME = appdb)
)
)

# 明确标明用dedicated方式连接数据库
APPD=
(DESCRIPTION=
(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.0.35)(PORT=1521))
(CONNECT_DATA=
(SERVICE_NAME=appdb)
(SERVER=DEDICATED)))

# 对多个listener端口做均衡负载方式连接数据库
APPS =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.35)(PORT = 1521))
(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.35)(PORT = 1856))
)
(CONNECT_DATA =
(SERVICE_NAME = appdb)
)
)

# 注意:如果数据库服务器用MTS,客户端程序需要用database link时最好明确指明客户端用dedicated直连方式,
# 不然会遇到很多跟分布式环境有关的ORACLE BUG。
# 一般情况下数据库服务器用直接的连接会好一些,除非你的实时数据库连接数接近1000。

2. /etc/hosts (UNIX)
或者windows\hosts(WIN98) winnt\system32\drivers\etc\hosts (WIN2000)
客户端需要写入数据库服务器IP地址和主机名的对应关系。

127.0.0.1 localhost
192.168.0.35 oracledb oracledb
192.168.0.45 tomcat tomcat
202.84.10.193 bj_db bj_db

有些时候我们配置好第一步后,tnsping 数据库服务器别名显示是成功的,
但是sqlplus username/password@servicename不通,jdbc thin link 也不通的时候,
一定不要忘了在客户端做这一步,原因可能是DNS服务器里没有设置这个服务器IP地址和主机名的对应关系。

如果同时有私有IP和Internet上公有IP,私有IP写在前面,公有IP写在后面。

编辑前最好留一个备份,增加一行时也最好用复制粘贴,避免编辑hosts时空格或者tab字符错误。

3. UNIX下ORACLE多数据库的环境,OS客户端需要配置下面两个环境变量

ORACLE_SID=appdb;export ORACLE_SID
TWO_TASK=appdb;export TWO_TASK

来指定默认的目标数据库。
posted @ 2006-07-17 22:31 Alex 阅读(263) | 评论 (0)编辑 收藏

Windows

  • 下载并安装 Tor & Privoxy
    http://www.vidalia-project.net/dist/vidalia-bundle-0.1.1.21-0.0.5.exe
    安装完之后,在localhost:8118有http代理(privoxy提供),localhost:9050有socks5代理(tor提供)
    如果这时候可以配置Firefox使用8118和9050这两个端口,那就所有的Internet访问都将通过tor访问,会很慢。所以这种方式不推荐,要自己编辑一下代理配置脚本
    Proxy settings in Firefox
  • 编辑proxy.pac
    可以在某个地方创建代理配置脚本, 比如说c:\proxy.pac, 下面是我用的内容, 可以上一些比较常用的网站, 比如wikipedia和google网页快照. 如果查询google也经常有问题, 可以把第一条 nosite.google.com 改成 .google.com 记得google.com前面有个点, 这样所有访问google的请求都会通过代理,不过做好心理准备,会很慢
    function FindProxyForURL(url, host)
    {
    url = url.toLowerCase();
    host = host.toLowerCase();

    if(dnsDomainIs(host,"nosite.google.com")) return "PROXY localhost:8118";
    else if(dnsDomainIs(host,".blogspot.com")) return "PROXY localhost:8118";
    else if(dnsDomainIs(host,".wordpress.com")) return "PROXY localhost:8118";
    else if(dnsDomainIs(host,"wikipedia.org")) return "PROXY localhost:8118";
    else if(shExpMatch(url,"*q=cache:*")) return "PROXY localhost:8118";
    else return "DIRECT";
    }
  • 配置IE使用代理配置脚本
    上面那个Firefox配置界面中,选择Aotumatic proxy configuration URL, 填入
    file://c:/proxy.pac

    IE的配置类似.每次重新修改proxy.pac,都应该到上面的界面Reload代理配置脚本(比较讨厌,好在这个文件修改次数不会很多)

Debian Linux

这里主要讲Debian下的安装配置,其他版本的Linux可以参考tor的官方说明

  • 安装 tor privoxy
  •  apt-get install tor privoxy
  • 配置privoxy
  • Debian下面的privoxy需要额外的配置,因为privoxy需要使用tor提供的9050 socks5端口.具体是配置/etc/privoxy/config,加一行

    forward-socks4a / localhost:9050 .

    不要漏掉最后的点.然后重起一下privoxy

    /etc/init.d/tor restart
  • 如果使用Firefox,剩下的配置可以参考Windows下面的配置方法

参考

http://www.linuxsir.org/bbs/showthread.php?t=232436
http://tor.eff.org/docs/tor-doc-win32.html.en

Technorati Tags: , ,

posted @ 2006-07-17 11:01 Alex 阅读(3133) | 评论 (0)编辑 收藏

     摘要: 作者:John Smiley 学习在 RHEL 2.1、RHEL 3 或 SLES 8 上从头安装 Oracle 数据库 10g 的基础知识(仅用于评估) ...  阅读全文
posted @ 2006-07-13 13:57 Alex 阅读(636) | 评论 (0)编辑 收藏

key words: Oracle闪回 flash

  1、Oracle 9i的闪回查询功能

   在Oracle 9i之前,如果用户错误操作数据后,除了不完全恢复外,没有好的解决办法。Oracle 9i中提供闪回查询,由一个新的包DBMS_FLASH来实现。用户使用闪回查询可以及时取得误操作DML(Delete、Update、Insert) 前某一时间点数据库的映像视图,用户可以利用系统时间或系统改变号(SCN:System Change Number)来指定这个只读视图,并可以针对错误进行相应的恢复措施。闪回查询功能完全依赖于自动回滚段管理(AUM),对于Drop等误操作不能恢 复。闪回特性可应用在以下方面:

  (1)自我维护过程中的修复:当一些重要的记录被意外删除,用户可以向后移动到一个时间点,查看丢失的行并把它们重新插入现在的表内恢复。

  (2)恢复Email和声音Email:当用户意外删除了Email或者声音信息时,可以通过移回到固定时间点来恢复删除。

  (3)账号平衡状况:可以查看以前的历史数据。如银行外币管理中用于记录特定时间的汇率。在以前,汇率变更被记录在一个历史表中,现在就可以通过闪回功能进行查询。

  (4)用于趋势分析的决策支持系统:决策支持系统和联机分析应用必须执行一个长时间的事务。使用闪回查询,这些应用可以对历史数据执行分析和建模。例如,特定产品如矿泉水随季节变化需求情况的变化。

  2、回滚段概述

  回滚段用于存放数据修改之前的位置和值,回滚段的头部包含正在使用的该回滚段事务的信息。回滚段的作用如下:

  (1)事务回滚:当事务修改表中数据的时候,该数据修改前的值(即前影像)会存放在回滚段中,当用户回滚事务时,Oracle将会利用回滚段中的数据前影像来将修改的数据恢复到原来的值。

  (2)事务恢复:当事务正在处理的时候,例程失败,回滚段的信息保存在重做日志文件中,Oracle将在下次打开数据库时利用回滚来恢复未提交的数据。

  (3)读一致性:当一个会话正在修改数据时,其它的会话将看不到该会话未提交的修改。而且,当一个语句正在执行时,该语句将看不到从该语句开始执行后的未提交的修改(语句级读一致性)。

  3、Oracle中Delete和Commit操作的流程分析

  (1)删除(Delete)流程

  ·Oracle读Block(数据块)到Buffer Cache(缓冲区)(如果该Block在Buffer中不存在);

  ·在Redo Log Buffer(重做日志缓冲区)中记录Delete操作的细节;

  ·在相应回滚段段头的事物表中创建一个Undo(回滚)条目;

  ·把将要删除的记录创建前镜像,存放到Undo Block(回滚块)中;

  ·在Buffer Cache中的相应数据块上删除记录,并且标记相应的数据块为Dirty(脏)。

  (2)提交(Commit)流程

  ·Oracle产生一个SCN;

  ·在回滚段事物表中标记该事物状态为Commited;

  ·LGWR(日志读写进程) Flush Log Buffer到日志文件;

  ·如果此时数据块仍然在Buffer Cache中,那么SCN将被记录到Block Header上,这被称为快速提交;

  ·如果Dirty Block已经被写回到磁盘,那么下一个访问这个Block的进程将会自回滚段中获取该事物的状态,确认该事物被提交。然后这个进程获得提交SCN并写回到Block Header上,这被称为延迟块清除。

  4、Oracle 9i中闪回查询操作实例

  进行闪回查询必须设置自动回滚段管理,在init.ora设置参数UNDO_MANAGEMENT=AUTO,参数UNDO_RETENTION=n,决定了能往前闪回的最大时间,值越大就需要越多Undo空间。

  例:Oracle 9i的Flashback Query操作。

  (1)创建闪回查询用户

SQL> create user flashtest identified by flashtest;
SQL> grant connect, resource to flashtest;
SQL> grant execute on dbms_flashback to flashtest;
SQL> connect flashtest/flashtest;

  (2)创建测试表,插入测试记录

SQL> create table test(id number(3));
SQL> insert into test values (1);
SQL> insert into test values(2);
SQL> commit;
SQL> create table rec_date(date_scn);

  注意:在执行步骤3或者步骤4之前,等待5分钟。

  (3)删除记录

SQL> execute dbms_flashback.disable;
SQL> insert into rec_date select sysdate from dual;
SQL> commit;
SQL> delete from test where id=1;
SQL> commit;

  通过以上的操作,我们插入了两条记录,并删除了其中一条记录。在以下的操作中,我们将通过flashback query找到删除的记录

  (4)闪回查询

SQL> DECLARE
Restore_scn date;
BEGIN
Select date_scn into restore_scn from rec_date;
Dbms_flashback.enable_at_time (restore_scn);
END;
SQL> select * from test;
ID
1
2


  可以看出,虽然删除记录并提交,但是通过闪回操作,仍能查询到删除前 的两条记录。需要注意Oracle每5分钟记录一次SCN,并将SCN和对应时间的映射进行纪录。如果原来插入的记录到做闪回操作的时间在5分钟之内,用 基于时间的闪回查询可能得不到记录,因为基于时间点的查询实际上是转化为最近的一次SCN,然后从这个SCN开始进行恢复。因此,如果需要精确的查询可以 采用基于SCN的闪回查询,可精确闪回到需要恢复的时间。可以通过DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER语句 获取SCN。

  Oracle 10g的闪回查询概述

  与Oracle 9i相比Oracle 10g的
Flash back有了非常大的改进,从普通的Flashback Query发展到了多种形式,主要表现在如下几方面新特性:

  1、Flashback Database

  Oracle Flashback Database特性允许通过SQL语句Flashback Database语句,让
数据库 前 滚到当前的前一个时间点或者SCN,而不需要做时间点的恢复。闪回数据库可以迅速将数据库回到误操作或人为错误的前一个时间点,如Word中的"撤消"操 作,可以不利用备份就快速的实现基于时间点的恢复。Oracle通过创建新的Flashback Logs(闪回日志),记录数据库的闪回操作。如果希望能闪回数据库,需要设置如下参数:DB_RECOVER_FILE_DEST日志的存放位置, DB_RECOVER_FILE_DEST_SIZE恢复区的大小。在创建数据库的时候,Oracle将自动创建恢复区,但默认是关闭的,需要执行 alter database flashback on命令。

  例:执行Flashback Database命令格式。

SQL>flashback database to time to_timestamp(xxx);
SQL>flashback database to scn xxx

  2、Flashback Table

  Oracle Flashback Table特性允许利用Flashback Table语句,确保闪回到表的前一个时间点。与Oracle 9i中的Flashback Query相似,利用回滚段信息来恢复一个或一些表到以前的一个时间点(一个快照)。要注意的是,Flashback Table不等于Flashback Query,Flashback Query仅仅是查询以前的一个快照点而已,并不改变当前表的状态,而Flashback Table将改变当前表及附属对象一起回到以前的时间点。

  语法:

flashback table tablename to timestamp xxx或
flashback table tablename to scn xxx

  注意:如果需要闪回一个表,需要以下条件:

  ·需要有flashback any table的系统权限或者是该表的flashback对象权限;

  ·需要有该表的select,insert,delete,alter权限;

  ·必须保证该表row movement。

  例:执行将test表闪回到2005年5月7日下午3点。

SQL>flashback table test to timestamp to_timestamp(’2005-05-07 15:00:00’,’yyyy-mm-dd hh24:mi:ss’);

  3、Flashback Drop

  Oracle Flashback Drop特性提供一个类似回收站的功能,用来恢复不小心被删除的表。当删除表时,Oracle 10g并不立刻释放被删除的表所占用的空间,而是将这个被删除的表进行自动重命名(为了避免同类对象名称的重复)并放进回收站中。所谓的回收站类似于
Windows 系统中的回收站,是一个虚拟的容器,用于存放所有被删除的对象,在回收站中被删除的对象将占用创建时的同样的空间。如果这个被删除的表需要进行恢复,就可利用Flashback Drop功能。

  例:进行一个删除表后恢复的简单测试。

  (1)显示回收站信息

SQL>show recyclebin;

  可以看到,回收站中是没有任何结果的,表示没有任何表在回收站中。

  (2)创建一个表,并删除,再次显示回收站信息

SQL>create table test_drop(name varchar2(10));
SQL>drop table test_drop;
SQL>show recyclebin;
ORIGINAL NAME RECYCLEBIN NAME OBJECT TYPE DROP TIME
TEST_DROP BIN$b+XkkO1RS5K10uKo9BfmuA==$0 TABLE 2005-05-07:14:30:47

  (3)对被删除的表进行恢复

SQL>flashback table test_drop to before drop;或
SQL>flashback table "BIN$b+XkkO1RS5K10uKo9BfmuA==$0" to before drop;

  (4)管理回收站

  清除回收站中的单个表:purge table test_drop

  清除整个回收站:purge recyclebin

  清除不同的对象回收站:purge user_recyclebin或purge dba_recyclebin

  (5)确认删除一个表

SQL>drop table test_drop purge;

  如果删除一个表且不放到回收站中不能进行恢复,在drop语句中可以利用purge选项。

  4、Flash Version Query

   Oracle Flashback Version Query特性,利用保存的回滚信息,可以看到特定的表在时间段内的任何修改,如电影的回放一样,可以了解表在该期间的任何变化。Flashback version query一样依赖于AUM,提供了一个查看行改变的功能,能找到所有已经提交了的行的记录,分析出过去时间都执行了什么操作。Flashback version query采用VERSIONS BETWEEN语句来进行查询,常用的方法:

  ·VERSIONS_SCN - 系统改变号

  ·VERSIONS_TIMESTAMP - 时间

   例如:在test表中,时间1插入一条记录,时间2删除了这条记录,对于时间3执行select * from test当然查询不到这条记录,只能看到该表最后的提交记录。这时如果利用Flash Table或者是Flash Query,只能看到过去的某一时间点的一个快照,而利用Flashback Version Query,能够把时间1、时间2的操作给记录下来,并详细的查询出对表进行的任何操作。

SQL>select versions_starttime,versions_endtime, versions_xid,versions_operation,id
from test versions
between timestamp minvalue and maxvalue
order by versions_starttime;

  在上述查询中,列 versions_starttime、versions_endtime、versions_xid、versions_operation是伪列,还 有一些伪列,如versions_startscn和versions_endscn显示了该时刻的系统更改号。列versions_xid显示了更改该 行的事务标识符。

  当然,除了分析以上所有的变更之外,可以根据需要指定时间段,如显示在2005-05-07时间在15:30到16:30之间test表的所有变更。

SQL>select id from test
versions between timestamp to_date(’2005-05-07 15:30:00’,’yyyy-mm-dd hh24:mi:ss’) and to_date(’2005-05-07 16:30:00’,’yyyy-mm-dd hh24:mi:ss’)

  5、Flashback Transaction Query

  Oracle Flashback Transaction Query特性确保检查数据库的任何改变在一个事务级别,可以利用此功能进行诊断
问题 、 性能分析和审计事务。它其实是Flashback Version Query查询的一个扩充,Flashback Version Query说明了可以审计一段时间内表的所有改变,但是也仅仅是能发现问题,对于错误的事务,没有好的处理办法。而Flashback Transaction Query提供了从FLASHBACK_TRANSACTION_QUERY视图中获得事务的历史以及Undo_sql(回滚事务对应的sql语句),也 就是说审计一个事务到底做了什么,甚至可以回滚一个已经提交的事务。

  例:Flashback Transaction Query的操作实例。

  (1)在test表中删除记录,获得事务的标识XID,然后提交。

SQL>delete from test where id=2;
SQL>select xid from v$transaction;
XID
----------------
04001200AE010000
SQL>commit;

  在测试中方便起见,在事务没有提交的时候,获得事务的XID为04001F0035000000。实际情况下,不可能去跟踪每个事务,想要获得已提交事务的XID,就必须通过上面的Flashback Version Query。

  (2)进行Flashback Transaction Query

SQL>select * from FLASHBACK_TRANSACTION_QUERY
where xid=’04001F0035000000’;
UNDO_SQL
insert into "FLASHTEST"."TEST"("ID") values (’2’);

  注意:这个删除语句对应的是1个Insert语句,如果想回滚这个事务,执行这个Insert语句即可。

   可以看到,Flashback Transaction Query主要用于审计一个事务,并可以回滚一个已经提交的事务。如果确定出错的事务是最后一个事务,我们利用Flashback Table或者Flashback Query就可以解决问题。但是,如果执行了一个错误的事务之后,又执行了一系列正确的事务,那么上面的方法就无能为力,利用Flashback Transaction Query可以查看或回滚这个错误的事务。

  结束语

  通过上面的描述,可以看出闪回功能使用户恢复偶然的错误删除更加容易,增强了系统的可用性与读一致性。
posted @ 2006-07-13 10:26 Alex 阅读(1346) | 评论 (0)编辑 收藏

start the oem service:
emctl start dbconsole

then type following addrese in browser:

http://yoursrv:1561/em


it's convient.

It's B/S model ,and it's C/S model at Oracle9i.


posted @ 2006-07-11 16:18 Alex 阅读(259) | 评论 (0)编辑 收藏

key words: cookie 保留历史记录 登陆记录

很多时候用session觉得挺方便的,今天突然发现自己竟然几乎没用过cookie,呵呵,有点意思。正好碰到一个登陆页面,需要用户选择站点类型,觉得每次都让用户选择有点不合理,毕竟一个用户常用的就一个,所以决定用cookie记录下这个站点,下次登陆的时候可以直接显示.

效果如下:
站点cookie

 /**
     * 从cookie里读取指定Name 对应的值
     * 如果没有返回空 null
     * 
@param cookieName
     * 
@param request
     *
@param decode :编码
     * @return  String
     
*/
    
public static String getCookieValue(String cookieName, HttpServletRequest request,String decode) {
        
if(null == cookieName || cookieName.trim().length() ==0return "";
        Cookie cookies[] 
= request.getCookies();
        Cookie sCookie 
= null;
        String sname 
= null;
        String svalue 
= null;
        
if (null != cookies && cookies.length > 0) {
            
for (int i = 0; i < cookies.length; i++) {
                sCookie 
= cookies[i];
                sname 
= sCookie.getName();
                
if (cookieName.equals(sname)) {
                    
try {
                        svalue 
= java.net.URLDecoder.decode(sCookie.getValue(),decode);
                    } 
catch (UnsupportedEncodingException e) {
                        e.printStackTrace();  
                    }
                    
break;
                }
            }
        }
        
return svalue ;
    }

    
/**
     * 设置cookie
     * 
@param cookieName
     * 
@param cookieValue
     * 
@param maxAge
     * 
@param response
     * @paramencode :编码
     */
    
public static void setCookieValue(String cookieName,String cookieValue,
int maxAge,HttpServletResponse response,String encode) {
        
if(null == cookieName || cookieName.trim().length() ==0return ;
        Cookie cookie 
= null;
        
try {
            cookie 
= new Cookie(cookieName, java.net.URLEncoder.encode(cookieValue,encode));
        } 
catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        cookie.setMaxAge(maxAge);
        response.addCookie(cookie);
    }

posted @ 2006-07-05 17:39 Alex 阅读(555) | 评论 (1)编辑 收藏

key words: 切割字符串 切割中文字符 DecimalFormat 格式化字符 科学计数法

一.切割字符串的前几个字符
在首页,有时候会因为table列表里的某个内容比较长而使得页面撑得很难看,一般做法就是截取前几个字符
/**
     * 截取前几个字符串
     * 
@param src  被截取的字符
     * 
@param num  截取的长度
     * 
@param append 附加的字符
     * 
@return String
     
*/
    
public static String splitStr(String src, int num, String append) {
        
if (null == src || num < 0return "";
        
if (src.length() < num) return src;
        
char[] rtnChar = src.toCharArray();
        StringBuffer sb 
= new StringBuffer();
        
for (int i = 0; i < num; i++) {
            sb.append(rtnChar[i]);
        }
        sb.append(append);
        
return sb.toString();
    }

toCharArray会把一个汉字当作一个char(java中一个char两个字节)

二.用DecimalFormat格式化字符
这次用POI读取Excel碰到的一个问题,即,如果Excel里的格式不同,比如同样的20060623,有可能是字符型的格式,也可能是普通数字型的,而且在数字型的时候如果比较长会给你返回科学计数法的格式,如:2.002623E7,而这个不是我希望出现的,所以后来只好碰到这种格式的就自己给转换一下:

if (null != row.getCell((short) i)) {
                    
switch (row.getCell((short) i).getCellType()) {
                        
case HSSFCell.CELL_TYPE_FORMULA :
                            strExcelLine[i] 
= "FORMULA ";
                            
break;
                        
case HSSFCell.CELL_TYPE_NUMERIC :
                            strExcelLine[i] 
= String.valueOf(row.getCell((short) i).getNumericCellValue());
                            
break;
                        
case HSSFCell.CELL_TYPE_STRING :
                            strExcelLine[i] 
= row.getCell((short) i).getStringCellValue();
                            
break;
                        
case HSSFCell.CELL_TYPE_BLANK :
                            strExcelLine[i] 
= "";
                            
break;
                        
default :
                            strExcelLine[i] 
= "";
                            
break;
                    }
                    
//如果读取的是科学计数法的格式,则转换为普通格式
                    
//added by Alex at 20060626
                    if(null != strExcelLine[i] &&
                            strExcelLine[i].indexOf(
"."!= -1 &&
                            strExcelLine[i].indexOf(
"E"!= -1){
                        DecimalFormat df 
= new DecimalFormat();
                        strExcelLine[i] 
= df.parse(strExcelLine[i]).toString();
                    }
                }



posted @ 2006-07-03 18:31 Alex 阅读(927) | 评论 (0)编辑 收藏

原文地址 :这里

 怎样成为优秀的软件模型设计者?

                    来自:blog   雪茶技术

我们期待自己成为一个优秀的软件模型设计者,但是,要怎样做,又从哪里开始呢?

将下列原则应用到你的软件工程中,你会获得立杆见影的成果。

1. 人远比技术重要

你开发软件是为了供别人使用,没有人使用的软件只是没有意义的数据的集合而已。许多在软件方面很有成就的行家在他们事业的初期却表现平平,因为他们 那时侯将主要精力都集中在技术上。显然,构件(components),EJB(Enterprise Java Beans)和代理(agent)是很有趣的东西。但是对于用户来说,如果你设计的软件很难使用或者不能满足他们的需求,后台用再好的技术也于事无补。多 花点时间到软件需求和设计一个使用户能很容易理解的界面上。

2. 理解你要实现的东西

好的软件设计人员把大多数时间花费在建立系统模型上,偶尔写一些源代码,但那只不过是为了验证设计过程中所遇到的问题。这将使他们的设计方案更加可行。

3. 谦虚是必须的品格

你不可能知道一切,你甚至要很努力才能获得足够用的知识。软件开发是一项复杂而艰巨的工作,因为软件开发所用到的工具和技术是在不断更新的。而且, 一个人也不可能了解软件开发的所有过程。在日常生活中你每天接触到的新鲜事物可能不会太多。但是对于从事软件开发的人来说,每天可以学习很多新东西(如果 愿意的话)。

4. 需求就是需求

如果你没有任何需求,你就不要动手开发任何软件。成功的软件取决于时间(在用户要求的时间内完成)、预算和是否满足用户的需求。如果你不能确切知道用户需要的是什么,或者软件的需求定义,那么你的工程注定会失败。

5. 需求其实很少改变,改变的是你对需求的理解

Object ToolSmiths公司(www.objecttoolsmiths.com) 的Doug Smith常喜欢说:“分析是一门科学,设计是一门艺术”。他的意思是说在众多的“正确”分析模型中只存在一个最“正确”分析模型可以完全满足解决某个具 体问题的需要(我理解的意思是需求分析需要一丝不苟、精确的完成,而设计的时候反而可以发挥创造力和想象力 - 译者注)。

如果需求经常改动,很可能是你没有作好需求分析,并不是需求真的改变了。

你可以抱怨用户不能告诉你他们想得到什么,但是不要忘记,收集需求信息是你工作。

你可以说是新来的开发人员把事情搞得一团糟,但是,你应该确定在工程的第一天就告诉他们应该做什么和怎样去做。

如果你觉得公司不让你与用户充分接触,那只能说明公司的管理层并不是真正支持你的项目。

你可以抱怨公司有关软件工程的管理制度不合理,但你必须了解大多同行公司是怎么做的。

你可以借口说你们的竞争对手的成功是因为他们有了一个新的理念,但是为什么你没先想到呢?

需求真正改变的情况很少,但是没有做好需求分析工作的理由却很多。

6. 经常阅读

在这个每日都在发生变化的产业中,你不可能在已取得的成就上陶醉太久。

每个月至少读2、3本专业杂志或者1本专业书籍。保持不落伍需要付出很多的时间和金钱,但会使你成为一个很有实力的竞争者。

7. 降低软件模块间的耦合度

高耦合度的系统是很难维护的。一处的修改引起另一处甚至更多处的变动。

你可以通过以下方法降低程序的耦合度:隐藏实现细节,强制构件接口定义,不使用公用数据结构,不让应用程序直接操作数据库(我的经验法则是:当应用程序员在写SQL代码的时候,你的程序的耦合度就已经很高了)。

耦合度低的软件可以很容易被重用、维护和扩充。

8. 提高软件的内聚性

如果一个软件的模块只实现一个功能,那么该模块具有高内聚性。高内聚性的软件更容易维护和改进。

判断一个模块是否有高的内聚性,看一看你是否能够用一个简单的句子描述它的功能就行了。如果你用了一段话或者你需要使用类似“和”、“或”等连词,则说明你需要将该模块细化。[注:only one]

只有高内聚性的模块才可能被重用。

9. 考虑软件的移植性

移植是软件开发中一项具体而又实际的工作,不要相信某些软件工具的广告宣传(比如java 的宣传口号write once run many ? 译者注)。

即使仅仅对软件进行常规升级,也要把这看得和向另一个操作系统或数据库移植一样重要。

记得从16位Windows移植到32位windows的“乐趣”吗 ?当你使用了某个操作系统的特性,如它的进程间通信(IPC)策略,或用某数据库专有语言写了存储过程。你的软件和那个特定的产品结合度就已经很高了。

好的软件设计者把那些特有的实现细节打包隐藏起来,所以,当那些特性该变的时候,你的仅仅需要更新那个包就可以了。

10. 接受变化

这是一句老话了:唯一不变的只有变化。

你应该将所有系统将可能发生的变化以及潜在需求记录下来,以便将来能够实现(参见“Architecting for Change”,Thinking Objectively, May 1999)

通过在建模期间考虑这些假设的情况,你就有可能开发出足够强壮且容易维护的软件。设计强壮的软件是你最基本的目标。

11. 不要低估对软件规模的需求

Internet 带给我们的最大的教训是你必须在软件开发的最初阶段就考虑软件规模的可扩充性。

今天只有100人的部门使用的应用程序,明天可能会被有好几万人的组织使用,下月,通过因特网可能会有几百万人使用它。

在软件设计的初期,根据在用例模型中定义的必须支持的基本事务处理,确定软件的基本功能。然后,在建造系统的时候再逐步加入比较常用的功能。

在设计的开始考虑软件的规模需求,避免在用户群突然增大的情况下,重写软件。

12. 性能仅仅是很多设计因素之一

关注软件设计中的一个重要因素--性能,这好象也是用户最关心的事情。一个性能不佳的软件将不可避免被重写。

但是你的设计还必须具有可靠性,可用性,便携性和可扩展性。你应该在工程开始就应该定义并区分好这些因素,以便在工作中恰当使用。性能可以是,也可以不是优先级最高的因素,我的观点是,给每个设计因素应有的考虑。

13. 管理接口

“UML User Guide”(Grady Booch,Ivar Jacobson和Jim Rumbaugh ,Addison Wesley, 1999)中指出,你应该在开发阶段的早期就定义软件模块之间的接口。

这有助于你的开发人员全面理解软件的设计结构并取得一致意见,让各模块开发小组相对独立的工作。一旦模块的接口确定之后,模块怎样实现就不是很重要了。

从根本上说,如果你不能够定义你的模块“从外部看上去会是什么样子”,你肯定也不清楚模块内要实现什么。

14. 走近路需要更长的时间

在软件开发中没有捷径可以走。

缩短你的在需求分析上花的时间,结果只能是开发出来的软件不能满足用户的需求,必须被重写。

在软件建模上每节省一周,在将来的编码阶段可能会多花几周时间,因为你在全面思考之前就动手写程序。

你为了节省一天的测试时间而漏掉了一个bug,在将来的维护阶段,可能需要花几周甚至几个月的时间去修复。与其如此,还不如重新安排一下项目计划。

避免走捷径,只做一次但要做对(do it once by doing it right)。

15. 别信赖任何人

产品和服务销售公司不是你的朋友,你的大部分员工和高层管理人员也不是。

大部分产品供应商希望把你牢牢绑在他们的产品上,可能是操作系统,数据库或者某个开发工具。

大部分的顾问和承包商只关心你的钱并不是你的工程(停止向他们付款,看一看他们会在周围呆多长时间)。

大部分程序员认为他们自己比其他人更优秀,他们可能抛弃你设计的模型而用自己认为更好的

只有良好的沟通才能解决这些问题

要明确的是,不要只依靠一家产品或服务提供商,即使你的公司(或组织)已经在建模、文档和过程等方面向那个公司投入了很多钱。

16. 证明你的设计在实践中可行

在设计的时候应当先建立一个技术原型, 或者称为“端到端”原型。以证明你的设计是能够工作的。

你应该在开发工作的早期做这些事情,因为,如果软件的设计方案是不可行的,在编码实现阶段无论采取什么措施都于事无补。技术原型将证明你的设计的可行性,从而,你的设计将更容易获得支持。

17. 应用已知的模式

目前,我们有大量现成的分析和设计模式以及问题的解决方案可以使用。

一般来说,好的模型设计和开发人员,都会避免重新设计已经成熟的并被广泛应用的东西。
http://www.ambysoft.com/processPatternsPage.html 收藏了许多开发模式的信息。

18. 研究每个模型的长处和弱点

目前有很多种类的模型可以使用,如下图所示。用例捕获的是系统行为需求,数据模型则描述支持一个系统运行所需要的数据构成。你可能会试图在用例中加 入实际数据描述,但是,这对开发者不是非常有用。同样,数据模型对描述软件需求来说是无用的。每个模型在你建模过程中有其相应的位置,但是,你需要明白在 什么地方,什么时候使用它们。

19. 在现有任务中应用多个模型

当你收集需求的时候,考虑使用用例模型,用户界面模型和领域级的类模型。

当你设计软件的时候,应该考虑制作类模型,顺序图、状态图、协作图和最终的软件实际物理模型。

程序设计人员应该慢慢意识到,仅仅使用一个模型而实现的软件要么不能够很好地满足用户的需求,要么很难扩展。

20. 教育你的听众

你花了很大力气建立一个很成熟的系统模型,而你的听众却不能理解它们,甚至更糟-连为什么要先建立模型都不知道。那么你的工作是毫无意义的。

教给你开发人员基本的建模知识;否则,他们会只看看你画的漂亮图表,然后继续编写不规范的程序。

另外, 你还需要告诉你的用户一些需求建模的基础知识。给他们解释你的用例(uses case)和用户界面模型,以使他们能够明白你要表达地东西。当每个人都能使用一个通用的设计语言的时候(比如UML-译者注),你的团队才能实现真正的合作。

21. 带工具的傻瓜还是傻瓜

你给我CAD/CAM工具,请我设计一座桥。但是,如果那座桥建成的话,我肯定不想当第一个从桥上过的人,因为我对建筑一窍不通。

使用一个很优秀的CASE工具并不能使你成为一个建模专家,只能使你成为一个优秀CASE工具的使用者。成为一个优秀的建模专家需要多年的积累,不 会是一周针对某个价值几千美元工具的培训。一个优秀的CASE工具是很重要,但你必须学习使用它,并能够使用它设计它支持的模型。

22. 理解完整的过程

好的设计人员应该理解整个软件过程,尽管他们可能不是精通全部实现细节。

软件开发是一个很复杂的过程,还记得《object-oriented software process》第36页的内容吗?除了编程、建模、测试等你擅长工作外,还有很多工作要做。

好的设计者需要考虑全局。必须从长远考虑如何使软件满足用户需要,如何提供维护和技术支持等。

23. 常做测试,早做测试

如果测试对你的软件来说是无所谓的,那么你的软件多半也没什么必要被开发出来。

建立一个技术原型供技术评审使用,以检验你的软件模型。

在软件生命周期中,越晚发现的错误越难修改,修改成本越昂贵。尽可能早的做测试是很值得的。

24. 把你的工作归档

不值得归档的工作往往也不值得做。归档你的设想,以及根据设想做出的决定;归档软件模型中很重要但不很明显的部分。 给每个模型一些概要描述以使别人很快明白模型所表达的内容

25. 技术会变,基本原理不会

如果有人说“使用某种开发语言、某个工具或某某技术,我们就不需要再做需求分析,建模,编码或测试”。不要相信,这只说明他还缺乏经验。抛开技术和 人的因素,实际上软件开发的基本原理自20世纪70年代以来就没有改变过。你必须还定义需求,建模,编码,测试,配置,面对风险,发布产品,管理工作人员 等等。

软件建模技术是需要多年的实际工作才能完全掌握的。好在你可以从我的建议开始,完善你们自己的软件开发经验。

以鸡汤开始,加入自己的蔬菜。然后,开始享受你自己的丰盛晚餐吧。

posted @ 2006-06-20 14:28 Alex 阅读(402) | 评论 (1)编辑 收藏

mark下资源,有空再来慢慢研究。

Prototype.js--Javascript编写者的小军刀


posted @ 2006-06-18 22:22 Alex 阅读(314) | 评论 (0)编辑 收藏

key words: bash编程

转自 这里

另一篇 详解Bash命令行处理

bash编程教学实例

bash编程

--------------------------------------------------------------------------------
時間:2004/03/02    來源:不详

  Shell Script(bash)简介

  众所皆知地,UNIX上以小工具著名,利用许多简单的小工具,来完成原本需要大量软体开发的工作,这一点特色,使得UNIX成为许多人心目中理想的系统平台。 
  在众多的小工具中,Shell Script算得上是最基本、最强大、运用最广泛的一个。它运用围之广,不但从系统启动、程式编译、定期作业、上网连线,甚至安装整个Linux系统,都可以用它来完成。 

  因为Shell Script是利用您平日在使用的一些指令,将之组合起来,成为一个"程式"。如果您平日某些序列的指令下得特别频繁,便可以将这些指令组合起来,成为另 一个新的指令。这样,不但可以简化并加速操作速度,甚至还可以干脆自动定期执行,大大简化系统管理工作。 

  *************************
  Bash(GNU Bourne-Again SHell)是许多Linux平台的内定Shell,事实上,还有许多传统UNIX上用的Shell,像tcsh、csh、ash、bsh、ksh等等, Shell Script大致都类同,当您学会一种Shell以后,其它的Shell会很快就上手,大多数的时候,一个Shell Script通常可以在很多种Shell上使用。 
  这里我介绍您bash的使用方法。事实上,当您"man bash"时,就可以看到bash的说明书,不过对许多人来说,这份说明书犹如"无字天书"一样难懂。这份文件,主要资料来源为"man bash",我加上一些实际日常的应用例来说明。希望这样能让那些始终不得其门而入的人们,多多少少能有点概念。 


  教学例子

  "Hello world" Shell Script 
  照传统程式教学例,这一节介绍Shell Script的"Hello World"如何撰写。 

  *************************

  #!/bin/sh 
  # Filename : hello 
  echo "Hello world!" 

  *************************

  大家应该会注意到第一行的"#!/bin/sh"。在UNIX下,所有的可执行Script,不管是那一种语言,其开头都是"#!",例如Perl是 "#!/usr/bin/perl",tcl/tk是"#!/usr/bin/wish",看您要执行的Script程式位置在那里。您也可以用"#! /bin/bash"、"#!/bin/tcsh"等等,来指定使用特定的Shell。 
  echo是个bash的内建指令。 

  *************************

  接下来,执行hello这个script: 
  要执行一个Script的方式有很多种。 

  *************************

  第一种 : 将hello这个档案的权限设定为可执行。 
  [foxman@foxman bash]# chmod 755 hello 
  执行 
  [foxman@foxman bash]# ./hello 
  hello world 

  *************************

  第二种 : 使用bash内建指令"source"或"."。 
  [foxman@foxman bash]# source hello 
  hello world 
  或 
  [foxman@foxman bash]# . hello 
  hello world 

  *************************

  第三种 : 直接使用sh/bash/tcsh指令来执行。 
  [foxman@foxman bash]# sh hello 
  hello world 
  或 
  [foxman@foxman bash]# bash hello 
  hello world 

  *************************

  Bash执行选项 

  *************************

  -c string : 读取string来当命令。 
  -i : 互动介面。 
  -s : 由stdin读取命令。 
  - : 取消往后选项的读取。 
  -norc : 不要读~/.bashrc来执行。 
  -noprofile : 不要读/etc/profile、~/.bash_profile、~/.bash_login、~/.profile等等来执行。 
  -rcfile filename : 执行filename,而非~/.bashrc 
  -version : 显示版本。 
  -quiet : 启动时不要哩唆。 
  -login : 确保bash是个login shell。 
  -nobraceexpansion : 不要用curly brace expansion({}符号展开)。 
  -nolineediting : 不用readline来读取命令列。 
  -posix : 改采Posix 1003.2标准。 


  用于自动备份的Shell Script


  一个用于自动备份的Shell Script
  我们先前提到,可利用Shell Script搭配crond来作定期的工作。要作定期性的工作,在UNIX上,就是与crond的搭配运用。 

  *************************

  首先我们先来研究如何对系统进行备份。 
  要对系统进行备份,不外乎便是利用一些压缩工具。在许多UNIX系统上,tar及gzip是de facto的资料交换标准。我们经常可以看见一些tar.gz或tgz档,这些档案,被称为tarball。当然了,您也可以用bzip2、zip等等压 缩工具来进行压缩,不必限定于gzip。但tar配合gzip是最普遍的,也是最方便的方式。 

  要将我们想要的资料压缩起来,进行备份,可以结合tar及gzip一起进行。方式有很多种,最常用的指令是以下这一种: 

  tar -c file/dir ... | gzip -9 > xxxx.tar.gz 

  您也可以分开来做: 

  tar -r file/dir ... -f xxxx.tar 
  gzip -9 xxxx.tar 

  或 

  tar -r file/dir ... -f xxxx.tar 
  gzip -9 < xxxx.tar > xxxx.tar.gz 

  *************************

  在解过Linux下档案备份的基本知识后,我们来写一个将档案备份的Script。 
  #!/bin/sh 
  # Filename : backup 

  DIRS="/etc /var /your_directories_or_files" 
  BACKUP="/tmp/backup.tgz" 

  tar -c $DIRS | gzip -9 > $BACKUP 

  其中DIRS放的是您要备份的档案及目录,BACKUP是您的备份档。可不要将/tmp放进DIRS中,那样做,您是在做备份的备份,可能将您的硬碟塞爆。 


  *************************

  接下来测试 
  [foxman@foxman bash]# chmod 755 backup 
  [foxman@foxman bash]# ./backup 

  执行完成后在/tmp就会有一个backup.tgz,里面储存了您重要的资料。您可用 

  gzip -dc /tmp/backup.tgz | tar -vt 
  或 
  tar vtfz /tmp/backup.tgz 

  来看看里面的档案列表。 

  要解开时,可用以下指令来完成复原: 

  gzip -dc /tmp/backup.tgz | tar -xv 
  或 
  tar xvfz /tmp/backup.tgz 

  备份通常是仅备份系统通常最重要的部份,/etc可说是不可缺少的一部份。另外,看您系统中有那些重要的资料需要备份。通常来说,您没有必要备份 /bin、/sbin、/usr/bin、/usr/sbin、/usr/X11R6/bin等等这些执行档目录。只要备份您重要的档案即可,别把整个硬 碟备份,那是蛮呆的动作。 

  *************************

  如果您有许多台机器,可利用其中一台任务较轻的内部网路主机,做为主要备份主机。将所有机器都自动执行备份,然后利用NFS/Coda/Samba等网路档案系统,将备份的资料放到该备份机器中,该机器则定时收取备份资料,然后您再由该机器中进行一次备份。 
  这里是整个系统备份方案的图示。 

  在您进行之前,先解一下,系统中那些是要备份的,那些是不需要的。 

  *************************

  新的backup
  #!/bin/sh 
  HOSTNAME=`hostname` 
  DIRS="/etc /var /your_important_directory" 
  BACKUP="/tmp/$HOSTNAME.tgz" 
  NFS="/mnt/nfs" 

  tar -c $DIRS | gzip -9 > $BACKUP 
  mv -f $BACKUP $NFS 


  *************************

  备份主机内的Script : collect_backup
  #!/bin/sh 
  NFS="/mnt/nfs" 
  BACKUP="/backup" 

  mv -f $NFS/*.tgz $BACKUP 


  在此,您不能够将所有备份都直接放在/mnt/nfs,这是危险的。万一任一台机器不小心将/mnt/nfs所有内容删除,那么备份就会消失。因此,您需要将/mnt/nfs移到一个只有该备份主机可存取的目录中。 


  *************************

  当这些个别的Script都测试好以后,接下来我们将他们放到crontab里面。找到您的crontab,它的位置可能在/var/spool/cron/crontabs/root、/etc/crontab、/var/cron/tabs/root。 
  在crontab中选择以下之一加入(看您定期的时间): 

  Slackware : /var/spool/cron/crontabs/root
  01 * * * * /full_backup_script_path/backup 1> /dev/null 2> /dev/null # 每小时(太过火一点) 
  30 16 * * * /full_backup_script_path/backup 1> /dev/null 2> /dev/null # 每日16:30,下班前备份 
  30 16 * * 0 /full_backup_script_path/backup 1> /dev/null 2> /dev/null # 每周一16:30 
  0 5 1 * * /full_backup_script_path/backup 1> /dev/null 2> /dev/null # 每月一号5:0 
  RedHat/Debian : /etc/crontab
  RedHat可直接将backup放入/etc/cron.hourly, /etc/cron.daily, /etc/cron.weekly, /etc/cron.monthly。或采用如上加入/etc/crontab的方式: 
  有关crontab的用法,可查"man 5 crontab",在此不详述。 

  备份主机的设定类同。 

  注意: 所有机器不要同时进行备份,否则网路会大塞车。备份主机收取备份的时间要设为最后,否则会收不到备份资料。您可以在实作后,将时间间隔调整一下。 


  *************************

  看看,两个小小不到三行的Shell Script,配合cron这个定时工具。可以让原本需要耗时多个小时的人工备份工作,简化到不到十分钟。善用您的想像力,多加一点变化,可你让您的生活变得轻松异常,快乐悠哉。


  档案系统检查

  系统安全一向是大多数电脑用户关心的事,在UNIX系统中,最重视的事,即系统中有没有"木马"(Trojan horse)。不管Trojan horse如何放进来的,有一点始终会不变,即被放置木马的档案,其档案日期一定会被改变,甚至会有其它的状态改变。此外,许多状况下,系统会多出一些不 知名的档案。因此,平日检查整个档案系统的状态是否有被改变,将所有状态有改变的档案,以及目前有那些程式正在执行,自动报告给系统管理员,是个避免坐上 "木马"的良方。 

  *************************

  #!/bin/sh 
  # Filename : whatever_you_name_it 
  DIRS="/etc /home /bin /sbin /usr/bin /usr/sbin /usr/local /var /your_directory" 
  ADMIN="email@your.domain.com" 
  FROM="admin@your.domain.com" 
  # 写入Sendmail的标头 
  echo "Subject: $HOSTNAME filesystem check" > /tmp/today.mail 
  echo "From: $FROM" >> /tmp/today.mail 
  echo "To: $ADMIN" >> /tmp/today.mail 
  echo "This is filesystem report comes from $HOSTNAME" >> /tmp/today.mail 
  # 报告目前正在执行的程式 
  ps axf >> /tmp/today.mail 
  # 档案系统检查 
  echo "File System Check" >> /tmp/today.mail 
  ls -alR $DIRS | gzip -9 > /tmp/today.gz 
  zdiff /tmp/today.gz /tmp/yesterday.gz >> /tmp/today.mail 
  mv -f /tmp/today.gz /tmp/yesterday.gz 
  # 寄出信件 
  sendmail -t < /tmp/today.mail 

  然后把它放到一个不显眼的地方去,让别人找不到。 

  把它加入crontab中。 

  30 7 * * * /full_check_script_path/whatever_you_name_it 1> /dev/null 2> /dev/null #上班前检查 

  有些档案是固定会更动的,像/var/log/messages、/var/log/syslog、/dev/ttyX等等,不要太大惊小怪。


  控制圈for

  演示了几个简单的Shell Script,相信您应该对Shell Script有点概念了。现在我们开始来仔细研究一些较高等的Shell Script写作。一些进一步的说明,例如"$"、">"、"<"、">>"、"1>"、"2>"符号的使用,会在 稍后解释。 

  *************************

  for name [ in word; ] do list ; done
  控制圈。 
  word是一序列的字,for会将word中的个别字展开,然后设定到name上面。list是一序列的工作。如果[in word;]省略掉,那么name将会被设定为Script后面所加的参数。 


  *************************

  例一: 
  #!/bin/sh 

  for i in a b c d e f ; do 
  echo $i 
  done 

  它将会显示出a到f。 


  *************************

  例二: 另一种用法,A-Z
  #!/bin/sh 
  WORD="a b c d e f g h i j l m n o p q r s t u v w x y z" 

  for i in $WORD ; do 
  echo $i 
  done 

  这个Script将会显示a到z。 


  *************************

  例三 : 修改副档名
  如果您有许多的.txt档想要改名成.doc档,您不需要一个一个来。 
  #!/bin/sh 

  FILES=`ls /txt/*.txt` 

  for txt in $FILES ; do 
  doc=`echo $txt | sed "s/.txt/.doc/"` 
  mv $txt $doc 
  done 

  这样可以将*.txt档修改成*.doc档。 


  *************************

  例四 : meow
  #!/bin/sh 
  # Filename : meow 
  for i ; do 
  cat $i 
  done 

  当您输入"meow file1 file2 ..."时,其作用就跟"cat file1 file2 ..."一样。 


  *************************

  例五 : listbin 
  #!/bin/sh 
  # Filename : listbin 

  for i in /bin/* ; do 
  echo $i 
  done 

  当您输入"listbin"时,其作用就跟"ls /bin/*"一样。 


  *************************

  例六 : /etc/rc.d/rc 
  拿一个实际的例来说,Red Hat的/etc/rc.d/rc的启动程式中的一个片断。 

  for i in /etc/rc.d/rc$runlevel.d/S*; do 
  # Check if the script is there. 
  [ ! -f $i ] && continue 

  # Check if the subsystem is already up. 
  subsys=${i#/etc/rc.d/rc$runlevel.d/S??} 
  [ -f /var/lock/subsys/$subsys ] || \ 
  [ -f /var/lock/subsys/${subsys}.init ] && continue 

  # Bring the subsystem up. 
  $i start 
  done 

  这个例中,它找出/etc/rc.d/rcX.d/S*所有档案,检查它是否存在,然后一一执行。 


  流程控制case

  case word in [ pattern [ | pattern ] ... ) list ;; ] ... esac
  case/esac的标准用法大致如下: 
case $arg in 
  pattern | sample) # arg in pattern or sample 
  ;; 
  pattern1) # arg in pattern1 
  ;; 
  *) #default 
  ;; 
esac 
  arg是您所引入的参数,如果arg内容符合pattern项目的话,那么便会执行pattern以下的程式码,而该段程式码则以两个分号";;"做结尾。 

  可以注意到"case"及"esac"是对称的,如果记不起来的话,把"case"颠倒过来即可。 


*************************

  例一 : paranoia
#!/bin/sh 
case $1 in 
    start | begin) 
     echo "start something" 
    ;; 
    stop | end) 
     echo "stop something" 
    ;; 
    *) 
     echo "Ignorant" 
    ;; 
esac 

  执行
  [foxman@foxman bash]# chmod 755 paranoia 
  [foxman@foxman bash]# ./paranoia 
  Ignorant 
  [foxman@foxman bash]# ./paranoia start 
  start something 
  [foxman@foxman bash]# ./paranoia begin 
  start something 
  [foxman@foxman bash]# ./paranoia stop 
  stop something 
  [foxman@foxman bash]# ./paranoia end 
  stop something 

*************************

  例二 : inetpanel
  许多的daemon都会附上一个管理用的Shell Script,像BIND就附上ndc,Apache就附上apachectl。这些管理程式都是用shell script来写的,以下示一个管理inetd的shell script。 
#!/bin/sh 

case $1 in 
  start | begin | commence) 
    /usr/sbin/inetd 
  ;; 
  stop | end | destroy) 
    killall inetd 
  ;; 
  restart | again) 
    killall -HUP inetd 
  ;; 
  *) 
    echo "usage: inetpanel [start | begin | commence | stop | end | destory | restart | again]" 
  ;; 
esac 


*************************

  例三 : 判断系统
  有时候,您所写的Script可能会跨越好几种平台,如Linux、FreeBSD、Solaris等等,而各平台之间,多多少少都有不同之处,有时候需要判断目前正在那一种平台上执行。此时,我们可以利用uname来找出系统资讯。 
#!/bin/sh 

SYSTEM=`uname -s` 

case $SYSTEM in 
  Linux) 
    echo "My system is Linux" 
    echo "Do Linux stuff here..." 
  ;; 
  FreeBSD) 
    echo "My system is FreeBSD" 
    echo "Do FreeBSD stuff here..." 
  ;; 
  *) 
    echo "Unknown system : $SYSTEM" 
    echo "I don't what to do..." 
  ;; 
esac 


  流程控制select

  select name [ in word; ] do list ; done
  select顾名思义就是在word中选择一项。与for相同,如果[in word;]省略,将会使用Script后面所加的参数。 
  例:
#!/bin/sh 
WORD="a b c" 

select i in $WORD ; do 
 case $i in 
  a) 
   echo "I am A" 
  ;; 
  b) 
   echo "I am B" 
  ;; 
  c) 
   echo "I am C" 
  ;; 
  *) 
   break; 
  ;; 
 esac 
done 

  执行结果
  [foxman@foxman bash]# ./select_demo 
1) a 
2) b 
3) c 
#? 1 
I am A 
1) a 
2) b 
3) c 
#? 2 
I am B 
1) a 
2) b 
3) c 
#? 3 
I am C 
1) a 
2) b 
3) c 
#? 4 


  返回状态Exit

  在继续下去之前,我们必须要切入另一个话题,即返回状态值 - Exit Status。因为if/while/until都迁涉到了使用Exit Status来控制程式流程的问题。 

  *************************

  许多人都知道,在许多语言中(C/C++/Perl....),都有一个exit的函数,甚至连Bash自己都有个exit的内建命令。而exit后面所带的数字,便是返回状态值 - Exit Status。 
  返回状态值可以使得程式与程式之间,利用Shell script来结合的可能性大增,利用小程式,透过Shell script,来完成很杂的工作。 

  在shell中,返回值为零表示成功(True),非零值为失败(False)。 


  *************************

  举例来说,以下这个两个小程式yes/no分别会返回0/1(成功/失败): 
  /* yes.c */ 
  void main(void) { exit(0); } 
  /* no.c */ 
  void main(void) { exit(1); } 
  那么以下这个"YES"的shell script便会显示"YES"。 
  #!/bin/sh 
  # YES 
  if yes ; then 
  echo "YES" 
  fi 
  而"NO"不会显示任何东西。 
  #!/bin/sh 
  # NO 
  if no ; then 
  echo "YES" 
  fi 

  *************************

  test express 
  [ express ] 
  在Shell script中,test express/[ express ]这个语法被大量地使用,它是个非常实用的指令。由于它的返回值即Exit Status,经常被运用在if/while/until的场合中。而在后面,我们也会大量运用到,在进入介绍if/while/until之前,有必要 先解一下。 

  其返回值为0(True)或1(False),要看表述(express)的结果为何。 

  express格式 

  -b file : 当档案存在并且属性是Block special(通常是/dev/xxx)时,返回True。 
  -c file : 当档案存在并且属性是character special(通常是/dev/xxx)时,返回True。 
  -d file : 当档案存在并且属性是目录时,返回True。 
  -e file : 当档案存在时,返回True。 
  -f file : 当档案存在并且是正常档案时,返回True。 
  -g file : 当档案存在并且是set-group-id时,返回True。 
  -k file : 当档案存在并且有"sticky" bit被设定时,返回True。 
  -L file : 当档案存在并且是symbolic link时,返回True。 
  -p file : 当档案存在并且是name pipe时,返回True。 
  -r file : 当档案存在并且可读取时,返回True。 
  -s file : 当档案存在并且档案大小大于零时,返回True。 
  -S file : 当档案存在并且是socket时,返回True。 
  -t fd : 当fd被开启为terminal时,返回True。 
  -u file : 当档案存在并且set-user-id bit被设定时,返回True。 
  -w file : 当档案存在并且可写入时,返回True。 
  -x file : 当档案存在并且可执行时,返回True。 
  -O file : 当档案存在并且是被执行的user id所拥有时,返回True。 
  -G file : 当档案存在并且是被执行的group id所拥有时,返回True。 
  file1 -nt file2 : 当file1比file2新时(根据修改时间),返回True。 
  file1 -ot file2 : 当file1比file2旧时(根据修改时间),返回True。 
  file1 -ef file2 : 当file1与file2有相同的device及inode number时,返回True。 
  -z string : 当string的长度为零时,返回True。 
  -n string : 当string的长度不为零时,返回True。 
  string1 = string2 : string1与string2相等时,返回True。 
  string1 != string2 : string1与string2不相等时,返回True。 
  ! express : express为False时,返回True。 
  expr1 -a expr2 : expr1及expr2为True。 
  expr1 -o expr2 : expr1或expr2其中之一为True。 
  arg1 OP arg2 : OP是-eq[equal]、-ne[not-equal]、-lt[less-than]、-le[less-than-or-equal]、-gt [greater-than]、-ge[greater-than-or-equal]的其中之一。 



  *************************

  在Bash中,当错误发生在致命信号时,bash会返回128+signal number做为返回值。如果找不到命令,将会返回127。如果命令找到了,但该命令是不可执行的,将返回126。除此以外,Bash本身会返回最后一个 指令的返回值。若是执行中发生错误,将会返回一个非零的值。 
  Fatal Signal : 128 + signo 
  Can't not find command : 127 
  Can't not execute : 126 
  Shell script successfully executed : return the last command exit status 
  Fatal during execution : return non-zero

  流程控制if

  if list then list [ elif list then list ] ... [ else list ] fi
  几种可能的写法 

*************************

第一种 
if list then 
 do something here 
fi 
当list表述返回值为True(0)时,将会执行"do something here"。 

例一 : 当我们要执行一个命令或程式之前,有时候需要检查该命令是否存在,然后才执行。 
if [ -x /sbin/quotaon ] ; then 
  echo "Turning on Quota for root filesystem" 
  /sbin/quotaon / 
fi 

例二 : 当我们将某个档案做为设定档时,可先检查是否存在,然后将该档案设定值载入。 
# Filename : /etc/ppp/settings 
PHONE=1-800-COLLECT 

#!/bin/sh 
# Filename : phonebill 
if [ -f /etc/ppp/settings ] ; then 
  source /etc/ppp/settings 
  echo $PHONE 
fi 
执行 
[foxman@foxman ppp]# ./phonebill 
1-800-COLLECT 


*************************

第二种 
if list then 
 do something here 
else 
 do something else here 
fi 
例三 : Hostname 
#!/bin/sh 
if [ -f /etc/HOSTNAME ] ; then 
  HOSTNAME=`cat /etc/HOSTNAME` 
else 
  HOSTNAME=localhost 
fi 


*************************

第三种 
if list then 
 do something here 
elif list then 
 do another thing here 
fi 
例四 : 如果某个设定档允许有好几个位置的话,例如crontab,可利用if then elif fi来找寻。 
#!/bin/sh 

if [ -f /etc/crontab ] ; then 
  CRONTAB="/etc/crontab" 
elif [ -f /var/spool/cron/crontabs/root ] ; then 
  CRONTAB="/var/spool/cron/crontabs/root" 
elif [ -f /var/cron/tabs/root ] ; then 
  CRONTAB="/var/cron/tabs/root" 
fi 
export CRONTAB 


*************************

第四种 
if list then 
 do something here 
elif list then 
 do another thing here 
else 
 do something else here 
fi 
例五 : 我们可利用uname来判断目前系统,并分别做各系统状况不同的事。 
#!/bin/sh 

SYSTEM=`uname -s` 

if [ $SYSTEM = "Linux" ] ; then 
 echo "Linux" 
elif [ $SYSTEM = "FreeBSD" ] ; then 
 echo "FreeBSD" 
elif [ $SYSTEM = "Solaris" ] ; then 
 echo "Solaris" 
else 
 echo "What?" 
fi 

控制圈while/until

while list do list done
当list为True时,该圈会不停地执行。 
例一 : 无限回圈写法 
#!/bin/sh 

while : ; do 
 echo "do something forever here" 
 sleep 5 
done 

例二 : 强迫把pppd杀掉。 
#!/bin/sh 

while [ -f /var/run/ppp0.pid ] ; do 
  killall pppd 
done 


*************************

until list do list done
当list为False(non-zero)时,该圈会不停地执行。 
例一 : 等待pppd上线。 
#!/bin/sh 
until [ -f /var/run/ppp0.pid ] ; do 
  sleep 1 
done 


  参数与变数

  在继续下去介绍function之前,我们必须停下来介绍"参数与变数"。 

  *************************

  参数(Parameters)是用来储存"值"的资料型态,有点像是一般语言中的变数。它可以是个名称(name)、数字(number)、或者是以下所列出来一些特殊符号(Special Parameters)。 
  在shell中,变数是由name形式的参数所构成的。 


  *************************

  在前面的许多例中,我们事实上已经看到许多参数的运用。要设定一个Parameter实际很简单: 
  name=value 

  例如说: 

  MYHOST="foxman" 

  而要使用它时,则是加个"$"符号。 

  echo $MYHOST 

  *************************

  位置参数(Positional Parameters) 

  *************************

  所谓的位置参数便是0,1,2,3,4,5,6,7,8,9...。使用时,用$0,$1,$2...。 
  位置参数是当script被载入时,后面所附加的参数。$0是本身,$1则为第一个参数,$2为第二个,依此类推。而当Positional Parameters被function所使用时,它们会被暂时取代(下一节会介绍function)。 

  例如以下这个script: 
  #!/bin/sh 
  # Filename : position 
  echo $0 
  echo $1 

  执行时: 
  [foxman@foxman bash]# ./position abc 
  ./position 
  abc 

  当位置参数超过两位数时,有特别的方法来展开,称为Expansion。 

  *************************

  特殊参数(Speical Parameters) 
  这些符号,非常不人性,对新手来说很困扰。但上手后,会觉得方便无比,有些如果您看不懂的话,就--算了,不用浪费太多时间在上面。 

  *************************

  * 星号 
  将Positional Parameters合成一个参数,其间隔为IFS内定参数的第一个字元(见内建变数一节)。 
  例: 
  #!/bin/sh 
  # starsig 
  echo $* 

  执行: 
  [foxman@foxman bash]# starsig a b c d e f g 
  a b c d e f g 

  *************************

  @ at符号 
  与*星号类同。不同之处在于不参照IFS。 

  例: 
  #!/bin/sh 
  # atsig 
  echo $@ 

  执行: 
  [foxman@foxman bash]# atsig a b c d e f g 
  a b c d e f g 


  *************************

  # 井字号 
  展开Positional parameters的数量。 

  例: 
  #!/bin/sh 
  # poundsig 
  echo $# 

  执行 
  [foxman@foxman bash]# poundsig a b c d e f g 
  7 

  *************************

  ? 问号 
  最近执行的foreground pipeline的状态。 


  *************************

  - 减号 
  最近执行的foreground pipeline的选项参数。 

  *************************

  $ 钱钱钱 
  本身的Process ID。 

  [foxman@foxman bash]# ps ax | grep bash 
  1635 p1 S  0:00 /bin/bash 

  [foxman@foxman bash]# echo $$ 
  1635 

  *************************

  ! 惊号 
  最近执行背景命令的Process ID。 

  *************************

  0 零 
  在Positional Parameters一部份已经说明过了,是执行的shell script本身。但如果是用"bash -c",则$0被设为第一个参数。 

  [foxman@foxman bash]# echo $0 
  /bin/bash 

  *************************

  _ 底线符号 
  显示出最后一个执行的命令。 

  [foxman@foxman bash]# echo $_ 
  bash 


  *************************

  内建变数(Shell Variables) 
  Bash有许多内建变数,像PATH、HOME、ENV......等等。这些内建变数将在另一节中,专门一一说明。

  函数function

  [ function ] name () { list; }
  function的参数是Positional Paraments。 

  例 
#!/bin/sh 

function func() { 
 echo $1 
 echo $2 
 return 1 
} 

func "Hello" "function" 

  局部变数可用local来宣告。 

  函数可export,使用下一层的shell可以使用。 

  函数可递,没有递层数的限制。

  Bash内建指令集 

  以下的命令,大部份都没有使用例,您可能会看不出所以然,摸不著头脑。在我加入例说明前,建议您"man bash",然后自己实际操作一次。 

  *************************
  : [arguments] 
  不做任何事,除了[arguments]一些参数展开及一些特定重导向的作业外。 

  永远返回零。它的用法跟true一样。 

  *************************
  . filename [arguments] 
  source filename [arguments] 
  由filename中读取命令,并执行。 
  您会在/etc/rc.d/*中发现很多 
  . /xxxx 
  的指令,而xxxx的permission都不是可执行的。事实上,在tcsh中,需要用 
  source /xxxx 
  来做同样的指令。 
  注意到"."的后面是有空格的(比较一下". /"跟"./",不一样)。filename是内含指令的纯文字档即可,无须chmod 755 filename。 

  例
  filename : my_source 
  DEV=lo 
  IP=127.0.0.1 
  NETMASK=255.0.0.0 
  BROADCAST=127.255.255.255 

  ifconfig $IP netmask $NETMASK broadcast $BROADCAST dev $DEV 

  接下来 
  . my_source 
  或 
  source my_source 

  便可执行该script,而不需要"chmod 755 my_source" 

  *************************
  alias [name[=value] ...] 
  昵称命令 
  例如您如果来自DOS的世界,对UNIX的指令不习惯,可用alias来修改,以符合您的习惯。 

  例
  alias ls="ls --color" 
  alias dir="ls" 
  alias cd..="cd .." 
  alias copy="cp -f" # dangerous, recommend, "cp -i" 
  alias del="rm -f" # dangerous, recommend, "rm -i" 
  alias move="mv -f" # dangerous, recommend, "mv -i" 
  alias md="mkdir" 
  alias rd="rmdir" 

  *************************
  unalias [-a] [name ...] 
  unalias取消alias的设定。"unalias -a"将全部alias取消。 

  例
  unalias copy 

  *************************
  bg [jobspec] 
  将指定任务放到背景中,如果jobspec未指定,内定为目前的。 

  *************************
  fg [jobspec] 
  将指定任务放到前景中,如果jobsepc没有指定,那么内定为目前的。 

  *************************
  jobs [-lnp] [ jobspec ... ] 
  第一种形式列出目前正在工作的任务。 
  -l : 除了列出一般资讯外,还列出Process IDs。 
  -p : 仅列出该工作群"首脑"(Process group leader)的Process ID. 
  -n : 则仅列出有改变的jobs的状态。 
  如果给定jobspec,输出资讯则只有该jobspec。 

  返回值为零,除非有非法的选项发生。 

  jobs -x command [ args ... ] 

  如果使用第二种形式(-x),jobs取代指定的command及args,并执行返回其Exit Status。 

  *************************
  kill [-s sigspec | -sigspec] [pid | jobspec] ... 
  将sigspec的信号送到pid或jobspec。 
  sigspec可以是SIGKILL/KILL这种形式或是信号号码。如果sigspec是signal name,则大小写无关,而且可以没有SIG。 
  kill -l [signum] 
  列出信号名称。 

  [foxman@foxman bash]# kill -l 
  1) SIGHUP    2) SIGINT    3) SIGQUIT   4) SIGILL 
  5) SIGTRAP   6) SIGIOT    7) SIGBUS    8) SIGFPE 
  9) SIGKILL   10) SIGUSR1   11) SIGSEGV   12) SIGUSR2 
  13) SIGPIPE   14) SIGALRM   15) SIGTERM   17) SIGCHLD 
  18) SIGCONT   19) SIGSTOP   20) SIGTSTP   21) SIGTTIN 
  22) SIGTTOU   23) SIGURG   24) SIGXCPU   25) SIGXFSZ 
  26) SIGVTALRM  27) SIGPROF   28) SIGWINCH  29) SIGIO 
  30) SIGPWR 

  *************************
  wait [n] 
  等待指定的行程,并返回其结束状态。n可以是个jobspec或Process ID。如果n未指定,则等待所有的子行程,及返回值为零。若n为不存在的job或process,则返回127。否则,返回值为最后一个 job/process的Exit Status。 

  *************************
  bind [-m keymap] [-lvd] [-q name] 
  bind [-m keymap] -f filename 
  bind [-m keymap] keyseq:function-name 
  显示出目前readline的按键及链结函数设定或是巨集。 

  -m keymap : 设定keymap binding。 
  -l : 显示出所有readline function的名称。 
  -v : 显示出目前的function name及bindings。 
  -d : 显示出function name及bindings。 
  -f filename : 从filename读取key bindings。 
  -q function : 询问那个按键触发function。 

  *************************
  break [n] 
  跳出控制回圈for/while/until中使用。如果有指定n,则跳出n层。n必须是大于等于1。若n大于巢状圈数,则所有的圈都会跳离。返回值回零。 

  *************************
  continue [n] 
  还原控制回圈for/while/until中使用。如果有指定n,则返回n层。n必须是大于等于1。若n大于巢状圈数,则还原到最上层。返回值回零。 

  *************************
  exit [n] 
  离开程式。n是Exit Status。 

  *************************
  return [n] 
  在function中使用。n为返回值,其作用与Exit Status一样。 

  *************************
  builtin shell-builtin [arguments] 
  执行内建函数。当您定义了与内建函数相同的指令时,可用此命令来执行内建函数。 

  *************************
  cd [dir] 
  更换目录到dir。如果没有指定,内定为HOME所指定的目录。 

  *************************
  command [-pVv] command [arg ...] 
  用command指定可取消正常的shell function寻找。只有内建命令及在PATH中找得到的才会被执行。"-p"选项,搜寻命令的方式是用PATH来找。"-V"或"-v"选项,会显示出该命令的一些简约描述。 

  *************************
  declare [-frxi] [name[=value]] 
  typeset [-frxi] [name[=value]] 
  宣告参数并给它们设定属性。如果没有给定名称,将会显示各参数值。 

  -f : 仅使用函数名称。 
  -r : 将name设为readonly。 
  -x : 将name输出给后续环境使用。 
  -i : 该参数被设为integer来使用,可用于算术表述。 

  用"+"时,关闭该属性。 

  *************************
  dirs [-l] [+/-n] 
  显示目前记忆的目录。目录可透过pushd/popd来操作。 

  +n : 显示开始的记录n个。 
  -n : 显示结尾的记录n个。 
  -l : 显示较多的资讯。 

  *************************
  echo [-neE] [arg ...] 
  输出显示args,由空白分隔。返回值永为零。 

  -n : 不跳行。 
  -e : 启动"\"符号的解译。 
  -E : 将ESC解译功能取消。 

  "\a" : alert(bell),发出声响。 
  "\b" : backspace,倒退。 
  "\c" : suppress trailing newline,不跳行。 
  "\f" : form feed,跳行跳格。 
  "\n" : new line,新行。 
  "\r" : carriage return,回到行起点。 
  "\t" : horizontal tab,水平跳位。 
  "\v" : vertical tab,垂直跳位。 
  "\\" : 输出"\"。 
  "\nnn" : 输出ASCII Code号码nnn(八进位)。 

  *************************
  enable [-n] [-all] [name ...] 
  启动或关闭内建函数命令。使用"-n"将所有指定命令皆关闭,否则都是启动的。如果只有"-n"参数,它将会显示所有关闭的函数。如果只有"-all",它将会显示所有内建命令。 

  *************************
  eval [arg ...] 
  读取args,并将args合为一个命令,然后执行。其返回值成为eval的返回值。如果没有参数,eval返回True。 

  *************************
  exec [[-] command [arguments]] 
  当命令执行时,该命令取代shell,没有新的process产生。如果第一个参数是"-",shell会将"-"放入第零个参数,传给command。 

  *************************
  export [-nf] [name[=word]] ... 
  export -p 
  将name输出给环境,给往后的命令使用。"-f"选项表示name是函数。"-p"显示出所有export的名称。"-n"移除name。 

  *************************
  set [--abefhkmnptuvxldCHP] [-o option] [arg ...] 
  -a : 自动将变数标记为可让后面环境所使用。 
  -b : 立即报告被终结的背景程式状态。 
  -e : 当命令(simple-command,见后面)返回非零值时,立即跳出。 
  -f : 取消pathname expansion。 
  -h : 找出所记忆的函数命令位置。 
  -k : 所有keyword参数都放到环境中。 
  -m : 监督模式。 
  -n : 读取命令,但不要执行。可用于语法检查。 
  -p : 打开privileged模式。 
  -t : 当读取一个命令并执行后,立即离开。 
  -u : 当参数展开时,把unset参数当成是错误。 
  -v : 列出shell input lines。 
  -x : 在展开每个simple-command后,bash显示展开值在PS4上。 
  -l : 储存并还原name binding在for语法中。 
  -d : 关闭hasing command搜寻。 
  -C : 跟`noclobber=`一样。请见内定参数一节。 
  -H : 启动! style history substitution。 
  -P : 在使用像cd这种指令时,不要跟随symbolic links。 
  -- : "--"之后,没有参数跟在后面。 
  - : 指定将所有后面的参数当成是位置参数。 
  -o option-name : option-name可以是以下之一 
  allexport : 与"-a"相同。 
  braceexpand : 启动Brace Expansion。这是内定设定。 
  emacs : 使用emacs-style命令列编辑界面。 
  errexit : 与"-e"相同。 
  histexpand : 与"-H"相同。 
  ignoreeof : 效果跟`IGNOREEOF=10`一样。 
  interactive-commands : 允许#做为解。 
  monitor : 与"-m"相同。 
  noclobber : 与"-C"相同。 
  noexec : 与"-n"相同。 
  noglob : 与"-f"相同。 
  nohash : 与"-d"相同。 
  notify : 与"-b"相同。 
  nounset : 与"-u"相同。 
  physical : 与"-P"相同。 
  posix : Bash行为修改为Posix 1003.2标准。 
  privileged : 与"-p"相同。 
  verbose : 与"-v"相同。 
  vi : 使用vi-style命令列编辑程式。 
  xtrace : 与"-x"相同。 

  *************************
  unset [-fv] [name ...] 
  移除对映于name的参数。要注意PATH、IFS、PPID、PS1、PS2、UID、EUID不能unset。若RANDOM、SECONDS、 LINENO、HISTCMD被unset,它们会丧失原有意义,既始它们后来被重设也一样。返回值为True,除非name是不能被unset的。 

  *************************
  fc [-e ename] [-nlr] [first] [last] 
  fc -s [pat=rep] [cmd] 
  修正命令。 

  *************************
  getopts optstring name [args] 
  解析位置参数。 

  *************************
  help [pattern] 
  显示协助资讯。 

  *************************
  history [n] 
  history -rwan [filename] 
  没有参数时,会显示所下命令的历史记录。带有参数"n"则显示最后n个。 

  其它参数如下: 
  -a : 新增"新历史"到历史档中。 
  -n : 读取尚未读到历史中的记录。 
  -r : 读取filename做为历史档,并用它为目前历史记录。 
  -w : 将现有历史记录写到filename中。 

  *************************
  let arg [arg ...] 
  算术表述。请参考算术表述一节。 

  *************************
  local [name[=value] ...] 
  产生一个局部参数。如果用于function,则其作用围在function内及其子函数。 

  *************************
  logout 
  离开login shell。 

  *************************
  popd [+/-n] 
  移除目录堆叠。"+n"移除上面n个,"-n"移除下面n个。 


  *************************
  pushd [dir] 
  pushd +/-n 
  将目录新增到目录堆叠的最上面。"+n"旋转该堆叠,使第n个目录变成最上面。"-n"旋转该堆叠,使倒数第n个目录变成最上面。 

  *************************
  pwd 
  列出目前工作目录的绝对路径。 

  *************************
  read [-r] [name ...] 
  读进一行,然后第一个字设到第一个name,第二个设到第二个name,依此类推。如果没有name在参数中,则read会将值设到REPLY。返回值为零,除非遇到End-Of-File。若有"-r"选项,则"\n"被考虑为该行的一部份。 

  *************************
  readonly [-f] [name ...] 
  readonly -p 
  将给定的name标记为readonly。如果是"-f"选项,则函数也一样被标记为readonly。"-p"会列出所有readonly的name。"--"取消检查剩余的参数。 

  *************************
  shift [n] 
  Positional Parameters从n+1...开始,会被改为$1...。n若为零,则没有改变。n若未给定,则内定为1。n必须是非负数,并且小于或等于$#。若n大于$#,则没有改变。返回值为零,除非n大于$#或小于零。 

  *************************
  suspend [-f] 
  暂停这个shell的执行,直到它收到SIGCONT信号。"-f"选项则是叫login shell不要抱怨,不过还是一样暂停。返回状态零,除非该shell是个login shell,而且没有"-f"选项。 

  *************************
  test expr 
  [ expr ] 
  我们在Exit Status的部份已经说过了,不再重。 

  *************************
  times 
  列出该shell的累积的使用者及系统时间及从shell执行的process时间,返回值为零。 

  ------------------------------------------------------------------------------
  trap [-l] [arg] [sigspec] 
  当收到sigspec信号时,执行arg命令。"-l"显示出信号名称及号码。 

  *************************
  type [-all] [-type | -path] name [name ...] 
  没有参数的状况下,它会显示出shell如何解译name做为命令。如果有"-type",它将会显示alias、keyword、 function、builtin或file。如果有"-path"的参数,它将会显示该命令的路径,找不到的话,不显示任何东西。如果有"-all"的 参数,它将会显示所有可执行name的可能路径。type接受"-a"、"-t"、"-p"做为缩写。 

  *************************
  ulimit [-SHacdfmstpnuv [limit]] 
  ulimit提供了对shell的可获取资源控制的功能。 

  -a : 报告目前所有限制。 
  -c : 设定最大可产生的core档案。 
  -d : 行程资料段(process's data segment)最大值。 
  -f : 可被这个shell产生的最大档案。 
  -m : resident set size最大值。 
  -s : 堆叠最大值。 
  -t : CPU TIME最大值(以秒计算)。 
  -p : pipe size in 512-byte blocks的最大值。 
  -n : 可开启的file descriptors最大值。 
  -u : 单一使用者可使用的最大process数。 
  -v : 该shell最大虚拟记忆体可用值。 

  所有项目是以1024做为单位。 

  *************************
  umask [-S] [mode] 
  将使用者的file-creation mask设为mode。"-S"选项将mask印成符号形式。


  Bash内建参数

  PPID : 该bash的呼叫者process ID. 

  PWD : 目前的工作目录。 

  OLDPWD : 上一个工作目录。 

  REPLY : 当read命令没有参数时,直接设在REPLY上。 

  UID : User ID。 

  EUID : Effective User ID。 

  BASH : Bash的完整路径。 

  BASH_VERSION : Bash版本。 

  SHLVL : 每次有Bash执行时,数字加一。 

  RANDOM : 每次这个参数被用到时,就会产生一个乱数在RANDOM上。 

  SECONDS : 从这个Shell一开始启动后的时间。 

  LINENO : Script的行数。 

  HISTCMD : 历史记录数。 

  OPTARG : getopts处理的最后一个选项参数。 

  OPTIND : 下一个要由getopts所处理的参数号码。 

  HOSTTYPE : 机器种类。 

  OSTYPE : 作业系统名称。 

  IFS : Internal Field Separator。 

  PATH : 命令搜寻路径。 
  PATH="/usr/gnu/bin:/usr/local/bin:/usr/ucb:/bin:/usr/bin:." 

  HOME : 目前使用者的home directory; 

  CDPATH : cd命令的搜寻路径。 

  ENV : 如果这个参数被设定,每次有shell script被执行时,将会执行它所设定的档名做为环境设定。 

  MAIL : 如果这个参数被设定,而且MAILPATH没有被设定,那么有信件进来时,bash会通知使用者。 

  MAILCHECK : 设定多久时间检查邮件一次。 

  MAILPATH : 一串的邮件检查路径。 

  MAIL_WARNING : 如果有设定的话,邮件被读取后,将会显示讯息。 

  PS1 : 提示讯息设定,内定为"bash\$ "。(请详见提示讯息一节。) 

  PS2 : 第二提示讯息设定,内定为"> "。 

  PS3 : select命令所使用的提示讯息。 

  PS4 : 执行追踪时用的提示讯息设定,内定为"+ "。 

  HISTSIZE : 命令历史记录量,内定为500。 

  HISTFILE : 历史记录档,内定~/.bash_history。 

  HISTFILESIZE : 历史记录档行数最大值,内定500。 

  OPTERR : 如果设为1,bash会显示getopts的错误。 

  PROMPT_COMMAND : 如果设定的话,该值会在每次执行命令前都显示。 

  IGNOREEOF : 将EOF值当成输入,内定为10。 

  TMOUT : 如果设为大于零,该值被解译为输入等待秒数。若无输入,当成没有输入。 

  FCEDIT : fc命令的内定编辑器。 

  FIGNORE : 请详见READLINE。 

  INPUTRC : readline的startup file,内定~/.inputrc 

  notify : 如果设定了,bash立即报告被终结的背景程式。 

  history_control, HISTCONTROL : history使用。 

  command_oriented_history : 存入多行指令。 

  glob_dot_filenames : 如果设定了,bash将会把"."包含入档案路径中。 

  allow_null_glob_expansion : 如果设定了,bash允许路径明称为null string。 

  histchars : history使用。 

  nolinks : 如果设定了,执行指令时,不会跟随symbolic links。 

  hostname_completion_file, HOSTFILE : 包含与/etc/hosts相同格式的档名。 

  noclobber : 如果设定了,Bash不会覆写任何由">"、">&"及"<>"所操作的档案。 

  auto_resume : 请见任务控制一节。 

  no_exit_on_failed_exec : 如果该值存在,非互动的shell不会因为exec失败而跳出。 

  cdable_vars : 如果启动,而cd命令找不到目录,可切换到参数形态指定的目录下。


  提示符号

  Bash使用PS1~PS4来显示提示符号,其格式如下: 

  *************************

  \t : 现在时间。 
  \d : 现在日期。 
  \n : 新行。 
  \s : shell的名称。 
  \w : 目前工作目录。 
  \W : 目前工作目录完整路径。 
  \u : 使用者名称。 
  \h : Hostname。 
  \# : 这个命令的号码。 
  \! : 历史号码。 
  \$ : 如果EUID是0,则#,否则为$。 
  \nnn : 八进位的字元。 
  \\ : "\"符号。 
  \[ : 开始一序列不可列印的字元。 
  \] : 结束一序列不可列印的字元。


  算术表述

  - + 
  ! ~ 
  * / % 
  + - 
  << >> 
  <= >= < > 
  == != 
  & 
  ^ 
  | 
  && 
  || 
  = *= /= %= += -= <<= >>= &= ^= |= 

  重导Redirection

  > 
  >> 
  1> 
  . 
  . 


  语法

  Simple Command 


  Pipelines 


  Lists 

  (list) 
  { list; }
posted @ 2006-06-16 14:20 Alex 阅读(346) | 评论 (0)编辑 收藏

key words: css  隔行换色 控制表格  交替颜色

最近用到不少css的东西,发现用css是可以省不少麻烦,特别更改效果。

google了一篇 利用CSS控制表格的交替颜色

下面是实现的代码:

<style type="text/css">
<!--
tr 
{background-color:expression((this.sectionRowIndex==0)?"":
(this.sectionRowIndex%2==0)?"red":"blue")
}
-->
</style>
</HEAD>
<table>
<tr>
<td>第1行</td><td>第1行</td>
</tr>
<tr>
<td>第2行</td><td>第2行</td>
</tr>
<tr>
<td>第3行</td><td>第3行</td>
</tr>
<tr>
<td>第4行</td><td>第4行</td>
</tr>
<tr>
<td>第5行</td><td>第5行</td>
</tr>
</table>

关键就是这一句:
tr {background-color:expression((this.sectionRowIndex==0)?"":
(this.sectionRowIndex%2==0)?"red":"blue")}


对于td的控制如下:
<style type="text/css">
<!--
tr 
{background-color:expression((this.sectionRowIndex%2==0)?"red":"blue")}
td 
{background-color:expression((this.cellIndex%2==0)?"":((this.parentElement.sectionRowIndex%2==0)?"green":"yellow"))}
-->
</style>
</HEAD>
<table>
<tr><td>第1行</td><td>第1行</td><td>第1行< /td><td>第1行</td><td>第1行</td></tr>
<tr><td>第2行</td><td>第2行</td><td>第2行< /td><td>第2行</td><td>第2行</td></tr>
<tr><td>第3行</td><td>第3行</td><td>第3行< /td><td>第3行</td><td>第3行</td></tr>
<tr><td>第4行</td><td>第4行</td><td>第4行< /td><td>第4行</td><td>第4行</td></tr>
<tr><td>第5行</td><td>第5行</td><td>第5行< /td><td>第5行</td><td>第5行</td></tr>
</table>

cellIndex 

另:和数组一样,第一行从0开始



但愿对你有用.  :)



posted @ 2006-06-15 18:14 Alex 阅读(2980) | 评论 (2)编辑 收藏

key words: jsp文件下载 中文乱码 文件名乱码

文件上传参看: 这里

碰到文件乱码,google了一下,发现这篇文章还不赖

摘录如下:


之前,写过一个Download.jsp文件,可以解决下载文件乱码问题(诸如:DOC,XSL文件等等).
后来发现,遇到中文名的文件的时候,文件下载将会报错~~~~
今天,通过改写原Download.jsp文件已经彻底解决了这个问题~
现在,把一整套的文件上传下载的方法给贴出来~~~以便大家借鉴!~!~!~!~! 
作者:古埃及法老

download.jsp文件
---------------------------------------------------------
<%
  java.io.BufferedInputStream bis
=null;
  java.io.BufferedOutputStream  bos
=null;
try{
 
String filename=request.getParameter("filename");
             filename
=new String(filename.getBytes("iso8859-1"),"gb2312");
 response.setContentType(
"application/x-msdownload");
 response.setHeader(
"Content-disposition","attachment; filename="+new String(filename.getBytes("gb2312"),"iso8859-1"));
 bis 
=new java.io.BufferedInputStream(new java.io.FileInputStream(config.getServletContext().getRealPath("files/" + filename)));
 bos
=new java.io.BufferedOutputStream(response.getOutputStream()); 
 
byte[] buff = new byte[2048];
 
int bytesRead;
 
while(-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
  bos.write(buff,
0,bytesRead);
 }
}
catch(Exception e){
 e.printStackTrace();
}
finally {
 
if (bis != null)bis.close();
 
if (bos != null)bos.close();
}
%> 


注意,关键就是setHeader里的filename需要重新编码,格式是ISO-8859-1就OK了

以下是我自己项目中用到的代码片断,供参考:

list.jsp: 显示附件名称的页面
<tr>
            
<td height="25" class="tdcor">&nbsp;&nbsp;&nbsp;</td>
            
<td colspan="3" height=50>
                
<%
                    
if (null != publish.getAttatchFilename() &&
publish.getAttatchFilename().length() 
> 0) {
                
%>
                
<href="publish_do.jsp?method=download&fileName=
<%=URLEncoder.encode(publish.getAttatchFilename(),"
GBK")%>">
<%
=URLDecoder.decode(publish.getAttatchFilename(),"GBK")%></a>
                
<%
                    }
                
%>
            
</td>
</tr>

download.jsp:下载页面
else if (null != method && method.equals("download")) {//下载附件

        String fileName 
= request.getParameter("fileName");
        File file 
= new File(Constants.PUBLISH_FILE_PATH + "/" + URLDecoder.decode(fileName,"GBK"));
        response.reset();
        response.setContentType(
"application/octet-stream; charset=GBK");
        response.addHeader(
"Content-Disposition""attachment; filename=" + CourseDetailBusiness.transfer(URLDecoder.decode(fileName,"GBK"),"GBK","ISO-8859-1"));
        response.setContentLength((
int) file.length());

        
byte[] buffer = new byte[4096];
        BufferedOutputStream output 
= null;
        BufferedInputStream input 
= null;

        
// 写缓冲区:
        try {
            output 
= new BufferedOutputStream(response.getOutputStream());
            input 
= new BufferedInputStream(new FileInputStream(file));

            
int n = (-1);
            
while ((n = input.read(buffer, 04096)) > -1) {
                output.write(buffer, 
0, n);
            }
            response.flushBuffer();
        }
        
catch (Exception e) {
        } 
// maybe user cancelled download
        finally {
            
if (input != null) input.close();
            
if (output != null) output.close();
        }


说明:
1。文件名在数据库中保存的编码为URLEncode
2.在list.jsp显示的时候多了一次encode,不知为什么,不encode一次还不行,实际上是第二次编码了


posted @ 2006-06-14 22:48 Alex 阅读(35473) | 评论 (35)编辑 收藏

key words: 数据字典 用户 表 视图

1、用户

查看当前用户的缺省表空间
SQL>select username,default_tablespace from user_users;

查看当前用户的角色
SQL>select * from user_role_privs;

查看当前用户的系统权限和表级权限
SQL>select * from user_sys_privs;
SQL>select * from user_tab_privs;

显示当前会话所具有的权限
SQL>select * from session_privs;

显示指定用户所具有的系统权限
SQL>select * from dba_sys_privs where grantee='GAME';


2、表

查看用户下所有的表
SQL>select * from user_tables;

查看名称包含log字符的表
SQL>select object_name,object_id from user_objects
where instr(object_name,'LOG')>0;

查看某表的创建时间
SQL>select object_name,created from user_objects where object_name=upper('&table_name');

查看某表的大小
SQL>select sum(bytes)/(1024*1024) as "size(M)" from user_segments
where segment_name=upper('&table_name');

查看放在ORACLE的内存区里的表
SQL>select table_name,cache from user_tables where instr(cache,'Y')>0;

3、索引

查看索引个数和类别
SQL>select index_name,index_type,table_name from user_indexes order by table_name;

查看索引被索引的字段
SQL>select * from user_ind_columns where index_name=upper('&index_name');

查看索引的大小
SQL>select sum(bytes)/(1024*1024) as "size(M)" from user_segments
where segment_name=upper('&index_name');

4、序列号

查看序列号,last_number是当前值
SQL>select * from user_sequences;

5、视图

查看视图的名称
SQL>select view_name from user_views;

查看创建视图的select语句
SQL>set view_name,text_length from user_views;
SQL>set long 2000; 说明:可以根据视图的text_length值设定set long 的大小
SQL>select text from user_views where view_name=upper('&view_name');

6、同义词

查看同义词的名称
SQL>select * from user_synonyms;

7、约束条件

查看某表的约束条件
SQL>select constraint_name, constraint_type,search_condition, r_constraint_name
from user_constraints where table_name = upper('&table_name');

SQL>select c.constraint_name,c.constraint_type,cc.column_name
from user_constraints c,user_cons_columns cc
where c.owner = upper('&table_owner') and c.table_name = upper('&table_name')
and c.owner = cc.owner and c.constraint_name = cc.constraint_name
order by cc.position;

8、存储函数和过程

查看函数和过程的状态
SQL>select object_name,status from user_objects where object_type='FUNCTION';
SQL>select object_name,status from user_objects where object_type='PROCEDURE';

查看函数和过程的源代码
SQL>select text from all_source where owner=user and name=upper('&plsql_name');
posted @ 2006-06-12 14:25 Alex 阅读(347) | 评论 (0)编辑 收藏

仅列出标题
共15页: First 上一页 7 8 9 10 11 12 13 14 15 下一页