<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    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 閱讀(1050) | 評論 (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 閱讀(1045) | 評論 (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 閱讀(867) | 評論 (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 閱讀(888) | 評論 (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 閱讀(1457) | 評論 (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 閱讀(3379) | 評論 (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 閱讀(781) | 評論 (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 閱讀(1018) | 評論 (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 閱讀(1795) | 評論 (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 閱讀(1194) | 評論 (0)編輯 收藏

    僅列出標題
    共20頁: First 上一頁 11 12 13 14 15 16 17 18 19 下一頁 Last 
    主站蜘蛛池模板: 亚洲精品精华液一区二区| 国产免费福利体检区久久| 国产免费观看青青草原网站| 美女露100%胸无遮挡免费观看| 久久夜色精品国产亚洲| 97无码免费人妻超级碰碰碰碰| 一个人看的在线免费视频| 亚洲视频一区网站| 四虎永久免费地址在线网站| 91禁漫免费进入| 国产成人综合久久精品亚洲| 在线观看亚洲人成网站| 免费国产在线观看| aⅴ在线免费观看| 久久久久国色AV免费观看| 亚洲av无码不卡久久| 亚洲伊人久久精品影院| 免费观看大片毛片| 88av免费观看| eeuss影院免费直达入口| 中文字幕亚洲男人的天堂网络 | 国产日韩精品无码区免费专区国产 | 亚洲AV无码一区二区一二区| 亚洲AV无码专区亚洲AV伊甸园| 免费理论片51人人看电影| 日韩精品无码专区免费播放| 免费人成再在线观看网站| 亚洲精品伊人久久久久 | 亚洲天堂免费在线视频| 国产亚洲精aa在线看| 亚洲第一中文字幕| 亚洲日韩在线观看免费视频| 成人免费a级毛片无码网站入口| 中国内地毛片免费高清| 黄色毛片免费在线观看| 伊人久久五月丁香综合中文亚洲| 亚洲AV无码码潮喷在线观看| 亚洲成AV人网址| 日韩中文无码有码免费视频 | 污视频网站在线免费看| 亚洲乱码无人区卡1卡2卡3|