首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> Oracle 資料庫 >> Oracle 資料庫基礎

Oracle 自增主鍵實作指南:Sequence + Trigger 完整範例

     編輯:Oracle 資料庫基礎

前言:Oracle 無 Identity 欄位,如何實現自增主鍵?

Oracle 12c 前無 GENERATED AS IDENTITY 語法,傳統做法是 Sequence(序列)+ Trigger(觸發器) 組合。即使 12c+ 支援 Identity,Sequence+Trigger 仍因靈活性(跨表共用序列、自定義快取、手動控制)被廣泛使用。本文提供生產級完整範例,涵蓋建表、建序列、建觸發器、效能調優、常見坑點。

一、建立業務表

CREATE TABLE users (
  id        NUMBER(18) NOT NULL,
  username  VARCHAR2(50) NOT NULL,
  password  VARCHAR2(100) NOT NULL,
  email     VARCHAR2(100),
  created_at TIMESTAMP DEFAULT SYSTIMESTAMP NOT NULL,
  CONSTRAINT pk_users PRIMARY KEY (id)
) TABLESPACE users_data
PCTFREE 10
STORAGE (INITIAL 64M NEXT 64M MAXEXTENTS UNLIMITED);

二、建立序列

CREATE SEQUENCE seq_users_id
  START WITH 1
  INCREMENT BY 1
  NOCACHE          -- 或 CACHE 1000(高併發建議)
  NOMAXVALUE
  NOCYCLE
  NOORDER;         -- RAC 環境建議 ORDER 保證全域有序

參數決策指南

三、建立 BEFORE INSERT 觸發器

CREATE OR REPLACE TRIGGER trg_users_bir
BEFORE INSERT ON users
FOR EACH ROW
WHEN (NEW.id IS NULL)  -- 僅在未指定 ID 時自動填充,支援手動指定 ID(如資料遷移)
BEGIN
  SELECT seq_users_id.NEXTVAL INTO :NEW.id FROM DUAL;
END;
/
ALTER TRIGGER trg_users_bir ENABLE;

關鍵設計點

  1. WHEN 子句:避免不必要的 Sequence 呼叫,支援「手動指定 ID」場景(資料遷移、種子資料)。
  2. FOR EACH ROW:列級觸發,確保每筆 INSERT 取一個序列值。
  3. 無例外處理:Sequence 耗盡極罕見,若發生讓錯誤上拋給應用層處理。

四、Oracle 12c+ Identity Column 寫法(現代替代方案)

CREATE TABLE users_new (
  id NUMBER(18) GENERATED ALWAYS AS IDENTITY
      (START WITH 1 INCREMENT BY 1 CACHE 1000 NOORDER),
  username VARCHAR2(50) NOT NULL,
  ...
);
  • GENERATED ALWAYS:不可手動指定 ID(遷移時需 OVERRIDING SYSTEM VALUE)。
  • GENERATED BY DEFAULT ON NULL:NULL 時自動填充,非 NULL 時接受手動值(最接近 Trigger 行為)。
  • 效能優於 Trigger(無 Context Switch),但靈活性稍低(難以跨表共用序列)。

五、效能壓測與調優

高併發插入測試腳本

-- 10 個 Session 各插 10,000 筆
DECLARE
  v_start NUMBER := DBMS_UTILITY.GET_TIME;
BEGIN
  FOR i IN 1..10000 LOOP
    INSERT INTO users (username, password, email)
    VALUES ('user_'||i, 'pwd', 'a@b.com');
  END LOOP;
  COMMIT;
  DBMS_OUTPUT.PUT_LINE('Elapsed cs: ' || (DBMS_UTILITY.GET_TIME - v_start));
END;
/

觀察指標

  • Sequence Cache 爭用:V$ENQUEUE_STAT WHERE EQ_TYPE = 'SQ'。
  • Redo Log 生成量:V$SYSSTAT 'redo size'。
  • Trigger Context Switch 開銷:V$SYSSTAT 'recursive cpu usage'。

調優建議

  • Sequence CACHE 10000 + NOORDER(非 RAC)可將 Sequence 待機時間降至微秒級。
  • 批次插入改用 FORALL + INSERT /*+ APPEND */ 繞過 Trigger(需手動取序列值填入集合)。
  • 極高併發(>5000 TPS)考慮 SHARDING 或應用層 ID 生成(Snowflake/UUID)。

六、常見坑點與解法

參數推薦值理由
CACHE1000~10000高併發減少 Sequence Cache 鎖競爭;單機小系統可 NOCACHE 避免跳號
ORDER/NOORDERRAC 必 ORDERRAC 下 NOORDER 可能導致跨節點號碼倒退,應用若依賴單調遞增必須 ORDER
MAXVALUENOMAXVALUENUMBER(18) 上限 10^18,業務極難耗盡
現象原因解法
ID 跳號(非連續)Sequence Cache 失效(實例重啟、Age Out)、Rollback、手動指定 ID業務接受跳號;若需連續需應用層管理
ORA-00001 PK Violation手動 INSERT 指定 ID 與 Sequence 產生值衝突Trigger 加 WHEN (NEW.id IS NULL);遷移資料時先調整 Sequence NEXTVAL > MAX(ID)
RAC 下 ID 倒退NOORDER 導致各節點獨立 Cache建序列加 ORDER;或接受最終一致性
Trigger 失效DDL 變更導致 Trigger INVALID部署腳本最後加 ALTER TRIGGER ... COMPILE;監控 USER_TRIGGERS.STATUS

七、資料遷移時的 Sequence 校準

-- 遷移歷史資料後,將 Sequence 推進至 MAX(ID) 以上
SELECT 'ALTER SEQUENCE seq_users_id INCREMENT BY ' || (max_id - curr_val + 1000) || ';'
FROM (SELECT MAX(id) max_id FROM users),
     (SELECT seq_users_id.CURRVAL curr_val FROM dual);

-- 執行上述動態 SQL,再改回 INCREMENT BY 1
ALTER SEQUENCE seq_users_id INCREMENT BY 1;

結語

Sequence + Trigger 是 Oracle 自增主鍵的經典模式,關鍵在於:CACHE 參數對效能的決定性影響、WHEN 子句對遷移相容性的支援、RAC 環境下 ORDER/NOORDER 的架構決策。12c+ 專案可優先評估 Identity Column,但掌握傳統模式調優細節,對維護既有系統、跨版本遷移仍具核心價值。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved