虛擬私有資料庫 (VPD, Virtual Private Database) 是 Oracle 8i 推出的行級安全功能,透過動態修飾查詢謂詞實現資料隔離。Oracle 10g 引入四種新策略類型,針對謂詞動態性質提供精細的快取與重新評估控制,大幅提升高併發環境下的效能。本文深度解析 Dynamic、Context-Sensitive、Shared Context-Sensitive、Shared Static、Static 五種策略類型的運作機制、適用場景與效能特徵。
dept_id = 10)SELECT * FROM emp → SELECT * FROM emp WHERE dept_id = 10以 WHERE am_name = 'SCOTT' 為例:
am_name = (欄位名、運算子)'SCOTT' (依用戶身分、權限、上下文變化)關鍵洞見:靜態部份不變時,動態部份若可從穩定上下文取得,即可避免重複執行策略函數。
| 策略類型 | DBMS_RLS 常數 | 重新評估條件 | 快取層級 | 適用場景 |
|---|---|---|---|---|
| Dynamic (動態) | DBMS_RLS.DYNAMIC (預設) | 每次查詢、每行、每用戶 | 無 | 謂詞高度動態、依賴即時計算、向後相容 |
| Context-Sensitive (上下文敏感) | DBMS_RLS.CONTEXT_SENSITIVE | 應用上下文 (SYS_CONTEXT) 變化時 | 會話級 (SGA) | 謂詞依賴應用上下文、上下文會話期間相對穩定 |
| Shared Context-Sensitive (共享上下文敏感) | DBMS_RLS.SHARED_CONTEXT_SENSITIVE | 上下文變化 或 物件不同但函數相同時 | 跨物件共享 (SGA) | 多表共用同一策略函數、上下文相同 |
| Static (靜態) | DBMS_RLS.STATIC | 僅首次執行,之後永不重新評估 | SGA 永久快取 | 謂詞完全由登入用戶決定、會話期間不變 (如多租戶 SaaS) |
| Shared Static (共享靜態) | DBMS_RLS.SHARED_STATIC | 僅首次執行,跨物件共享 | 跨物件 SGA 永久快取 | 多表共用靜態策略函數 |
BEGIN
DBMS_RLS.ADD_POLICY(
object_schema => 'HR',
object_name => 'EMPLOYEES',
policy_name => 'EMP_DYNAMIC_POL',
function_schema => 'SECURITY',
policy_function => 'GET_EMP_PREDICATE',
policy_type => DBMS_RLS.DYNAMIC
);
END;
-- 策略函數
CREATE OR REPLACE FUNCTION get_balance_predicate(
p_schema IN VARCHAR2, p_table IN VARCHAR2
) RETURN VARCHAR2 IS
BEGIN
RETURN 'balance < SYS_CONTEXT(''VPD_CTX'', ''MAX_BAL'')';
END;
-- 註冊策略
BEGIN
DBMS_RLS.ADD_POLICY(
policy_type => DBMS_RLS.CONTEXT_SENSITIVE,
-- 其他參數同上
);
END;
-- 應用登入後設定上下文
BEGIN
DBMS_SESSION.SET_CONTEXT('VPD_CTX', 'MAX_BAL', 100000);
END;
效能機制:會話期間 MAX_BAL 不變 → 策略函數不重新執行 → 謂詞從 SGA 快取取得。
CREATE OR REPLACE FUNCTION get_cust_predicate(
p_schema IN VARCHAR2, p_table IN VARCHAR2
) RETURN VARCHAR2 IS
BEGIN
RETURN 'cust_name = SYS_CONTEXT(''VPD_CTX'', ''CUST_NAME'')';
END;
BEGIN
DBMS_RLS.ADD_POLICY(
policy_type => DBMS_RLS.STATIC,
-- 其他參數
);
END;
風險警示:若會話中 CUST_NAME 上下文被修改,策略函數不會重跑 → 回傳錯誤資料!
鐵律:只有 100% 確定會話期間上下文不變時才用 STATIC,否則改用 CONTEXT_SENSITIVE。
-- SAVINGS 與 CHECKING 表共用同一策略函數
BEGIN
DBMS_RLS.ADD_POLICY(object_name => 'SAVINGS', policy_type => DBMS_RLS.SHARED_CONTEXT_SENSITIVE, ...);
DBMS_RLS.ADD_POLICY(object_name => 'CHECKING', policy_type => DBMS_RLS.SHARED_CONTEXT_SENSITIVE, ...);
END;
效能增益:同會話查詢 SAVINGS 再查 CHECKING,上下文未變 → 策略函數不重跑,直接複用謂詞快取。
謂詞是否每次查詢都不同?
├─ 是 → DYNAMIC (無快取)
└─ 否 → 謂詞是否依賴應用上下文?
├─ 否 → 謂詞是否僅依賴登入用戶 (會話不變)?
│ ├─ 是 → STATIC (最快,但風險最高)
│ └─ 否 → CONTEXT_SENSITIVE (平衡)
└─ 是 → 是否多表共用同一函數?
├─ 是 → SHARED_CONTEXT_SENSITIVE
└─ 否 → CONTEXT_SENSITIVE
| 策略類型 | 策略函數執行次數/秒 | CPU 開銷 | 延遲影響 |
|---|---|---|---|
| Dynamic | 高 (每查詢每行) | 高 | 明顯 |
| Context-Sensitive | 低 (上下文變化時) | 低 | 微乎其微 |
| Shared Context-Sensitive | 極低 (跨表複用) | 極低 | 無 |
| Static | 1 (首次) | 最低 | 無 |
VPD_<業務域>_<屬性>,避免衝突DBMS_RLS.ADD_POLICY 的 sec_relevant_cols、sec_relevant_cols_opt 進行欄位級保護Oracle 10g VPD 策略類型演進,本質上是謂詞快取粒度與失效條件的精細控制。從 Dynamic 的零快取到 Static 的永久快取,中間的 Context-Sensitive 與 Shared 系列提供了豐富的效能調優槓桿。核心原則:謂詞靜態部份越多、動態部份來源越穩定 (應用上下文 vs 即時計算)、共用範圍越廣 (跨表/跨會話),效能增益越大。實務中建議以 Context-Sensitive 為基準,經效能測試驗證後,再針對熱點表升級為 Shared Context-Sensitive 或謹慎降級為 Static,構建安全且高效的行級存取控制體系。