tbwshc

#

  oracle 字段类型

 oracle 字段类型   CHAR    固定长度字符串    最大长度2000    bytes           

   VARCHAR2    可变长度的字符串    最大长度4000    bytes      可做索引的最大长度749     

   NCHAR    根据字符集而定的固定长度字符串    最大长度2000    bytes           

   NVARCHAR2    根据字符集而定的可变长度字符串    最大长度4000    bytes           

   DATE    日期(日-月-年)    DD-MM-YY(HH-MI-SS)    经过严格测试,无千虫问题     

   LONG    超长字符串    最大长度2G(231-1)    足够存储大部头著作     

   RAW    固定长度的二进制数据    最大长度2000    bytes      可存放多媒体图象声音等     

   LONG    RAW    可变长度的二进制数据    最大长度2G    同上     

   BLOB    二进制数据    最大长度4G         

   CLOB    字符数据    最大长度4G         

   NCLOB    根据字符集而定的字符数据    最大长度4G         

   BFILE    存放在数据库外的二进制数据    最大长度4G         

   ROWID    数据表中记录的唯一行号    10    bytes    ********.****.****格式,*为0或1     

   NROWID    二进制数据表中记录的唯一行号    最大长度4000    bytes     

   NUMBER(P,S)    数字类型    P为整数位,S为小数位     

   DECIMAL(P,S)    数字类型    P为整数位,S为小数位     

   INTEGER    整数类型    小的整数     

   FLOAT    浮点数类型    NUMBER(38),双精度     

   REAL    实数类型    NUMBER(63),精度更高     

数据类型 参数 描述

char(n) n=1 to 2000字节 定长字符串,n字节长,如果不指定长度,缺省为1个字节长(一个汉字为2字节)

varchar2(n) n=1 to 4000字节 可变长的字符串,具体定义时指明最大长度n,

这种数据类型可以放数字、字母以及ASCII码字符集(或者EBCDIC等数据库系统接受的字符集标准)中的所有符号。

如果数据长度没有达到最大值n,Oracle 8i会根据数据大小自动调节字段长度,

如果你的数据前后有空格,Oracle 8i会自动将其删去。VARCHAR2是最常用的数据类型。

可做索引的最大长度3209。

number(m,n) m=1 to 38

n=-84 to 127 可变长的数值列,允许0、正值及负值,m是所有有效数字的位数,n是小数点以后的位数。

如:number(5,2),则这个字段的最大值是99,999,如果数值超出了位数限制就会被截取多余的位数。

如:number(5,2),但在一行数据中的这个字段输入575.316,则真正保存到字段中的数值是575.32。

如:number(3,0),输入575.316,真正保存的数据是575。  

date 无 从公元前4712年1月1日到公元4712年12月31日的所有合法日期,

Oracle 8i其实在内部是按7个字节来保存日期数据,在定义中还包括小时、分、秒。

缺省格式为DD-MON-YY,如07-11月-00 表示2000年11月7日。  

long 无 可变长字符列,最大长度限制是2GB,用于不需要作字符串搜索的长串数据,如果要进行字符搜索就要用varchar2类型。

long是一种较老的数据类型,将来会逐渐被BLOB、CLOB、NCLOB等大的对象数据类型所取代。  

raw(n) n=1 to 2000 可变长二进制数据,在具体定义字段的时候必须指明最大长度n,Oracle 8i用这种格式来保存较小的图形文件或带格式的文本文件,如Miceosoft Word文档。

raw是一种较老的数据类型,将来会逐渐被BLOB、CLOB、NCLOB等大的对象数据类型所取代。  

long raw 无 可变长二进制数据,最大长度是2GB。Oracle 8i用这种格式来保存较大的图形文件或带格式的文本文件,如Miceosoft Word文档,以及音频、视频等非文本文件。

在同一张表中不能同时有long类型和long raw类型,long raw也是一种较老的数据类型,将来会逐渐被BLOB、CLOB、NCLOB等大的对象数据类型所取代。  

blob

clob

nclob 无 三种大型对象(LOB),用来保存较大的图形文件或带格式的文本文件,如Miceosoft Word文档,以及音频、视频等非文本文件,最大长度是4GB。

LOB有几种类型,取决于你使用的字节的类型,Oracle 8i实实在在地将这些数据存储在数据库内部保存。

可以执行读取、存储、写入等特殊操作。  

bfile 无 在数据库外部保存的大型二进制对象文件,最大长度是4GB。

这种外部的LOB类型,通过数据库记录变化情况,但是数据的具体保存是在数据库外部进行的。

Oracle 8i可以读取、查询BFILE,但是不能写入。

大小由操作系统决定。  

数据类型是列或存储过程中的一个属性。

    Oracle支持的数据类型可以分为三个基本种类:字符数据类型、数字数据类型以及表示其它数据的数据类型。

    字符数据类型

      CHAR             char数据类型存储固定长度的字符值。一个CHAR数据类型可以包括1到2000个字符。如果对CHAR没有明确地说明长度,它的默认长度则设置为1。如果对某个CHAR类型变量赋值,其长度小于规定的长度,那么Oracle自动用空格填充。

      VARCHAR2 存储可变长度的字符串。虽然也必须指定一个VARCHAR2数据变量的长度,但是这个长度是指对该变量赋值的最大长度而非实际赋值长度。不需用空格填充。最多可设置为4000个字符。因为VARCHAR2数据类型只存储为该列所赋的字符(不加空格),所以VARCHAR2需要的存储空间比CHAR数据类型要小。

    Oracle推荐使用VARCHAR2

      NCHAR和NVARCHAR2 NCHAR和NVARCHAR2数据类型分别存储固定长度与可变长度的字符串,但是它们使用的是和数据库其他类型不同的字符集。在创建数据库时,需要指定所使用的字符集,以便对数据中数据进行编码。还可以指定一个辅助的字符集[即本地语言集]。NCHAR和NVARCHAR2类型的列使用辅助字符集。NCHAR和NVARCHAR2类型的列使用辅助字符集。

      在Oracle 9i中,可以以字符而不是字节为单位表示NCHAR和NVARCHAR2列的长度。

      LONG long数据类型可以存放2GB的字符数据,它是从早期版本中继承下来的。现在如果存储大容量的数据,Oracle推荐使用CLOB和NCLOB数据类型。在表和SQL语句中使用LONG类型有许多限制。

      CLOB和NCLOB    CLOB和NCLOB数据类型可以存储多达4GB的字符数据。NCLOB数据类型可存储NLS数据。

      数字数据类型

      Oracle使用标准、可变长度的内部格式来存储数字。这个内部格式精度可以高达38位。

      NUMBER数据类型可以有两个限定符,如:column NUMBER(precision,scale)。precision表示数字中的有效位。如果没有指定precision的话,Oracle将使用38作为精度。scale表示小数点右边的位数,scale默认设置为0。如果把scale设成负数,Oracle将把该数字取舍到小数点左边的指定位数。

      日期数据类型

       Oracle标准日期格式为:DD-MON-YY HH:MI:SS

       通过修改实例的参数NLS_DATE_FORMAT,可以改变实例中插入日期的格式。在一个会话期间,可以通过Alter session SQL命令来修改日期,或者通过使用SQL语句的TO_DATE表达式中的参数来更新一个特定值。

       其它的数据类型

       RAW和LONG RAW    RAW和LONG RAW数据类型主要用于对数据库进行解释。tb指定这两种类型时,Oracle以位的形式来存储数据。RAW数据类型一般用于存储有特定格式的对象,如位图。RAW数据类型可占用2KB的空间,而LONG RAW数据类型则可以占用2GB大小。

       ROWID ROWID是一种特殊的列类型,称之为伪列(pseudocolumn)。ROWID伪列在SQL SELECT语句中可以像普通列那样被访问。Oracle数据库中每行都有一个伪列。ROWID表示行的地址,ROWID伪列用ROWID数据类型定义。

       ROWID与磁盘驱动的特定位置有关,因此,ROWID是获得行的最快方法。但是,行的ROWID会随着卸载和重载数据库而发生变化,因此建议不要在事务中使用ROWID伪列的值。例如,一旦当前应用已经使用完记录,就没有理由保存行的ROWID。不能通过任何SQL语句来设置标准的ROWID伪列的值。

      列或变量可以定义成ROWID数据类型,但是Oracle不能保证该列或变量的值是一个有效的ROWID。

    LOB(大型对象)数据类型,可以保存4GB的信息。LOB有以下3中类型:

      <CLOB>,只能存储字符数据

    <NCLOB>,保存本地语言字符集数据

    <BLOB>    ,以二进制信息保存数据

      可以指定将一个LOB数据保存在Oracle数据库内,还是指向一个包含次数据的外部文件。

      LOB可以参与事务。管理LOB中的数据必须通过DBMS_LOB PL/SQL内置软件包或者OGI接口。

      为了便于将LONG数据类型转换成LOB,Oracle 9i包含许多同时支持LOB和LONG的函数,喊包括一个ALTER TABLE语句的新选择,它允许将LONG数据类型自动转换成LOB。

    BFILE

      BFILE数据类型用做指向存储在Oracle数据库以外的文件的指针。

      XML Type

      作为对XML支持的一部分,Oracle 9i包含了一个新的数据类型XML Type。定义为XMLType的列将存储一个字符LOB列中的XML文档。有许多内置的功能可以使你从文档中抽取单个节点,还可以在XML Type文档中对任何节点创建索引。

      用户自定义数据

      从Oracle 8以后,用户可以定义自己的复杂数据类型,它们由Oracle基本数据类型组合而成。

      AnyType、AnyData和AnyDataSet

       Oracle包括3个新的数据类型,用于定义在现有数据类型之外的数据结构。其中每种数据类型必须用程序单元来定义,以便让Oracle9i知道如何处理这些类型的特定实现。

    类型转换

    Oracle会自动将某些数据类型转换成其他的数据类型,转换取决于包括该值的SQL语句。

    数据转换还可以通过Oracle的类型转换函数显示地进行。

    连接与比较

    在大多数平台上Oracle SQL中的连接操作符用两条竖线(||)表示。连接是将两个字符值连接。Oracle的自动类型转换功能使得两个数字值也可以进行连接。

    NULL

    NULL值是关系数据库的重要特征之一。实际上,NULL不代表任何值,它表示没有值。如果要创建表的一个列,而这个列必须有值,那么应将它指定为NOT NULL,这表示该列不能包含NULL值。

    任何数据类型都可以赋予NULL值。NULL值引入了SQL运算的三态逻辑。如果比较的一方是NULL值,那么会出现3种状态:TURE、FALSE以及两者都不是。

    因为NULL值不等于0或其他任何值,所以测试某个数据是否为NULL值只能通过关系运算符IS NULL来进行。

    NULL值特别适合以下情况:当一个列还未赋值时。如果选择不使用NULL值,那么必须对行的所有列都要赋值。这实际上也取消了某列不需要值的可能性,同时对它赋的值也很容易产生误解。这种情况则可能误导终端用户,并且导致累计操作的错误结果。

number(p,s)

p:1~38

s:-84~127

p>0,对s分2种情况:1. s>0

精确到小数点右边s位,并四舍五入。然后检验有效数位是否<=p;如果s>p,小数点右边至少有s-p个0填充。

2. s<0

精确到小数点左边s位,并四舍五入。然后检验有效数位是否<=p+|s|

123.2564 NUMBER 123.2564

1234.9876 NUMBER(6,2) 1234.99

12345.12345 NUMBER(6,2) Error

1234.9876 NUMBER(6) 1235

12345.345 NUMBER(5,-2) 12300

1234567 NUMBER(5,-2) 1234600

12345678 NUMBER(5,-2) Error

123456789 NUMBER(5,-4) 123460000

1234567890 NUMBER(5,-4) Error

12345.58 NUMBER(*, 1) 12345.6

0.1 NUMBER(4,5) Error

0.01234567 NUMBER(4,5) 0.01235

0.09999 NUMBER(4,5) 0.09999

posted @ 2012-07-17 15:20 chen11-1 阅读(1045) | 评论 (0)编辑 收藏

Java编程基础-变量,常量,运算符,函数,程序流程控制

今天看了第二章Java编程基础,总体来说这一章我自认为是最简单的一张。同其他的编程语言一样,同样有变量,常量,运算符,函数,程序流程控制等。但是我觉得学好这一章主要是要抓住Java与其他的语言的不同,至于相同的就没必要花费大量的心思去研究了。

首先Java是严格区分大小写的,我觉得这正是语言的严谨性的一个重要方便,很多不错的编程语言都区分,如C,C++,C#,PHP等。Java的格式是自由的多个语句可以写在一行,一个语句也可以写在多行,但是一个连续的字符串不能分开在在多行写,功能执行语句必须以;结束。为了使程序具有可读性,还是要按照缩进和对齐的标准来写。

Java的注释有三种,前两种是其他语言所共有的,而文档注释是Java所特有的。文档注释是以结束。Javadoc工具是处理文档注释的工具,Javadoc可以将文档注释的内容信息取出,然后转换为HTML的格式文档。如:

Javadoc运行程序的命令格式为javadoc –d 类名 –version –author 源文件名。

Java中标示符的规定是:任意顺序的大小写字母,数字,下划线,tb美元符号,但不能以数字开头,最主要的特点可以包含美元符号。Java中的常量包括:整型,浮点型,布尔型,字符型,字符串型。内建有8种基本变量:整型(byte,short,int,long)浮点型(floatdouble)字符型(char)布尔型(boolean)。数据类型之间的转换包括自动类型的转换和强制类型转换。实现自动类型的转换符合的条件是两种类型彼此兼容和目标类型的取值范围大于源类型。如byte可以自动转换为short,int,long。不会产生数据丢失。而强制类型转换一般会有数据的丢失。格式为 目标类型 目标变量=(目标类型)值。

表达式的类型提升规则:

1. 所有的byte,short,char类型可以自动提升为int

2. 如果一个操作数是long型,计算结果就是long

3. 如果一个操作数是float型,计算结果就是float

4. 如果一个操作数是double型,计算结果就是double

关于函数和函数的重载问题,函数的概念就不用提了,是所有编程语言所共有的。关于函数重载是面向对象的编程语言所特有的。在一个类中允许有一个以上的同名函数,只要参数个数或类型不同即可,在这种情况下,就叫做重载。Java中的运算符和C语言的运算符基本相同,没有什么异同。

程序的流程控制有三种结构:顺序结构,循环结构,选择结构。这三个结构没什么特殊的,和其他的语言没有什么不同。break语句是可以中止循环体内的语句和switch语句而continue语句就是跳出当前循环的剩余语句块。

Java中没有真正的多维数组,只有数组的数组。Java中的多维数组不一定是规则矩阵的形式。一些与数组操作相关的函数:system.arraycopy()函数用于复制数组,Arrays.sort()函数是用来排序数组。

posted @ 2012-07-16 13:26 chen11-1 阅读(1043) | 评论 (0)编辑 收藏

设计模式之Flyweight(享元)

设计模式之Flyweight(享元)

Flyweight定义:
避免大量拥有相同内容的小类的开销(如耗费内存),使大家共享一个类(元类).

为什么使用?
面向对象语言的原则就是一切都是对象,但是如果真正使用起来,有时对象数可能显得很庞大,比如,字处理软件,如果以每个文字都作为一个对象,几千个字,对象数就是几千,无疑耗费内存,那么我们还是要"求同存异",找出这些对象群的共同点,设计一个元类,封装可以被共享的类,另外,还有一些特性是取决于应用(context),是不可共享的,这也Flyweight中两个重要概念内部状态intrinsic和外部状态extrinsic之分.

说白点,就是先捏一个的原始模型,然后随着不同场合和环境,再产生各具特征的具体模型,很显然,在这里需要产生不同的新对象,所以Flyweight模式中常出现Factory模式.Flyweight的内部状态是用来共享的,Flyweight factory负责维护一个Flyweight pool(模式池)来存放内部状态的对象.

Flyweight模式是一个提高程序效率和性能的模式,会大大加快程序的运行速度.应用场合很多:比如你要从一个数据库中读取一系列字符串,这些字符串中有许多是重复的,那么我们可以将这些字符串储存在Flyweight池(pool)中.

如何使用?

我们先从Flyweight抽象接口开始:

public interface Flyweightb
{
  public void operation( ExtrinsicState state );
}

//用于本模式的抽象数据类型(自行设计)
public interface ExtrinsicState { }

下面是接口的具体实现(ConcreteFlyweight) ,并为内部状态增加内存空间, ConcreteFlyweight必须是可共享的,它保存的任何状态都必须是内部(intrinsic),也就是说,ConcreteFlyweight必须和它的应用环境场合无关.

public class ConcreteFlyweight implements Flyweight {
  private IntrinsicState state;
  
  public void operation( ExtrinsicState state )
  {
      //具体操作
  }

}

当然,并不是所有的Flyweight具体实现子类都需要被共享的,所以还有另外一种不共享的ConcreteFlyweight:

public class UnsharedConcreteFlyweight implements Flyweight {

  public void operation( ExtrinsicState state ) { }

}

Flyweight factory负责维护一个Flyweight池(存放内部状态),当客户端请求一个共享Flyweight时,这个factory首先搜索池中是否已经有可适用的,如果有,factory只是简单返回送出这个对象,否则,创建一个新的对象,加入到池中,再返回送出这个对象.池

public class FlyweightFactory {
  //Flyweight pool
  private Hashtable flyweights = new Hashtable();

  public Flyweight getFlyweight( Object key ) {

    Flyweight flyweight = (Flyweight) flyweights.get(key);

    if( flyweight == null ) {
      //产生新的ConcreteFlyweight
      flyweight = new ConcreteFlyweight();
      flyweights.put( key, flyweight );
    }

    return flyweight;
  }
}

至此,Flyweight模式的基本框架已经就绪,我们看看如何调用:

FlyweightFactory factory = new FlyweightFactory();
Flyweight fly1 = factory.getFlyweight( "Fred" );
Flyweight fly2 = factory.getFlyweight( "Wilma" );
......

从调用上看,好象是个纯粹的Factory使用,但奥妙就在于Factory的内部设计上.

Flyweight模式在XML等数据源中应用
我们上面已经提到,当大量从数据源中读取字符串,其中肯定有重复的,那么我们使用Flyweight模式可以提高效率,以唱片CD为例,在一个XML文件中,存放了多个CD的资料.

每个CD有三个字段:
1.出片日期(year)
2.歌唱者姓名等信息(artist)
3.唱片曲目 (title)

其中,歌唱者姓名有可能重复,也就是说,可能有同一个演唱者的多个不同时期 不同曲目的CD.我们将"歌唱者姓名"作为可共享的ConcreteFlyweight.其他两个字段作为UnsharedConcreteFlyweight.

首先看看数据源XML文件的内容:


<?xml version="1.0"?>
<collection>

<cd>
<title>Another Green World</title>
<year>1978</year>
<artist>Eno, Brian</artist>
</cd>

<cd>
<title>Greatest Hits</title>
<year>1950</year>
<artist>Holiday, Billie</artist>
</cd>

<cd>
<title>Taking Tiger Mountain (by strategy)</title>
<year>1977</year>
<artist>Eno, Brian</artist>
</cd>

.......

</collection>


虽然上面举例CD只有3张,CD可看成是大量重复的小类,因为其中成分只有三个字段,而且有重复的(歌唱者姓名).

CD就是类似上面接口 Flyweight:


public class CD {

  private String title;
  private int year;
  private Artist artist;

  public String getTitle() {  return title; }
  public int getYear() {    return year;  }
  public Artist getArtist() {    return artist;  }

  public void setTitle(String t){    title = t;}
  public void setYear(int y){year = y;}
  public void setArtist(Artist a){artist = a;}

}

"歌唱者姓名"作为可共享的ConcreteFlyweight:

public class Artist {

  //内部状态
  private String name;

  // note that Artist is immutable.
  String getName(){return name;}

  Artist(String n){
    name = n;
  }

}

再看看Flyweight factory,专门用来制造上面的可共享的ConcreteFlyweight:Artist

public class ArtistFactory {

  Hashtable pool = new Hashtable();

  Artist getArtist(String key){

    Artist result;
    result = (Artist)pool.get(key);
    ////产生新的Artist
    if(result == null) {
      result = new Artist(key);
      pool.put(key,result);
      
    }
    return result;
  }

}

当你有几千张甚至更多CD时,Flyweight模式将节省更多空间,共享的flyweight越多,空间节省也就越大.

posted @ 2012-07-16 13:25 chen11-1 阅读(865) | 评论 (0)编辑 收藏

设计模式之Bridge

设计模式之Bridge
板桥里人 http://www.jdon.com 2002/05/01

Bridge定义 :
将抽象和行为划分开来,各自独立,但能动态的结合.

为什么使用?
通常,当一个抽象类或接口有多个具体实现(concrete subclass),这些concrete之间关系可能有以下两种:
1. 这多个具体实现之间恰好是并列的,如前面举例,打桩,有两个concrete class:方形桩和圆形桩;这两个形状上的桩是并列的,没有概念上的重复,那么我们只要使用继承就可以了.

2.实际应用上,常常有可能在这多个concrete class之间有概念上重叠.那么需要我们把抽象共同部分和行为共同部分各自独立开来,原来是准备放在一个接口里,现在需要设计两个接口,分别放置抽象和行为.

例如,一杯咖啡为例,有中杯和大杯之分,同时还有加奶 不加奶之分. 如果用单纯的继承,这四个具体实现(中杯 大杯 加奶 不加奶)之间有概念重叠,因为有中杯加奶,也有中杯不加奶, 如果再在中杯这一层再实现两个继承,很显然混乱,扩展性极差.那我们使用Bridge模式来实现它.

如何实现?
以上面提到的咖啡 为例. 我们原来打算只设计一个接口(抽象类),使用Bridge模式后,我们需要将抽象和行为分开,加奶和不加奶属于行为,我们将它们抽象成一个专门的行为接口.

先看看抽象部分的接口代码:

public abstract class Coffee
{
  CoffeeImp coffeeImp;

  public void setCoffeeImp() {
    this.CoffeeImp = CoffeeImpSingleton.getTheCoffeImp();
  }

  public CoffeeImp getCoffeeImp() {return this.CoffeeImp;}

  public abstract void pourCoffee();
}
 

其中CoffeeImp 是加不加奶的行为接口,看其代码如下:

public abstract class CoffeeImp
{
  public abstract void pourCoffeeImp();
}
 

现在我们有了两个抽象类,下面我们分别对其进行继承,实现concrete class:

//中杯
public class MediumCoffee extends Coffee
{
  public MediumCoffee() {setCoffeeImp();}

  public void pourCoffee()
  {
    CoffeeImp coffeeImp = this.getCoffeeImp();
    //我们以重复次数来说明是冲中杯还是大杯 ,重复2次是中杯
    for (int i = 0; i < 2; i++)
    {

      coffeeImp.pourCoffeeImp();
    }
  
  }
}

//大杯
public class SuperSizeCoffee extends Coffee
{
  public SuperSizeCoffee() {setCoffeeImp();}

  public void pourCoffee()
  {
    CoffeeImp coffeeImp = this.getCoffeeImp();
    //我们以重复次数来说明是冲中杯还是大杯 ,重复5次是大杯
    for (int i = 0; i < 5; i++)
    {

      coffeeImp.pourCoffeeImp();
    }
  
  }
}
 

上面分别是中杯和大杯的具体实现.下面再对行为CoffeeImp进行继承:

//加奶
public class MilkCoffeeImp extends CoffeeImp
{
  MilkCoffeeImp() {}

  public void pourCoffeeImp()
  {
    System.out.println("加了美味的牛奶");
  }
}

//不加奶
public class FragrantCoffeeImp extends CoffeeImp
{
  FragrantCoffeeImp() {}

  public void pourCoffeeImp()
  {
    System.out.println("什么也没加,清香");
  }
}
 

Bridge模式的基本框架我们已经搭好了,别忘记定义中还有一句:动态结合,我们现在可以喝到至少四种咖啡:
1.中杯加奶
2.中杯不加奶
3.大杯加奶
4.大杯不加奶

看看是如何动态结合的,在使用之前,我们做个准备工作,设计一个单态类(Singleton)用来hold当前的CoffeeImp:

public class CoffeeImpSingleton
{
  private static CoffeeImp coffeeImp;

  public CoffeeImpSingleton(CoffeeImp coffeeImpIn)
   {this.coffeeImp = coffeeImpIn;}

  public static CoffeeImp getTheCoffeeImp()
  {
    return coffeeImp;
  }
}
 

看看中杯加奶 和大杯加奶 是怎么出来的:

//拿出牛奶
CoffeeImpSingleton coffeeImpSingleton = new CoffeeImpSingleton(new MilkCoffeeImp());

//中杯加奶
MediumCoffee mediumCoffee = new MediumCoffee();
mediumCoffee.pourCoffee();

//大杯加奶
SuperSizeCoffee superSizeCoffee = new SuperSizeCoffee();
superSizeCoffee.pourCoffee();

注意: Bridge模式的执行类如CoffeeImp和Coffee是一对一的关系, 正确创建CoffeeImp是该模式的关键,

Bridge模式在EJB中的应用
EJB中有一个Data Access Object (DAO)模式,这是将商业逻辑和具体数据资源分开的,因为不同的数据库有不同的数据库操作.将操作不同数据库的行为独立抽象成一个行为接口DAO.如下:

1.Business Object (类似Coffee)

实现一些抽象的商业操作:如寻找一个用户下所有的订单

涉及数据库操作都使用DAOImplementbor.

2.Data Access Object (类似CoffeeImp)

一些抽象的对数据库资源操作
3.DAOImplementor 如OrderDAOCS, OrderDAOOracle, OrderDAOSybase(类似MilkCoffeeImp FragrantCoffeeImp)

具体的数据库操作,如"INSERT INTO "等语句,OrderDAOOracle是Oracle OrderDAOSybase是Sybase数据库.

4.数据库 (Cloudscape, Oracle, or Sybase database via JDBC API)

 

posted @ 2012-07-16 13:24 chen11-1 阅读(884) | 评论 (0)编辑 收藏

配置Apache+Tomcat集群

操作系统:Debian6.0 (192.168.225.129 虚拟机1台)

软件版本:Apache-2.2.16, Tomcat-6.0.35

1.安装软件

    >apt-get install update

    *安装Apache

    >apt-get install apache2

    *安装Tomcat

    >wget http://apache.etoak.com/tomcat/tomcat-6/v6.0.35/bin/apache-tomcat-6.0.35.tar.gz

    >tar zxvf apache-tomcat-6.0.35.tar.gz

    >mv apache-tomcat-6.0.35 /user/local/tomcat

    >cp /usr/local/tomcat  /usr/local/tomcat2

    *安装mod_jk

    >apt-get install libapache2-mod-jk

2.配置2个Tomcat

    现在/usr/local目录中已经有2个tomcat目录了,需要更改tomcat2的端口,防止端口冲突。

    >nano /usr/local/tomcat/conf/server.xml

    有3处的默认端口需要更改:

    1.<Server port="8004" shutdown="SHUTDOWN"> 我更改为8003

    2.<Connector port="8080" protocol="HTTP/1.1" 
               connectionTimeout="20000" 
               redirectPort="8443" />

        更改为7080

    3.<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

        更改为7009,其中redirectPort 8443不需要更改

    o.另外还需要在<Engine>配置中加入<Clustber>配置

 

Java代码 复制代码 收藏代码
  1. <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"  
  2.     channelSendOptions="8">   
  3.     <Manager className="org.apache.catalina.ha.session.DeltaManager" expireSessionsOnShutdown="false"    
  4.         notifyListenersOnReplication="true"/>      
  5.     <Channel className="org.apache.catalina.tribes.group.GroupChannel">     
  6.         <Membership className="org.apache.catalina.tribes.membership.McastService"    
  7.             address="228.0.0.4"    
  8.             port="45564"    
  9.             frequency="500"    
  10.             dropTime="3000"/>     
  11.         <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"    
  12.             address="auto"    
  13.             port="4001"    
  14.             selectorTimeout="5000"    
  15.             maxThreads="6"/>     
  16.         <!-- timeout="60000"-->     
  17.         <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">     
  18.             <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>     
  19.         </Sender>     
  20.         <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>     
  21.         <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>     
  22.         <Interceptor className="org.apache.catalina.tribes.group.interceptors.ThroughputInterceptor"/>     
  23.     </Channel>    
  24.     <Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=""/>     
  25.     <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>      
  26.     <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"    
  27.         tempDir="/tmp/war-temp/"    
  28.         deployDir="/tmp/war-deploy/"    
  29.         watchDir="/tmp/war-listen/"    
  30.         watchEnabled="false"/>    
  31.     <ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/>     
  32.     <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>       
  33. </Cluster>  

     x.最后2个tomcat都需要改变<Engine的 jvmRoute属性分别为tomcat1和tomcat2,以对应之后worker.properties中的名字。


3.配置Apache以jk方式和tomcat集群

   安装完成后apache的主目录为/etc/apache2,安装完mod-jk之后,mods-enabled里面会多一个jk.load

   创建文件   /etc/apache2/mods-enabled/jk.conf

    >nano /etc/apache2/mods-enabled/jk.conf

       JkWorkersFile /etc/apache2/workers.properties
       JkShmFile     /var/log/apache2/mod_jk.shm
       JkLogFile     /var/log/apache2/mod_jk.log
       JkLogLevel    info

   创建文件  /etc/apache2/workers.properties

    >nano /etc/apache2/workers.properties

       worker.list=controller1

       worker.tomcat1.port=8009
       worker.tomcat1.host=localhost
       worker.tomcat1.type=ajp13
       worker.tomcat1.lbfactor=1

       worker.tomcat2.port=7009
       worker.tomcat2.host=localhost
       worker.tomcat2.type=ajp13
       worker.tomcat2.lbfactor=1

       worker.controller1.type=lb
       worker.controller1.sticky_session=1   #可选项0,1
       worker.controller1.balance_workers=tomcat1,tomcat2

    更改/etc/apache2/sites-enabled/000-default文件

      在</VirtualHost>之前添加

       JkMount /* controller1

       (controller1对应workers.properties中的名字)

配置完成之后先后启动tomcat和apache。

测试:

2个tomcat部署相同的应用:即首页index.jsp显示各自tomcat名称tomcat1和tomcat2

通过80端口访问应用,刷新几次,tomcat1和tomcat2轮流显示。

posted @ 2012-07-13 13:53 chen11-1 阅读(1456) | 评论 (0)编辑 收藏

SSL介绍与Java实例

有关SSL的原理和介绍在网上已经有不少,对于Java下使用keytool生成证书,配置SSL通信的教程也非常多。但如果我们不能够亲自动手做一个SSL Sever和SSL Client,可能就永远也不能深入地理解Java环境下,SSL的通信是如何实现的。对SSL中的各种概念的认识也可能会仅限于可以使用的程度。本文通过构造一个简单的SSL Server和SSL Client来讲解Java环境下SSL的通信原理。

首先我们先回顾一下常规的Java Socket编程。在Java下写一个Socket服务器和客户端的例子还是比较简单的。以下是服务端的代码:


Java代码 
1.package org.bluedash.tryssl;  
2. 
3.import java.io.BufferedReader;  
4.import java.io.IOException;  
5.import java.io.InputStbreamReader;  
6.import java.io.PrintWriter;  
7.import java.net.ServerSocket;  
8.import java.net.Socket;  
9. 
10.public class Server extends Thread {  
11.    private Socket socket;  
12. 
13.    public Server(Socket socket) {  
14.        this.socket = socket;  
15.    }  
16. 
17.    public void run() {  
18.        try {  
19.            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));  
20.            PrintWriter writer = new PrintWriter(socket.getOutputStream());  
21. 
22.            String data = reader.readLine();  
23.            writer.println(data);  
24.            writer.close();  
25.            socket.close();  
26.        } catch (IOException e) {  
27. 
28.        }  
29.    }  
30.      
31.    public static void main(String[] args) throws Exception {  
32.        while (true) {  
33.            new Server((new ServerSocket(8080)).accept()).start();  
34.        }  
35.    }  
36.} 
package org.bluedash.tryssl;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class Server extends Thread {
 private Socket socket;

 public Server(Socket socket) {
  this.socket = socket;
 }

 public void run() {
  try {
   BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
   PrintWriter writer = new PrintWriter(socket.getOutputStream());

   String data = reader.readLine();
   writer.println(data);
   writer.close();
   socket.close();
  } catch (IOException e) {

  }
 }
 
 public static void main(String[] args) throws Exception {
  while (true) {
   new Server((new ServerSocket(8080)).accept()).start();
  }
 }
}


服务端很简单:侦听8080端口,并把客户端发来的字符串返回去。下面是客户端的代码:


Java代码 
1.package org.bluedash.tryssl;  
2. 
3.import java.io.BufferedReader;  
4.import java.io.InputStreamReader;  
5.import java.io.PrintWriter;  
6.import java.net.Socket;  
7. 
8.public class Client {  
9. 
10.    public static void main(String[] args) throws Exception {  
11. 
12.        Socket s = new Socket("localhost", 8080);  
13. 
14.        PrintWriter writer = new PrintWriter(s.getOutputStream());  
15.        BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));  
16.        writer.println("hello");  
17.        writer.flush();  
18.        System.out.println(reader.readLine());  
19.        s.close();  
20.    }  
21. 
22.} 
package org.bluedash.tryssl;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

public class Client {

 public static void main(String[] args) throws Exception {

  Socket s = new Socket("localhost", 8080);

  PrintWriter writer = new PrintWriter(s.getOutputStream());
  BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
  writer.println("hello");
  writer.flush();
  System.out.println(reader.readLine());
  s.close();
 }

}


客户端也非常简单:向服务端发起请求,发送一个"hello"字串,然后获得服务端的返回。把服务端运行起来后,执行客户端,我们将得到"hello"的返回。

就是这样一套简单的网络通信的代码,我们来把它改造成使用SSL通信。在SSL通信协议中,我们都知道首先服务端必须有一个数字证书,当客户端连接到服务端时,会得到这个证书,然后客户端会判断这个证书是否是可信的,如果是,则交换信道加密密钥,进行通信。如果不信任这个证书,则连接失败。

因此,我们首先要为服务端生成一个数字证书。Java环境下,数字证书是用keytool生成的,这些证书被存储在store的概念中,就是证书仓库。我们来调用keytool命令为服务端生成数字证书和保存它使用的证书仓库:


Bash代码 
1.keytool -genkey -v -alias bluedash-ssl-demo-server -keyalg RSA -keystore ./server_ks -dname "CN=localhost,OU=cn,O=cn,L=cn,ST=cn,C=cn" -storepass server -keypass 123123 
keytool -genkey -v -alias bluedash-ssl-demo-server -keyalg RSA -keystore ./server_ks -dname "CN=localhost,OU=cn,O=cn,L=cn,ST=cn,C=cn" -storepass server -keypass 123123


这样,我们就将服务端证书bluedash-ssl-demo-server保存在了server_ksy这个store文件当中。有关keytool的用法在本文中就不再多赘述。执行上面的命令得到如下结果:


Bash代码 
1.Generating 1,024 bit RSA key pair and self-signed certificate (SHA1withRSA) with a validity of 90 days  
2.        for: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn  
3.[Storing ./server_ks] 
Generating 1,024 bit RSA key pair and self-signed certificate (SHA1withRSA) with a validity of 90 days
        for: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn
[Storing ./server_ks]


然后,改造我们的服务端代码,让服务端使用这个证书,并提供SSL通信:


Java代码 
1.package org.bluedash.tryssl;  
2. 
3.import java.io.BufferedReader;  
4.import java.io.FileInputStream;  
5.import java.io.IOException;  
6.import java.io.InputStreamReader;  
7.import java.io.PrintWriter;  
8.import java.net.ServerSocket;  
9.import java.net.Socket;  
10.import java.security.KeyStore;  
11. 
12.import javax.net.ServerSocketFactory;  
13.import javax.net.ssl.KeyManagerFactory;  
14.import javax.net.ssl.SSLContext;  
15.import javax.net.ssl.SSLServerSocket;  
16. 
17.public class SSLServer extends Thread {  
18.    private Socket socket;  
19. 
20.    public SSLServer(Socket socket) {  
21.        this.socket = socket;  
22.    }  
23. 
24.    public void run() {  
25.        try {  
26.            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));  
27.            PrintWriter writer = new PrintWriter(socket.getOutputStream());  
28. 
29.            String data = reader.readLine();  
30.            writer.println(data);  
31.            writer.close();  
32.            socket.close();  
33.        } catch (IOException e) {  
34. 
35.        }  
36.    }  
37. 
38.    private static String SERVER_KEY_STORE = "/Users/liweinan/projs/ssl/src/main/resources/META-INF/server_ks";  
39.    private static String SERVER_KEY_STORE_PASSWORD = "123123";  
40. 
41.    public static void main(String[] args) throws Exception {  
42.        System.setProperty("javax.net.ssl.trustStore", SERVER_KEY_STORE);  
43.        SSLContext context = SSLContext.getInstance("TLS");  
44.          
45.        KeyStore ks = KeyStore.getInstance("jceks");  
46.        ks.load(new FileInputStream(SERVER_KEY_STORE), null);  
47.        KeyManagerFactory kf = KeyManagerFactory.getInstance("SunX509");  
48.        kf.init(ks, SERVER_KEY_STORE_PASSWORD.toCharArray());  
49.          
50.        context.init(kf.getKeyManagers(), null, null);  
51. 
52.        ServerSocketFactory factory = context.getServerSocketFactory();  
53.        ServerSocket _socket = factory.createServerSocket(8443);  
54.        ((SSLServerSocket) _socket).setNeedClientAuth(false);  
55. 
56.        while (true) {  
57.            new SSLServer(_socket.accept()).start();  
58.        }  
59.    }  
60.} 
package org.bluedash.tryssl;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.KeyStore;

import javax.net.ServerSocketFactory;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;

public class SSLServer extends Thread {
 private Socket socket;

 public SSLServer(Socket socket) {
  this.socket = socket;
 }

 public void run() {
  try {
   BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
   PrintWriter writer = new PrintWriter(socket.getOutputStream());

   String data = reader.readLine();
   writer.println(data);
   writer.close();
   socket.close();
  } catch (IOException e) {

  }
 }

 private static String SERVER_KEY_STORE = "/Users/liweinan/projs/ssl/src/main/resources/META-INF/server_ks";
 private static String SERVER_KEY_STORE_PASSWORD = "123123";

 public static void main(String[] args) throws Exception {
  System.setProperty("javax.net.ssl.trustStore", SERVER_KEY_STORE);
  SSLContext context = SSLContext.getInstance("TLS");
  
  KeyStore ks = KeyStore.getInstance("jceks");
  ks.load(new FileInputStream(SERVER_KEY_STORE), null);
  KeyManagerFactory kf = KeyManagerFactory.getInstance("SunX509");
  kf.init(ks, SERVER_KEY_STORE_PASSWORD.toCharArray());
  
  context.init(kf.getKeyManagers(), null, null);

  ServerSocketFactory factory = context.getServerSocketFactory();
  ServerSocket _socket = factory.createServerSocket(8443);
  ((SSLServerSocket) _socket).setNeedClientAuth(false);

  while (true) {
   new SSLServer(_socket.accept()).start();
  }
 }
}


可以看到,服务端的Socket准备设置工作大大增加了,增加的代码的作用主要是将证书导入并进行使用。此外,所使用的Socket变成了SSLServerSocket,另外端口改到了8443(这个不是强制的,仅仅是为了遵守习惯)。另外,最重要的一点,服务端证书里面的CN一定和服务端的域名统一,我们的证书服务的域名是localhost,那么我们的客户端在连接服务端时一定也要用localhost来连接,否则根据SSL协议标准,域名与证书的CN不匹配,说明这个证书是不安全的,通信将无法正常运行。

有了服务端,我们原来的客户端就不能使用了,必须要走SSL协议。由于服务端的证书是我们自己生成的,没有任何受信任机构的签名,所以客户端是无法验证服务端证书的有效性的,通信必然会失败。所以我们需要为客户端创建一个保存所有信任证书的仓库,然后把服务端证书导进这个仓库。这样,当客户端连接服务端时,会发现服务端的证书在自己的信任列表中,就可以正常通信了。

因此现在我们要做的是生成一个客户端的证书仓库,因为keytool不能仅生成一个空白仓库,所以和服务端一样,我们还是生成一个证书加一个仓库(客户端证书加仓库):


Bash代码 
1.keytool -genkey -v -alias bluedash-ssl-demo-client -keyalg RSA -keystore ./client_ks -dname "CN=localhost,OU=cn,O=cn,L=cn,ST=cn,C=cn" -storepass client -keypass 456456 
keytool -genkey -v -alias bluedash-ssl-demo-client -keyalg RSA -keystore ./client_ks -dname "CN=localhost,OU=cn,O=cn,L=cn,ST=cn,C=cn" -storepass client -keypass 456456


结果如下:


Bash代码 
1.Generating 1,024 bit RSA key pair and self-signed certificate (SHA1withRSA) with a validity of 90 days  
2.        for: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn  
3.[Storing ./client_ks] 
Generating 1,024 bit RSA key pair and self-signed certificate (SHA1withRSA) with a validity of 90 days
        for: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn
[Storing ./client_ks]

接下来,我们要把服务端的证书导出来,并导入到客户端的仓库。第一步是导出服务端的证书:


Bash代码 
1.keytool -export -alias bluedash-ssl-demo-server -keystore ./server_ks -file server_key.cer 
keytool -export -alias bluedash-ssl-demo-server -keystore ./server_ks -file server_key.cer

执行结果如下:


Bash代码 
1.Enter keystore password:  server  
2.Certificate stored in file <server_key.cer> 
Enter keystore password:  server
Certificate stored in file <server_key.cer>

然后是把导出的证书导入到客户端证书仓库:


Bash代码 
1.keytool -import -trustcacerts -alias bluedash-ssl-demo-server -file ./server_key.cer -keystore ./client_ks 
keytool -import -trustcacerts -alias bluedash-ssl-demo-server -file ./server_key.cer -keystore ./client_ks

结果如下:


Bash代码 
1.Enter keystore password:  client  
2.Owner: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn  
3.Issuer: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn  
4.Serial number: 4c57c7de  
5.Valid from: Tue Aug 03 15:40:14 CST 2010 until: Mon Nov 01 15:40:14 CST 2010 
6.Certificate fingerprints:  
7.         MD5:  FC:D4:8B:36:3F:1B:30:EA:6D:63:55:4F:C7:68:3B:0C  
8.         SHA1: E1:54:2F:7C:1A:50:F5:74:AA:63:1E:F9:CC:B1:1C:73:AA:34:8A:C4  
9.         Signature algorithm name: SHA1withRSA  
10.         Version: 3 
11.Trust this certificate? [no]:  yes  
12.Certificate was added to keystore 
Enter keystore password:  client
Owner: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn
Issuer: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn
Serial number: 4c57c7de
Valid from: Tue Aug 03 15:40:14 CST 2010 until: Mon Nov 01 15:40:14 CST 2010
Certificate fingerprints:
         MD5:  FC:D4:8B:36:3F:1B:30:EA:6D:63:55:4F:C7:68:3B:0C
         SHA1: E1:54:2F:7C:1A:50:F5:74:AA:63:1E:F9:CC:B1:1C:73:AA:34:8A:C4
         Signature algorithm name: SHA1withRSA
         Version: 3
Trust this certificate? [no]:  yes
Certificate was added to keystore

好,准备工作做完了,我们来撰写客户端的代码:


Java代码 
1.package org.bluedash.tryssl;  
2. 
3.import java.io.BufferedReader;  
4.import java.io.InputStreamReader;  
5.import java.io.PrintWriter;  
6.import java.net.Socket;  
7. 
8.import javax.net.SocketFactory;  
9.import javax.net.ssl.SSLSocketFactory;  
10. 
11.public class SSLClient {  
12. 
13.    private static String CLIENT_KEY_STORE = "/Users/liweinan/projs/ssl/src/main/resources/META-INF/client_ks";  
14. 
15.    public static void main(String[] args) throws Exception {  
16.        // Set the key store to use for validating the server cert.  
17.        System.setProperty("javax.net.ssl.trustStore", CLIENT_KEY_STORE);  
18.          
19.        System.setProperty("javax.net.debug", "ssl,handshake");  
20. 
21.        SSLClient client = new SSLClient();  
22.        Socket s = client.clientWithoutCert();  
23. 
24.        PrintWriter writer = new PrintWriter(s.getOutputStream());  
25.        BufferedReader reader = new BufferedReader(new InputStreamReader(s  
26.                .getInputStream()));  
27.        writer.println("hello");  
28.        writer.flush();  
29.        System.out.println(reader.readLine());  
30.        s.close();  
31.    }  
32. 
33.    private Socket clientWithoutCert() throws Exception {  
34.        SocketFactory sf = SSLSocketFactory.getDefault();  
35.        Socket s = sf.createSocket("localhost", 8443);  
36.        return s;  
37.    }  
38.} 
package org.bluedash.tryssl;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;

public class SSLClient {

 private static String CLIENT_KEY_STORE = "/Users/liweinan/projs/ssl/src/main/resources/META-INF/client_ks";

 public static void main(String[] args) throws Exception {
  // Set the key store to use for validating the server cert.
  System.setProperty("javax.net.ssl.trustStore", CLIENT_KEY_STORE);
  
  System.setProperty("javax.net.debug", "ssl,handshake");

  SSLClient client = new SSLClient();
  Socket s = client.clientWithoutCert();

  PrintWriter writer = new PrintWriter(s.getOutputStream());
  BufferedReader reader = new BufferedReader(new InputStreamReader(s
    .getInputStream()));
  writer.println("hello");
  writer.flush();
  System.out.println(reader.readLine());
  s.close();
 }

 private Socket clientWithoutCert() throws Exception {
  SocketFactory sf = SSLSocketFactory.getDefault();
  Socket s = sf.createSocket("localhost", 8443);
  return s;
 }
}


可以看到,除了把一些类变成SSL通信类以外,客户端也多出了使用信任证书仓库的代码。以上,我们便完成了SSL单向握手通信。即:客户端验证服务端的证书,服务端不认证客户端的证书。

以上便是Java环境下SSL单向握手的全过程。因为我们在客户端设置了日志输出级别为DEBUG:


Java代码 
1.System.setProperty("javax.net.debug", "ssl,handshake"); 
System.setProperty("javax.net.debug", "ssl,handshake");

因此我们可以看到SSL通信的全过程,这些日志可以帮助我们更具体地了解通过SSL协议建立网络连接时的全过程。

结合日志,我们来看一下SSL双向认证的全过程:

 

第一步: 客户端发送ClientHello消息,发起SSL连接请求,告诉服务器自己支持的SSL选项(加密方式等)。


Bash代码 
1.*** ClientHello, TLSv1 
*** ClientHello, TLSv1


第二步: 服务器响应请求,回复ServerHello消息,和客户端确认SSL加密方式:


Bash代码 
1.*** ServerHello, TLSv1 
*** ServerHello, TLSv1


第三步: 服务端向客户端发布自己的公钥。

第四步: 客户端与服务端的协通沟通完毕,服务端发送ServerHelloDone消息:


Bash代码 
1.*** ServerHelloDone 
*** ServerHelloDone


第五步: 客户端使用服务端给予的公钥,创建会话用密钥(SSL证书认证完成后,为了提高性能,所有的信息交互就可能会使用对称加密算法),并通过ClientKeyExchange消息发给服务器:


Bash代码 
1.*** ClientKeyExchange, RSA PreMasterSecret, TLSv1 
*** ClientKeyExchange, RSA PreMasterSecret, TLSv1


第六步: 客户端通知服务器改变加密算法,通过ChangeCipherSpec消息发给服务端:


Bash代码 
1.main, WRITE: TLSv1 Change Cipher Spec, length = 1 
main, WRITE: TLSv1 Change Cipher Spec, length = 1


第七步: 客户端发送Finished消息,告知服务器请检查加密算法的变更请求:


Bash代码 
1.*** Finished 
*** Finished


第八步:服务端确认算法变更,返回ChangeCipherSpec消息


Bash代码 
1.main, READ: TLSv1 Change Cipher Spec, length = 1 
main, READ: TLSv1 Change Cipher Spec, length = 1


第九步:服务端发送Finished消息,加密算法生效:


Bash代码 
1.*** Finished 
*** Finished


那么如何让服务端也认证客户端的身份,即双向握手呢?其实很简单,在服务端代码中,把这一行:


Java代码 
1.((SSLServerSocket) _socket).setNeedClientAuth(false); 
((SSLServerSocket) _socket).setNeedClientAuth(false);

改成:


Java代码 
1.((SSLServerSocket) _socket).setNeedClientAuth(true); 
((SSLServerSocket) _socket).setNeedClientAuth(true);

就可以了。但是,同样的道理,现在服务端并没有信任客户端的证书,因为客户端的证书也是自己生成的。所以,对于服务端,需要做同样的工作:把客户端的证书导出来,并导入到服务端的证书仓库:


Bash代码 
1.keytool -export -alias bluedash-ssl-demo-client -keystore ./client_ks -file client_key.cer  
2.Enter keystore password:  client  
3.Certificate stored in file <client_key.cer> 
keytool -export -alias bluedash-ssl-demo-client -keystore ./client_ks -file client_key.cer
Enter keystore password:  client
Certificate stored in file <client_key.cer>


Bash代码 
1.keytool -import -trustcacerts -alias bluedash-ssl-demo-client -file ./client_key.cer -keystore ./server_ks  
2.Enter keystore password:  server  
3.Owner: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn  
4.Issuer: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn  
5.Serial number: 4c57c80b  
6.Valid from: Tue Aug 03 15:40:59 CST 2010 until: Mon Nov 01 15:40:59 CST 2010 
7.Certificate fingerprints:  
8.         MD5:  DB:91:F4:1E:65:D1:81:F2:1E:A6:A3:55:3F:E8:12:79 
9.         SHA1: BF:77:56:61:04:DD:95:FC:E5:84:48:5C:BE:60:AF:02:96:A2:E1:E2  
10.         Signature algorithm name: SHA1withRSA  
11.         Version: 3 
12.Trust this certificate? [no]:  yes  
13.Certificate was added to keystore 
keytool -import -trustcacerts -alias bluedash-ssl-demo-client -file ./client_key.cer -keystore ./server_ks
Enter keystore password:  server
Owner: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn
Issuer: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn
Serial number: 4c57c80b
Valid from: Tue Aug 03 15:40:59 CST 2010 until: Mon Nov 01 15:40:59 CST 2010
Certificate fingerprints:
         MD5:  DB:91:F4:1E:65:D1:81:F2:1E:A6:A3:55:3F:E8:12:79
         SHA1: BF:77:56:61:04:DD:95:FC:E5:84:48:5C:BE:60:AF:02:96:A2:E1:E2
         Signature algorithm name: SHA1withRSA
         Version: 3
Trust this certificate? [no]:  yes
Certificate was added to keystore

完成了证书的导入,还要在客户端需要加入一段代码,用于在连接时,客户端向服务端出示自己的证书:


Java代码 
1.package org.bluedash.tryssl;  
2. 
3.import java.io.BufferedReader;  
4.import java.io.FileInputStream;  
5.import java.io.InputStreamReader;  
6.import java.io.PrintWriter;  
7.import java.net.Socket;  
8.import java.security.KeyStore;  
9.import javax.net.SocketFactory;  
10.import javax.net.ssl.KeyManagerFactory;  
11.import javax.net.ssl.SSLContext;  
12.import javax.net.ssl.SSLSocketFactory;  
13. 
14.public class SSLClient {  
15.    private static String CLIENT_KEY_STORE = "/Users/liweinan/projs/ssl/src/main/resources/META-INF/client_ks";  
16.    private static String CLIENT_KEY_STORE_PASSWORD = "456456";  
17.      
18.    public static void main(String[] args) throws Exception {  
19.        // Set the key store to use for validating the server cert.  
20.        System.setProperty("javax.net.ssl.trustStore", CLIENT_KEY_STORE);  
21.        System.setProperty("javax.net.debug", "ssl,handshake");  
22.        SSLClient client = new SSLClient();  
23.        Socket s = client.clientWithCert();  
24.          
25.        PrintWriter writer = new PrintWriter(s.getOutputStream());  
26.        BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));  
27.        writer.println("hello");  
28.        writer.flush();  
29.        System.out.println(reader.readLine());  
30.        s.close();  
31.    }  
32. 
33.    private Socket clientWithoutCert() throws Exception {  
34.        SocketFactory sf = SSLSocketFactory.getDefault();  
35.        Socket s = sf.createSocket("localhost", 8443);  
36.        return s;  
37.    }  
38. 
39.    private Socket clientWithCert() throws Exception {  
40.        SSLContext context = SSLContext.getInstance("TLS");  
41.        KeyStore ks = KeyStore.getInstance("jceks");  
42.          
43.        ks.load(new FileInputStream(CLIENT_KEY_STORE), null);  
44.        KeyManagerFactory kf = KeyManagerFactory.getInstance("SunX509");  
45.        kf.init(ks, CLIENT_KEY_STORE_PASSWORD.toCharArray());  
46.        context.init(kf.getKeyManagers(), null, null);  
47.          
48.        SocketFactory factory = context.getSocketFactory();  
49.        Socket s = factory.createSocket("localhost", 8443);  
50.        return s;  
51.    }  
52.} 
package org.bluedash.tryssl;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.security.KeyStore;
import javax.net.SocketFactory;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;

public class SSLClient {
 private static String CLIENT_KEY_STORE = "/Users/liweinan/projs/ssl/src/main/resources/META-INF/client_ks";
 private static String CLIENT_KEY_STORE_PASSWORD = "456456";
 
 public static void main(String[] args) throws Exception {
  // Set the key store to use for validating the server cert.
  System.setProperty("javax.net.ssl.trustStore", CLIENT_KEY_STORE);
  System.setProperty("javax.net.debug", "ssl,handshake");
  SSLClient client = new SSLClient();
  Socket s = client.clientWithCert();
  
  PrintWriter writer = new PrintWriter(s.getOutputStream());
  BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
  writer.println("hello");
  writer.flush();
  System.out.println(reader.readLine());
  s.close();
 }

 private Socket clientWithoutCert() tbhrows Exception {
  SocketFactory sf = SSLSocketFactory.getDefault();
  Socket s = sf.createSocket("localhost", 8443);
  return s;
 }

 private Socket clientWithCert() throws Exception {
  SSLContext context = SSLContext.getInstance("TLS");
  KeyStore ks = KeyStore.getInstance("jceks");
  
  ks.load(new FileInputStream(CLIENT_KEY_STORE), null);
  KeyManagerFactory kf = KeyManagerFactory.getInstance("SunX509");
  kf.init(ks, CLIENT_KEY_STORE_PASSWORD.toCharArray());
  context.init(kf.getKeyManagers(), null, null);
  
  SocketFactory factory = context.getSocketFactory();
  Socket s = factory.createSocket("localhost", 8443);
  return s;
 }
}

通过比对单向认证的日志输出,我们可以发现双向认证时,多出了服务端认证客户端证书的步骤:


Bash代码 
1.*** CertificateRequest  
2.Cert Types: RSA, DSS  
3.Cert Authorities:  
4.<CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn>  
5.<CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn>  
6.*** ServerHelloDone 
*** CertificateRequest
Cert Types: RSA, DSS
Cert Authorities:
<CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn>
<CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn>
*** ServerHelloDone

 

Bash代码 
1.*** CertificateVerify  
2.main, WRITE: TLSv1 Handshake, length = 134 
3.main, WRITE: TLSv1 Change Cipher Spec, length = 1 
*** CertificateVerify
main, WRITE: TLSv1 Handshake, length = 134
main, WRITE: TLSv1 Change Cipher Spec, length = 1


在 @*** ServerHelloDone@ 之前,服务端向客户端发起了需要证书的请求 @*** CertificateRequest@ 。

在客户端向服务端发出 @Change Cipher Spec@ 请求之前,多了一步客户端证书认证的过程 @*** CertificateVerify@ 。

客户端与服务端互相认证证书的情景

posted @ 2012-07-13 13:50 chen11-1 阅读(3377) | 评论 (0)编辑 收藏

Web 应用测试接口即将规范化 W3C

WebDriver API的首个草案已经交付W3C了,该API提供了一个用于远程控制浏览器的接口,该功能主要用于Web应用程序的自动化测试。
WebDriver API与Web测试框架Selenium 2中的具有同样名称的接口非常相似,其设计的核心是WebElement,这是函数findElement()返回的一个DOM对象。它需要一个用于定位元素的定位器(Locator)。该API允许元素通过XPath、ID、CSS选择器或链接文本被调用。

此外,该API还具有其他方法,允许你读取并设置各种WebElements属性。比如决定使用哪个字符串填充文本框、点击哪个按钮触发tb事件、select元素的实际选择等。

一些类似于Selenium 1中的DOM事件触发功能现在可以通过JavaScript代码在W3C API和Selenium 2中实现。

 

posted @ 2012-07-13 13:48 chen11-1 阅读(778) | 评论 (0)编辑 收藏

Oracle安全:SCN可能最大值与耗尽问题

在2012年第一季度的CPU补丁中,包含了一个关于SCN修正的重要变更,这个补丁提示,在异常情况下,Oracle的SCN可能出现异常增长,使得数据库的一切事务停止,由于SCN不能后退,所以数据库必须重建,才能够重用。

我曾经在以下链接中描述过这个问题:

http://www.eygle.com/archives/2012/03/oracle_scn_bug_exhaused.html

Oracle使用6 Bytes记录SCN,也就是48位,其最大值是:


SQL> col scn for 999,999,999,999,999,999  SQL> select power(2,48) scn from dual;  SCN  ------------------------  281,474,976,710,656

Oracle在内部控制每秒增减的SCN不超过 16K,按照这样计算,这个数值可以使用大约544年:


SQL> select power(2,48) / 16 / 1024 / 3600 / 24 / 365 from dual;  POWER(2,48)/16/1024/3600/24/365  -------------------------------  544.770078

然而在出现异常时,尤其是当使用DB Link跨数据库查询时,SCN会被同步,在以下链接中,我曾经描述过此问题:

http://www.eygle.com/archives/2006/11/db_link_checkpoint_scn.html

一个数据库当前最大的可能SCN被称为"最大合理SCN",该值可以通过如下方式计算:

col scn for 999,999,999,999,999,999  select (  (   (  (  (  (  to_char(sysdate,'YYYY')-1988  )*12+  to_char(sysdate,'mm')-1  )*31+to_char(sysdate,'dd')-1  )*24+to_char(sysdate,'hh24')  )*60+to_char(sysdate,'mi')  )*60+to_char(sysdate,'ss')  ) * to_number('ffff','XXXXXXXX')/4 scn  from dual  / 这个算法即SCN算法,以1988年1月1日 00点00时00分开始,每秒计算1个点数,最大SCN为16K。

这个内容可以参考如下链接:

http://www.eygle.com/archives/2006/01/how_big_scn_can_be.html

在CPU补丁中,Oracle提供了一个脚本 scnhealthcheck.sql 用于检查数据库当前SCN的剩余情况。

该脚本的算法和以上描述相同,最终将最大合理SCN 减去当前数据库SCN,计算得出一个指标:HeadRoom。也就是SCN尚余的顶部空间,这个顶部空间最后折合成天数:

以下是这个脚本的内容:

Rem  Rem $Header: rdbms/admin/scnhealthcheck.sql st_server_tbhukya_bug-13498243/8 2012/01/17 03:37:18 tbhukya Exp $  Rem  Rem scnhealthcheck.sql  Rem  Rem Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.  Rem  Rem NAME Rem scnhealthcheck.sql - Scn Health check Rem  Rem DESCRIPTION  Rem Checks scn health of a DB  Rem  Rem NOTES  Rem .  Rem  Rem MODIFIED (MM/DD/YY)  Rem tbhukya 01/11/12 - Created  Rem  Rem  define LOWTHRESHOLD=10  define MIDTHRESHOLD=62  define VERBOSE=FALSE set veri off;  set feedback off;  set serverout on DECLARE verbose boolean:=&&VERBOSE;  BEGIN For C in (  select version,  date_time,   dbms_flashback.get_system_change_number current_scn,  indicator  from (  select version,  to_char(SYSDATE,'YYYY/MM/DD HH24:MI:SS') DATE_TIME,  ((((  ((to_number(to_char(sysdate,'YYYY'))-1988)*12*31*24*60*60) +  ((to_number(to_char(sysdate,'MM'))-1)*31*24*60*60) +  (((to_number(to_char(sysdate,'DD'))-1))*24*60*60) +  (to_number(to_char(sysdate,'HH24'))*60*60) +  (to_number(to_char(sysdate,'MI'))*60) +  (to_number(to_char(sysdate,'SS')))  ) * (16*1024)) - dbms_flashback.get_system_change_number)  / (16*1024*60*60*24)  ) indicator  from v$instance  )  ) LOOP  dbms_output.put_line( '-----------------------------------------------------' || '---------' );  dbms_output.put_line( 'ScnHealthCheck' );  dbms_output.put_line( '-----------------------------------------------------' || '---------' );  dbms_output.put_line( 'Current Date: '||C.date_time );  dbms_output.put_line( 'Current SCN: '||C.current_scn );  if (verbose) then dbms_output.put_line( 'SCN Headroom: '||round(C.indicator,2) );  end if;  dbms_output.put_line( 'Version: '||C.version );  dbms_output.put_line( '-----------------------------------------------------' || '---------' );  IF C.version > '10.2.0.5.0' and C.version NOT LIKE '9.2%' THEN IF C.indicator>&MIDTHRESHOLD THEN dbms_output.put_line('Result: A - SCN Headroom is good');  dbms_output.put_line('Apply the latest recommended patches');  dbms_output.put_line('based on your maintenance schedule');  IF (C.version < '11.2.0.2') THEN dbms_output.put_line('AND set _external_scn_rejection_threshold_hours=' || '24 after apply.');  END IF;  ELSIF C.indicator<=&LOWTHRESHOLD THEN dbms_output.put_line('Result: C - SCN Headroom is low');  dbms_output.put_line('If you have not already done so apply' );  dbms_output.put_line('the latest recommended patches right now' );  IF (C.version < '11.2.0.2') THEN dbms_output.put_line('set _external_scn_rejection_threshold_hours=24 ' || 'after apply');  END IF;  dbms_output.put_line('AND contact Oracle support immediately.' );   ELSE dbms_output.put_line('Result: B - SCN Headroom is low');  dbms_output.put_line('If you have not already done so apply' );  dbms_output.put_line('the latest recommended patches right now');  IF (C.version < '11.2.0.2') THEN dbms_output.put_line('AND set _external_scn_rejection_threshold_hours=' ||'24 after apply.');  END IF;  END IF;  ELSE IF C.indicator<=&MIDTHRESHOLD THEN dbms_output.put_line('Result: C - SCN Headroom is low');  dbms_output.put_line('If you have not already done so apply' );  dbms_output.put_line('the latest recommended patches right now' );  IF (C.version >= '10.1.0.5.0' and C.version <= '10.2.0.5.0' and C.version NOT LIKE '9.2%') THEN dbms_output.put_line(', set _external_scn_rejection_threshold_hours=24' || ' after apply');  END IF;  dbms_output.put_line('AND contact Oracle support immediately.' );  ELSE dbms_output.put_line('Result: A - SCN Headroom is good');  dbms_output.put_line('Apply the latest recommended patches');  dbms_output.put_line('based on your maintenance schedule ');  IF (C.version >= '10.1.0.5.0' and C.version <= '10.2.0.5.0' and C.version NOT LIKE '9.2%') THEN dbms_output.put_line('AND set _external_scn_rejection_threshold_hours=24' || ' after apply.');  END IF;  END IF;  END IF;  dbms_output.put_line(  'For further information review MOS document id 1393363.1');  dbms_output.put_line( '-----------------------------------------------------' || '---------' );  END LOOP;  end;  / 在应用补丁之后,一个新的隐含参数 _external_scn_rejection_threshold_hours 引入,通常设置该参数为 24 小时:

_external_scn_rejection_threshold_hours=24

这个设置降低了SCN Headroom的顶部空间,以前缺省的设置容量至少为31天,降低为 24 小时,tb可以增大SCN允许增长的合理空间。

但是如果不加控制,SCN仍然可能会超过最大的合理范围,导致数据库问题。

这个问题的影响会极其严重,我们建议用户检验当前数据库的SCN使用情况,以下是检查脚本的输出范例:

--------------------------------------  ScnHealthCheck  --------------------------------------  Current Date: 2012/01/15 14:17:49   Current SCN: 13194140054241  Version: 11.2.0.2.0  --------------------------------------  Result: C - SCN Headroom is low  If you have not already done so apply  the latest recommended patches right now  AND contact Oracle support immediately.  For further information review MOS document id 1393363.  -------------------------------------- 这个问题已经出现在客户环境中,需要引起大家的足够重视。

 

posted @ 2012-07-10 11:12 chen11-1 阅读(1015) | 评论 (1)编辑 收藏

详解Oracle数据库优化方案与实践

在这里我们将介绍Oracle数据库优化方案与实践,不同的环境会有不同的调试,但是也会有差别,希望大家能合理的吸收。

一、前言

二、ORACLE数据库优化概述

1、内存等参数配置的优化

2、减少物理读写的优化

3、批量重复操作的SQL语句及大表操作的优化

二、ORACLE数据库优化方案

1、内存等Oracle系统参数配置

2、使用索引

3、表分区

4、Procedure优化

5、其他改造

6、维护作业计划

三、ORACLE数据库优化前后比较

1、批量重复的SQL语句执行性能

2、一些单次、不常用的操作的语句执行性能

四、参考

1、常用的优化工具

2、参考文献

一、前言

随着实际项目的启动,实际项目中使用的 Oracle数据库经过一段时间的运行,在线保存的数据量和业务处理的数据量在逐渐增大,最初的Oracle设置,与现在实际需要的运行性能有一定差距,需要进行一些优化调整。

本文将结合本人实际维护经验,相应地提出实际项目数据处理的一些优化方法,以供参考。

适用于Oracle 9i。

二、Oracle数据库优化概述

Oracle数据库的优化,针对不同的应用,会有侧重点不同的优化方法,根据我们实际项目的应用特点,我们主要关心的是每次事务执行完成的时间长短。

从Oracle数据库本身的特点,我们可以把优化工作划分为初始优化设置,微优化。

在初始优化设置时,我们只能根据硬件情况,估计业务运行的情况,综合经验,给出一种经验设置,大体上来说,这种经验设置离满足优化需求的目标不是很远。在完成了初始优化设置后,经过一段时间的业务运行,已可开始收集实际运行环境的性能数据,此时,就可以对各种Oracle性能指标、各种关心的事务操作进行性能评估,然后进行微优化了。

Oracle优化,不是一个一蹴而就的工作,也不是一个一劳永逸的工作,需要定期维护,tb定期观察,在发现性能瓶颈时及时进行调整。Oracle总是存在性能瓶颈的,不使用、不操作的数据库总是最快的,在解决当前瓶颈后,总是会有另一个瓶颈出现,所以在优化前,我们需要确定一个优化目标,我们的目标是满足我们的应用性能要求就可以了。

Oracle优化,涉及的范围太广泛,包含的有主机性能,内存使用性能,网络传输性能,SQL语句执行性能等等,从我们面向网管来说,满足事务执行速度性能主要表现在:

1)批量重复的SQL语句执行性能(主要是通过Procedure计算完成数据合并和数据汇总的性能和批量数据采集入库的性能);

2)一些单次、不常用的操作的语句执行性能(主要是GUI的非规律操作)。

根据这两个特点,我们可把优化方法归纳到3个重要方向:

1)内存等参数配置的优化。内存优化,是性能受益最快的地方。

2)减少物理读写的优化。内存逻辑I/O操作的时间,远远小于物理I/O的操作时间。

3)批量重复操作的SQL语句及大表操作的优化。减少SQL执行次数,减少大表操作次数。

下面主要针对得益最大的这三个方向的优化进行阐述。

1、内存等参数配置的优化

对于大多数应用来说,最直接、最快速得到优化收益的,肯定属于内存的优化。给每个Oracle内存块分配合理的大小,可以有效的使用数据库。通过观察各种数据库活动在内存里的命中率,执行情况,我们能很快的掌握数据库的主要瓶颈。我们从下面的一条SQL语句的执行步骤就可知道。

一个SQL语句,从发布到执行,会按顺序经历如下几个步骤:

1)Oracle把该SQL的字符转换成它们的ASCII等效数字码。

2)该ASCII数字码被传送给一个散列算法,生成一个散列值。

3)用户server process查看该散列值是否在shared pool内存块中存在。

若存在:

4)使用shared pool中缓存的版本来执行。

若不存在:

4)检查该语句的语义正确性。

5)执行对象解析(这期间对照数据字典,检查被引用的对象的名称和结构的正确性)。

6)检查数据字典,收集该操作所引用的所有对象的相关统计数据。

7)准备执行计划,从可用的执行计划中选择一个执行计划。(包括对stored outline和materialized view的相关使用的决定)

8)检查数据字典,确定所引用对象的安全性。

9)生成一个编译代码(P-CODE)。

10)执行。

这里,通过内存的合理分配,参数的合理设置,我们主要解决:

1)减少执行到第五步的可能,节约SQL语句解析的时间。第五步以后的执行过程,是一个很消耗资源的操作过程。

2)通过内存配置,尽可能让SQL语句所做的操作和操作的数据都在内存里完成。大家都知道,从内存读取数据的速度,要远远快于从物理硬盘上读数据,一次内存排序要比硬盘排序快很多倍。

3)根据数据库内存活动,减少每个内存块活动的响应时间,充分利用每个内存块,减少内存latch争用发生的次数。

2、减少物理读写的优化

无论如何配置Oracle数据库,我们的网管系统,每小时周期性的都会有新数据被处理,就会发生物理读写,这是避免不了的。

减少物理读写的优化,一般所用的方法有:

1) 增加内存data buffer的大小,尽可能让数据库操作的数据都能在内存里找到,不需要进行物理读写操作。

2) 通过使用索引,避免不必要的全表扫描。

3) 大表物理分区,Oracle具有很好的分区识别功能,减少数据扫描范围。

上述3个方法,是从整体上改善数据库物理I/O性能最明显的3个方法。能非常快速的减少数据库在物理I/O,最直接的反应是数据库事务执行时间能能以数量级为单位减少。其他的一些减少物理读写的优化方法,比如使用materialized view,Cluster等方法;还有一些分散I/O的方法,比如 Oracle日志文件不与数据文件放在一个物理硬盘,数据热点文件物理I/O分开等等方法,就目前我们的网管系统而言,能得到的效果不是很明显,在网管系统中,为了不增加数据库维护的复杂性,不推荐使用。

3、批量重复操作的SQL语句及大表操作的优化

批量重复执行的SQL语句,一般出现在每个周期时间内的数据批量入库的insert语句,和数据合并、汇总的周期性select、delete、insert操作。

我们需要注意以下几点:

1) 减少不必要的SQL语句执行和SQL语句的执行次数。

每条SQL语句执行,都会消费系统资源,都有执行时间。减少不必要的SQL语句执行和减少SQL语句的执行次数,自然能减少业务执行时间。需要根据业务流程,重新设计数据处理的代码。此方法主要适用于procedure执行的数据合并、汇总。

2) 这些SQL语句,由于每个SQL语句都要执行很多次,应该尽量让该SQL的散列值在shared pool内存块中存在。也就是使用动态SQL,避免SQL硬解析。

可通过Oracle参数的设置,和动态SQL语句的应用,通过绑定变量的方式,减少SQL语句的解析次数。

3)减少大表的操作,确保在一次事务中,同类操作只对大表执行一次。主要在数据合并和数据汇总的pprocedure和数据采集时出现

三、Oracle数据库优化方案

1、内存等Oracle系统参数配置

Oracle 的parameter参数,分动态参数和静态参数,静态参数需要重新启动数据库才能生效,动态参数不需要重新启动数据库即可生效。

Oracle 9i可以使用spfile的特性,使用alter system set 参数名=参数值 scope=both[spfile];的方法进行修改。也可以直接修改pfile。

以下给出了网管Oracle 数据库重点关注的parameter的初始优化设置。

最大可使用的内存SGA总和

静态参数sga_max_size=物理内存的大小减1.5G

Shared pool

动态参数shared_pool_size= 600 ~ 800 M

静态参数shared_pool_reserved_size= 300 M

动态参数open_cursors= 400 ~ 600

静态参数cursor_space_for_time= TRUE

静态参数session_cached_cursors= 60 ~ 100

动态参数cursor_sharing= SIMILAR

Data buffer

动态参数db_cache_advice= READY

动态参数db_cache_size

动态参数Db_keep_cache_size

动态参数db_recycle_cache_size

(sga_max_size大小,除了分配给所有非data buffer的size,都分配给data buffer)

Sga other memory

动态参数large_pool_size= 50 M

静态参数java_pool_size= 100 M

动态参数log_buffer= 3 M

Other memory

动态参数sort_area_size= 3 M

静态参数sort_area_retained_size= 0.5 M

静态参数pga_aggregate_target= 800 M

动态参数workarea_size_policy= AUTO

磁盘I/O配置

静态参数sql_trace= FALSE

动态参数timed_statistics= true

动态参数db_file_multiblock_read_count= 16

静态参数dbwr_io_slaves= 0

静态参数db_writer_processes= 3

静态参数undo_management= AUTO

动态参数undo_retention= 7200

2、使用索引

我们初步定义,表数据超过1000行的表,我们都要求使用索引。(不区分事务操作的数据在表数据中所占的比例)

索引所包含的字段不超过4个。

检查SQL语句是否使用了索引,我们使用execute plan来看,获得explain的方法,我们通过SQL*PLUS工具,使用如下命令进行查看:

setautotraceonsetautotracetraceonlyexplain settimingon或通过SQL*PLUS trace,然后查看user_dump_dest下的跟踪文件,使用tkprof工具格式化后阅览。

altersessionsetevents'10046tracenamecontextforever,level12'; altersessionsetevents'10046tracenamecontextoff'; SELECTp.spid,s.usernameFROMv$sessions,v$processpWHEREs.audsid=USERENV('sessionid')ANDs.paddr=p.addr;3、表分区

在网管数据库里,比较突出的大表有小区表和告警表。

性能表,使用范围分区。

以时间点start_time为范围分区字段。

告警表,使用range-hash的混合分区和范围分区。

范围分区以时间点starttime为分区字段,混合分区增加ALARMNUMBER为字段的hash子分区。

同时,创建本地分区索引。

4、Procedure优化

1)取消地市一级的Procedure,只保留其上层调用Procedure,并保持参数输入方法,调用方法不变。

2)确保大表数据查询操作只有1次,确保大表数据删除只有一次。

3)确保单条SQL语句执行已优化。

4)减少SQL执行次数。

5、其他改造

修改表存储参数,提前预先分配extents。

修改表空间存储参数(采集表空间所用块设置为大块,比如32k一个块;修改ptcfree,pctused,pctincrease等)。

避免使用唯一索引和非空约束。

创建合理的索引。

各模块SQL语句优化,比如使用提示固定索引等。

确认每一条历史数据删除语句已优化和删除方法。

临时表的使用。

6、维护作业计划

表分析(包含确定具体的表的分析方法,分区表分析方法,索引分析方法)。

空间回收维护(包括确定HWM,回收多余分配给表的块,合并数据块碎片等)。

索引维护(包括定期重建索引,索引使用情况监视等)。

历史数据删除检查(检查保存的数据是否符合要求,检查历史数据删除方法是否正确-比如批量删除提交的方法等)。

全库性能分析和问题报告及优化(比如使用statspack进行性能趋势分析,检查有问题的SQL或事务,确定当前系统等待的top 5事件等等)。

表数据keep,default及reclye(比如把一些常用的配置表固定在内存里等)。

数据库参数核查(防止数据库参数被修改,定期对系统配置参数进行比较)。

日志文件分析(定期检查Oracle生成的日志文件,定期备份、删除)。

硬盘空间维护(定期对Oracle 对象使用的空间情况进行监视)。

四,Oracle数据库优化前后比较

1、批量重复的SQL语句执行性能

根据网元数量,各地的执行的完成时间有所区别。

用于数据合并和汇总的Procedure的计算性能

通过statspack的周期性采集数据,我们可以使用以下语句,计算我们想统计的Procedure的执行情况:

SELECTTO_CHAR(sn.snap_time,'yyyy-mm-ddhh24:mi:ss')ASsnap_time,s.disk_reads, s.buffer_gets,s.elapsed_time/1000000ASelapsedtime FROM(SELECThash_value,sql_text,address,last_snap_id FROMSTATS$SQLTEXTWHEREpiece=0ANDsql_textLIKE'%&sqltext_key%')t, (SELECTaddress,hash_value,snap_id,sql_text,disk_reads,executions, buffer_gets,rows_processed,elapsed_time FROMSTATS$SQL_SUMMARY)s,STATS$SNAPSHOTsn WHEREs.hash_value=t.hash_value ANDs.address=t.address ANDs.snap_id=t.last_snap_id ANDsn.snap_id=s.snap_id;比如,我们以perfstat用户执行该SQL,输入“to_comp”,可以观察到数据库里保存的有的to_comp存储过程的执行时间,我们发现,其执行时间,从优化前的几千秒,最后稳定在优化后的几十秒。

注:to_comp是整体调用执行一次所有网元的数据合并和汇总的procedure。

用于小区分析数据的Procedure的计算性能

使用上面的方法,我们一样可以知道,小区分析的procedure执行,从优化前的约几千秒,最后稳定在优化后的几十秒。

批量数据采集入库性能

使用bcp,能从以前约15分钟,减少到约4分钟。

2、一些单次、不常用的操作的语句执行性能

GUI上的性能数据查询,告警数据查询,响应时间都极快,几乎不再出现长时间等待响应的情况。

五,参考

常用的优化工具

statspack

sql*plus

TOAD

 

posted @ 2012-07-10 11:10 chen11-1 阅读(1792) | 评论 (1)编辑 收藏

Oracle容灾方案的选择

容灾方案的选择
---- 容灾首先是一个概念,要认识到为什么做容灾,才能做好容灾。世界上没有卖后悔药的,当灾难降临了,如果没有行之有效的数据保护、数据恢复的容灾措施,带来不可预估的损失将是无法避免的。类似电信行业、金融行业,证券行业也是如此,动辄涉及数以百亿计的资金、涉及庞大的客户量,在系统数据的准确、业务的连续、关键业务的不中断等方面更是不容出现任何的差错。
 
----目前,业界具有容灾功能的常用解决方案主要包括以下几类:磁盘阵列复制技术,主要由一些磁盘阵列厂商提供,如EMC SRDF、IBM PPRC 、HP BusinessCopy、HDS TrueCopy、tb等;存储卷复制技术,由一些卷管理软件厂商提供,如VERITAS VVR;数据库复制技术,由数据库厂商以及一些第三方厂商提供,如DSG RealSync,Quest SharePlex等;应用层复制技术,由各系统的应用厂商自己提供。
 
----磁盘阵列复制技术主要适用于数据中心级的海量数据复制,此技术用户必需采用支持该功能的磁盘阵列型号,而这些阵列大都为高端阵列,投资非常昂贵。并且,由于政府行业用户的带宽有限,而磁盘阵列复制技术对带宽的要求又相对很高,动辄需要上GB的带宽。此外,采用磁盘阵列复制技术,其目标端无法提供实时数据查询,由于目标端数据库在复制过程中不能被打开,难于实现交易与查询的分离,同时也造成大量投资浪费。因此,磁盘阵列复制技术无法满足某些行业集中交易系统的容灾需求,使得这些用户难以选择此种解决方案。
 
----存储卷复制技术主要适用于工作组级的数据复制,它对CPU资源占用高。同样由于目标端数据无法提供实时数据查询和对带宽的要求高,使得证券等行业用户也难以选择。
 
----而应用层复制技术只适合那些在应用中提供了该技术的应用,由于它的非标准化、开发和维护工作量大,使得其应用不成熟也不普遍。关键行业对数据的可靠性要求又非常之高,使得关键行业用户也不敢冒然选择此种复制技术。
 
----DSG RealSync属于数据库复制技术,它适用于从工作组级、企业级到数据中心级的复制需求,无论系统采用什么样的服务器平台、什么样的存储平台,只要是ORACLE系统之间的复制即可适用。采用DSG RealSync复制技术,其目标端数据库在复制过程中处于可用状态,帮助关键行业用户实现生产系统与查询统计报表系统的分离;其源端系统和目标端系统可以采用异构的操作系统平台、存储平台;支持选择性复制,即支持只复制指定的user、指定的Table、指定的行和列,从而节省存储空间,提高应用灵活性;支持1对多,多对1的复制结构,即:能够将多个数据库中的数据复制到一个数据库中,tb能够将一个数据库中的不同数据分发到不同的数据库中;也节约带宽和网络资源,其所需带宽一般在几Mbps,几十Mbps。
 
----随着用户容灾意识的逐渐增强,关键行业也提出了建设一套高效、可靠、投资回收比高的灾难备份系统的需求,以确保系统的数据安全和灾难发生时数据的快速恢复。  
 

posted @ 2012-07-10 11:08 chen11-1 阅读(1189) | 评论 (0)编辑 收藏

仅列出标题
共20页: First 上一页 11 12 13 14 15 16 17 18 19 下一页 Last