Oracle中序列(Sequence)詳解


一 序列定義

 

序列(SEQUENCE)是序列號生成器,可以為表中的行自動生成序列號,產生一組等間隔的數值(類型為數字)。不占用磁盤空間,占用內存。

其主要用途是生成表的主鍵值,可以在插入語句中引用,也可以通過查詢檢查當前值,或使序列增至下一個值。

 

二 創建序列

 

創建序列需要CREATE SEQUENCE系統權限。序列的創建語法如下:

  CREATE SEQUENCE 序列名

  [INCREMENT BY n]

  [START WITH n]

  [{MAXVALUE/ MINVALUE n| NOMAXVALUE}]

  [{CYCLE|NOCYCLE}]

  [{CACHE n| NOCACHE}];

    其中:

1)  INCREMENT BY用於定義序列的步長,如果省略,則默認為1,如果出現負值,則代表Oracle序列的值是按照此步長遞減的。

2)  START WITH 定義序列的初始值(即產生的第一個值),默認為1。

3)  MAXVALUE 定義序列生成器能產生的最大值。選項NOMAXVALUE是默認選項,代表沒有最大值定義,這時對於遞增Oracle序列,系統能夠產生的最大值是10的27次方;對於遞減序列,最大值是-1。

4)  MINVALUE定義序列生成器能產生的最小值。選項NOMAXVALUE是默認選項,代表沒有最小值定義,這時對於遞減序列,系統能夠產生的最小值是?10的26次方;對於遞增序列,最小值是1。

5)  CYCLE和NOCYCLE 表示當序列生成器的值達到限制值后是否循環。CYCLE代表循環,NOCYCLE代表不循環。如果循環,則當遞增序列達到最大值時,循環到最小值;對於遞減序列達到最小值時,循環到最大值。如果不循環,達到限制值后,繼續產生新值就會發生錯誤。

6)  CACHE(緩沖)定義存放序列的內存塊的大小,默認為20。NOCACHE表示不對序列進行內存緩沖。對序列進行內存緩沖,可以改善序列的性能。

大量語句發生請求,申請序列時,為了避免序列在運用層實現序列而引起的性能瓶頸。Oracle序列允許將序列提前生成 cache x個先存入內存,在發生大量申請序列語句時,可直接到運行最快的內存中去得到序列。但cache個數也不能設置太大,因為在數據庫重啟時,會清空內存信息,預存在內存中的序列會丟失,當數據庫再次啟動后,序列從上次內存中最大的序列號+1 開始存入cache x個。這種情況也能會在數據庫關閉時也會導致序號不連續。

7)  NEXTVAL 返回序列中下一個有效的值,任何用戶都可以引用。

8)  CURRVAL 中存放序列的當前值,NEXTVAL 應在 CURRVAL 之前指定 ,二者應同時有效。

 

例子:

SQL> create sequence t1_seq increment by 1 start with 1;

SYS@orclasm > drop sequence t1_seq;


Sequence dropped.


SYS@orclasm > create sequence t1_seq increment by -1 start with  -5;


Sequence created.


SYS@orclasm > select t1_seq.currval,t1_seq.nextval from dual;


   CURRVAL    NEXTVAL
---------- ----------
        -5         -5


SYS@orclasm > select t1_seq.currval,t1_seq.nextval from dual;


   CURRVAL    NEXTVAL
---------- ----------
        -6         -6


SYS@orclasm > select t1_seq.currval,t1_seq.nextval from dual;


   CURRVAL    NEXTVAL
---------- ----------
        -7         -7

 

 

三 使用序列

 

調用NEXTVAL將生成序列中的下一個序列號,調用時要指出序列名,即用以下方式調用: 序

列名.NEXTVAL

CURRVAL用於產生序列的當前值,無論調用多少次都不會產生序列的下一個值。如果序列還

沒有通過調用NEXTVAL產生過序列的下一個值,先引用CURRVAL沒有意義。調用CURRVAL

的方法同上,要指出序列名,即用以下方式調用:序列名.CURRVAL

SQL> create table t1(id number,qq number,ww number);

SQL> insert into t1 values(t1_seq.nextval,1,1);

1 row created.

SQL> /

1 row created.

SQL> /

 

SQL> /

SQL> /

1 row created.

 

SQL> select * from t1;

 

        ID         QQ         WW

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

         1          1          1

         2          1          1

         3          1          1

         4          1          1

         5          1          1

 

SQL> select t1_seq.currval from dual;

 

   CURRVAL

----------

         5

 

SQL> select t1_seq.nextval from dual;

 

   NEXTVAL

----------

         6

 

SQL> select t1_seq.nextval from dual;

 

   NEXTVAL

----------

         7

 

 

 

四 修改序列

 

修改序列的注意事項:

1  必須是序列的擁有者或對序列有 ALTER any sequence權限

2  只有將來的序列值會被改變

3  改變序列的初始值只能通過刪除序列之后重建序列的方法實現

 

Alter sequence示例:

ALTER SEQUENCE emp_sequence INCREMENT BY 10 MAXVALUE 10000 CYCLE -- 到10000后從頭開始 NOCACHE ;

 

 可以影響Sequence的初始化參數:

 SEQUENCE_CACHE_ENTRIES =設置能同時被cache的sequence數目。

SQL> alter sequence t1_seq increment by 2 maxvalue 10 cycle;

alter sequence t1_seq increment by 2 maxvalue 10 cycle

*

ERROR at line 1:

ORA-04013: number to CACHE must be less than one cycle

 

SQL> alter sequence t1_seq increment by 2 maxvalue 40 cycle;

Sequence altered.

 

SQL> insert into t1 values(t1_seq.nextval,1,1);

/

/

……….

/

SQL> select * from t1;

        ID         QQ         WW

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

         1          1          1

         2          1          1

         3          1          1

         4          1          1

         5          1          1

         9          1          1

        11          1          1

        13          1          1

        15          1          1

        17          1          1

       ………………………………………………..

        37          1          1

        39          1          1

         1          1          1

         3          1          1

         5          1          1

 

24 rows selected.

 

 

 

五 查詢序列

 

1  通過數據字典USER_OBJECTS可以查看用戶擁有的序列。

2  通過數據字典USER_SEQUENCES可以查看序列的設置。

  例:查看用戶的序列:

SELECT SEQUENCE_NAME,MIN_VALUE,MAX_VALUE,INCREMENT_BY,LAST_NUMBER FROM USER_SEQUENCES;

 

  執行結果:

  

SEQUENCE_NAME MIN_VALUE MAX_VALUE INCREMENT_BY LAST_NUMBER

 

 

SQL> select object_name,object_type from user_objects;

 

OBJECT_NAM   OBJECT_TYPE

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

T1_SEQ        SEQUENCE

T1            TABLE

 

SQL> select sequence_name,increment_by,cycle_flag,cache_size from user_sequences;

SEQUENCE_N  INCREMENT_BY  CYCLE_FLAG   CACHE_SIZE

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

T1_SEQ            2             Y            20

 

 

六 刪除序列

 

SQL> drop sequence t1_seq;

 

 


 



序列作為主鍵使用的原理、優缺點討論 

這幾天和同事一直在討論關於表設計中主鍵選擇的問題,用sequence作為主鍵究竟有什么好處,又有什么缺點,尤其是有些事務場景上下文需要用到創建的序列值,如何用?其實我想說的是,可能只是一個很簡單的概念,可能深入理解,還是有很多未知的知識,當然也就可能會有一些容易忽略但又可能很關鍵的坑,只有碰了才知道。。。
以下是總結摘要,如有疏漏,還請過路的各位大俠賜教。

 

 
1、首先說下seq.nextval主要有以下兩種使用場景:
(1). 如果一個事務中只是INSERT時需要序列,其他地方不會需要這個序列,那么只需要在INSERT ... VALUES (seq.nextval ...)語句中使用即可。
(2). 如果一個事務中INSERT一張表后,還需要插入時的主鍵ID值,作為外鍵插入其他表,那么就需要在INSERT第一張表前使用select seq.nextval from dual提前獲取可用的ID保存到一個變量中,為后面使用。

2、其次可以簡單說下調用序列的原理,只有理解了序列的原理,才能有助於我們知道如何正確使用序列。
使用序列時Oracle內部大體是按照如下步驟進行:
(1). 一個序列會被定義到Oracle內部的一張數據字典表(seq$)的一行。
(2). 第一次使用序列,序列的起始值會加上緩存大小,然后更新回行。
(3). Oracle內部會自動跟蹤內存中的兩個值,當前值和目標值。
(4). 每次有回話調用seq.nextval,Oracle會遞增當前值,然后檢查是否超過了目標值,再返回結果。
(5). 如果當前值和目標值相同,Oracle會更新數據字典表中的行,為目標值加上緩存大小,同時內存中產生了一個新的目標值。
例如create sequence seq cache 20;這樣一序列。名稱為seq的序列,緩存大小是20,默認初始值是1,步長默認是1。

當使用了一次seq.nextval后,可以看HIGHWATER字段值為21,即目標值1+緩存大小20=21。

當執行20次后,seq.nextval值變為21,此時HIGHWATER字段值是41,即目標值21+緩存大小20=41。

也就是每調用seq.nextval值20次,會更新一次seq$表,那么問題來了,如果cache值較小,且序列使用的頻率較高,那么會對seq$表有頻繁的更新操作,日志量會增加,尤其在RAC下,更新該行的時候,該數據塊會在節點間不停的傳送,就會產生可能的爭用,這種問題會被放大。因此為了減少這種情況,我們可以將cache緩存值設置大一些,例如1000,減少對字典表的更新。
序列還有一個問題,就是cache緩存是實例級的,對於RAC,比如第一個節點使用序列時會分配1-20,第二個節點會被分配21-40,Oracle保證不會重復,但若節點crash了,比如節點1壞了,那么序列就會出現斷號,節點1再次使用時,只會從41-60,由於我們用主鍵只為了標示唯一,不關心段號,也不關心產生的順序,所以這些可以忽略。
 
:最近在討論某系統和一個外系統做全局事務的事情,本想用這個主鍵作為兩系統傳輸的一部分,用於控制全局事務,且用其作為判斷交易先后順序的依據,這是不太符合要求的,因為是RAC,序列是基於實例級cache,那么如果不能保證某一類型的交易總在一個節點上執行,那么不同次交易產生的主鍵序列值可能不是遞增的,例如節點1處理一次交易,產生序列是1,節點2處理一次交易,產生序列是21,此時節點1再處理一次交易,產生序列是2;除非設置序列為order,但這樣在RAC就有可能產生資源爭用的問題,因為為了保證多節點間每次產生的序列值是遞增的,每次產生就需要多節點間判斷當前值后,才能知道下一個值是多少,而且會有額外的鎖,保證同一時間只有一個節點在做這個操作,當然究竟是否會產生資源爭用,還是要依據實際的業務並發量,或者壓力測試才能證明,這里只是說可能會這樣的問題,不是一定會,否則Oracle就不會有提供這種order的創建屬性,凡事不絕對。

3、結合(1)的場景,
(1). 如果一個事務中只是INSERT時需要序列,其他地方不會需要這個序列,那么只需要在INSERT ... VALUES (seq.nextval ...)語句中使用即可。
> 這個場景下,如果序列cache設置為1000,調用100次nextval幾乎沒有影響。
(2). 如果一個事務中INSERT一張表后,還需要插入時的主鍵ID值,作為外鍵插入其他表,那么就需要在INSERT第一張表前使用select seq.nextval from dual提前獲取可用的ID保存到一個變量中,為后面使用。
> 這個場景,就有些說的了。如果是同一事務中需要用到之前的序列值,那么就需要提前用select seq.nextval from dual保存到變量中,(當然,如果是用PLSQL語句,則可以不用提前保存變量這步,但咱們的應用中不適用,就不多說了)。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM