Oracle PL/SQL 包初始化与状态管理详解

Oracle PL/SQL 包初始化与状态管理详解

适用版本:Oracle Database 10g / 11g / 12c / 19c / 23ai 文档版本:v1.0 / 2026-07


1. 概述

PL/SQL 包初始化与状态管理[1]:

详细见:Oracle PL/SQL 包设计与最佳实践


2. 初始化块

2.1 基本

CREATE OR REPLACE PACKAGE BODY emp_pkg AS
  v_init_time TIMESTAMP;
  v_user VARCHAR2(30);
  
  PROCEDURE hire(...) IS ... BEGIN ... END;
  FUNCTION get_count(...) RETURN NUMBER IS ... BEGIN ... END;

BEGIN
  -- 首次引用时执行
  -- 一次/会话
  v_init_time := SYSTIMESTAMP;
  v_user := USER;
  
  DBMS_OUTPUT.PUT_LINE('Package initialized');
END emp_pkg;
/

2.2 用途

- 默认值
- 加载配置
- 验证
- 一次性初始化

2.3 示例

CREATE OR REPLACE PACKAGE config_pkg AS
  FUNCTION get_param(p_name VARCHAR2) RETURN VARCHAR2;
END;
/

CREATE OR REPLACE PACKAGE BODY config_pkg AS
  TYPE param_tab IS TABLE OF VARCHAR2(4000) INDEX BY VARCHAR2(100);
  v_params param_tab;
  
  FUNCTION get_param(p_name VARCHAR2) RETURN VARCHAR2 IS
  BEGIN
    IF v_params.EXISTS(p_name) THEN
      RETURN v_params(p_name);
    END IF;
    RETURN NULL;
  END;

BEGIN
  -- 加载配置
  FOR rec IN (SELECT name, value FROM app_config) LOOP
    v_params(rec.name) := rec.value;
  END LOOP;
END config_pkg;
/

3. 会话状态

3.1 变量

CREATE OR REPLACE PACKAGE session_pkg AS
  v_user_id NUMBER;
  v_login_time TIMESTAMP;
  v_session_id VARCHAR2(50);
  
  PROCEDURE init(p_user_id NUMBER);
END;
/

CREATE OR REPLACE PACKAGE BODY session_pkg AS
  PROCEDURE init(p_user_id NUMBER) IS
  BEGIN
    v_user_id := p_user_id;
    v_login_time := SYSTIMESTAMP;
    v_session_id := SYS_GUID();
  END;
END;
/

3.2 持久性

- 会话级
- 跨调用保留
- 重启丢失
- RAC 各节点独立

4. SERIALLY_REUSABLE

4.1 基本

CREATE OR REPLACE PACKAGE temp_pkg AS
  PRAGMA SERIALLY_REUSABLE;
  v_counter NUMBER := 0;
  
  PROCEDURE increment;
  FUNCTION get_counter RETURN NUMBER;
END;
/

CREATE OR REPLACE PACKAGE BODY temp_pkg AS
  PRAGMA SERIALLY_REUSABLE;
  
  PROCEDURE increment IS
  BEGIN
    v_counter := v_counter + 1;
  END;
  
  FUNCTION get_counter RETURN NUMBER IS
  BEGIN
    RETURN v_counter;
  END;
END;
/

-- 状态不跨调用保留
EXEC temp_pkg.increment;
EXEC temp_pkg.increment;
EXEC DBMS_OUTPUT.PUT_LINE(temp_pkg.get_counter);  -- 0

4.2 优势

- 内存优化
- 大包
- 临时

4.3 限制

- 状态不保留
- 不适合缓存

5. PRAGMA

5.1 AUTONOMOUS_TRANSACTION

PROCEDURE log_msg(p_msg VARCHAR2) IS
  PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
  INSERT INTO log VALUES (p_msg, SYSTIMESTAMP);
  COMMIT;
END;

5.2 EXCEPTION_INIT

DECLARE
  e_fk EXCEPTION;
  PRAGMA EXCEPTION_INIT(e_fk, -02292);
BEGIN
  ...
EXCEPTION
  WHEN e_fk THEN ...
END;

详细见:Oracle PL/SQL 异常处理详解

5.3 INLINE

CREATE OR REPLACE PROCEDURE caller IS
  PRAGMA INLINE(small_proc, 'YES');
BEGIN
  small_proc;
END;

6. 包状态重置

6.1 重新编译

ALTER PACKAGE emp_pkg COMPILE;
-- 重置包状态

6.2 DBMS_SESSION

EXEC DBMS_SESSION.MODIFY_PACKAGE_STATE(DBMS_SESSION.REINITIALIZE);
-- 或
EXEC DBMS_SESSION.FREE_UNUSED_USER_MEMORY;

7. 跨包状态

7.1 共享

CREATE OR REPLACE PACKAGE shared_pkg AS
  v_global_count NUMBER := 0;
END;
/

CREATE OR REPLACE PACKAGE other_pkg AS
  PROCEDURE increment;
END;
/

CREATE OR REPLACE PACKAGE BODY other_pkg AS
  PROCEDURE increment IS
  begin
    shared_pkg.v_global_count := shared_pkg.v_global_count + 1;
  END;
END;
/

7.2 RAC

- 各节点独立
- 不共享
- 同步需表/队列

8. 持久化

8.1 表

CREATE TABLE pkg_state (
  session_id VARCHAR2(50),
  pkg_name VARCHAR2(50),
  state CLOB
);

-- 保存
INSERT INTO pkg_state VALUES (..., 'EMP_PKG', ...);

-- 加载
SELECT state INTO ... FROM pkg_state WHERE ...;

8.2 Context

CREATE OR REPLACE CONTEXT app_ctx USING ctx_pkg;

CREATE OR REPLACE PACKAGE ctx_pkg AS
  PROCEDURE set(p_name VARCHAR2, p_value VARCHAR2);
END;
/

CREATE OR REPLACE PACKAGE BODY ctx_pkg AS
  PROCEDURE set(p_name VARCHAR2, p_value VARCHAR2) IS
  BEGIN
    DBMS_SESSION.SET_CONTEXT('app_ctx', p_name, p_value);
  END;
END;
/

-- 使用
SELECT SYS_CONTEXT('app_ctx', 'user_id') FROM dual;

详细见:Oracle PL/SQL 安全编程详解


9. 全局临时表

9.1 会话级

CREATE GLOBAL TEMPORARY TABLE temp_state (
  key VARCHAR2(50),
  value VARCHAR2(4000)
) ON COMMIT PRESERVE ROWS;

-- 使用
INSERT INTO temp_state VALUES ('user_id', '100');
SELECT value INTO v_user FROM temp_state WHERE key = 'user_id';

10. 缓存

10.1 包级

CREATE OR REPLACE PACKAGE cache_pkg AS
  TYPE dept_cache IS TABLE OF departments%ROWTYPE INDEX BY PLS_INTEGER;
  v_dept_cache dept_cache;
  
  FUNCTION get_dept(p_id NUMBER) RETURN departments%ROWTYPE;
END;
/

CREATE OR REPLACE PACKAGE BODY cache_pkg AS
  FUNCTION get_dept(p_id NUMBER) RETURN departments%ROWTYPE IS
  BEGIN
    IF NOT v_dept_cache.EXISTS(p_id) THEN
      SELECT * INTO v_dept_cache(p_id) FROM departments WHERE id = p_id;
    END IF;
    RETURN v_dept_cache(p_id);
  END;
END;
/

10.2 RESULT_CACHE

CREATE OR REPLACE FUNCTION get_dept_name(p_id NUMBER) RETURN VARCHAR2
  RESULT_CACHE RELIES_ON (departments)
IS
  v_name VARCHAR2(100);
BEGIN
  SELECT name INTO v_name FROM departments WHERE id = p_id;
  RETURN v_name;
END;
/

详细见:Oracle PL/SQL 性能优化详解


11. 应用场景

11.1 会话管理

CREATE OR REPLACE PACKAGE session_mgr AS
  PROCEDURE login(p_user VARCHAR2);
  PROCEDURE logout;
  FUNCTION is_logged_in RETURN BOOLEAN;
END;
/

11.2 配置

CREATE OR REPLACE PACKAGE config_pkg AS
  FUNCTION get(p_name VARCHAR2) RETURN VARCHAR2;
END;
/

11.3 计数器

CREATE OR REPLACE PACKAGE counter_pkg AS
  PROCEDURE increment;
  FUNCTION get RETURN NUMBER;
END;
/

11.4 缓存

CREATE OR REPLACE PACKAGE lookup_pkg AS
  FUNCTION get_dept_name(p_id NUMBER) RETURN VARCHAR2;
END;
/

12. 性能

12.1 内存

- 包状态占 PGA
- 大集合注意
- SERIALLY_REUSABLE 优化

12.2 启动

- 首次引用初始化
- 一次/会话
- 简洁

13. 常见坑与排错

13.1 状态丢失

- 重启
- RAC
- SERIALLY_REUSABLE

13.2 内存

- 大集合
- OOM
- LIMIT

13.3 并发

- 会话独立
- RAC
- 同步

14. 最佳实践

  1. 初始化块:一次
  2. 会话状态:登录
  3. SERIALLY_REUSABLE:临时
  4. RESULT_CACHE:缓存
  5. 持久化:表
  6. Context:共享
  7. GTT:临时
  8. 重置:MODIFY_PACKAGE_STATE
  9. 监控:内存
  10. 文档:说明

15. 参考资料

[1] Oracle Database PL/SQL Language Reference 19c, “Package State” https://docs.oracle.com/en/database/oracle/oracle-database/19/lnpls/plsql-packages.html