Oracle 数据库 10g 版本 1 中的 PL/SQL 纯编译 (NCOMP)

From http://www.oracle.com/technology/global/cn/tech/pl_sql/htdocs/ncomp_faq.html

 

Oracle 数据库 10g 版本 1 中的 PL/SQL 纯编译 (NCOMP)

2004 年 9 月 4 日更新 

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

什么是纯编译?
如何实现 PL/SQL 纯编译?
Oracle 数据库 10g 有什么新特性?
什么对象可以进行 PL/SQL 纯编译?
共享库 (DLL) 的命名惯例是什么?
共享的动态链接库是否可以移植?
在部署端是否需要 C 编译环境?
哪些 Oracle 参数与纯编译相关?
plsql_native_library_dir 参数
plsql_native_library_subdir_count 参数
plsql_code_type 参数
$ORACLE_HOME/plsql/spnc_commands 文件的格式
纯编译生成的共享库存储在哪个字典表中?
当删除一个"纯编译"单元时,文件系统上的共享库将发生什么?
如何更改 plsql_native_library_dir?
是否还有其他场合需要手动删除共享库?
如果误删除了一个共享库该怎么办?
甲骨文公司是否建议混合使用纯编译单元和解释单元?
在操作系统升级时,是否需要重新生成纯编译的共享库?
在应用程序部署期间是否能够使用纯编译模式来节省时间(通过提供预先生成的 NCOMP 共享库)?
纯编译测试:简单测试
将数据库中的所有 PL/SQL 单元进行纯编译

什么是纯编译?

您可以通过将 PL/SQL 模块(程序包、触发器、过程、函数和类型)编译成驻留在共享库中的纯代码来加速它们的运行。过程被转换成 C 代码,然后用一个 C 编译器进行编译,并与 Oracle 进程动态链接。

您可以将该技术用于随付的 Oracle 程序包以及您自己编写的过程。用这种方式编译的过程可以用于所有服务器环境,如共享服务器配置(以前称为多线程服务器)和 Oracle 真正应用集群。

使用了大量有代表性测试程序的性能实验(发布在 OTN 上)表明,在 Oracle 数据库 10g 中,纯 PL/SQL 程序的应用程序在纯编译时的速度与解释编译时的速度相比(其他保持不变),预计可以获得大约 1.05 到 2.4 倍的性能提高。提高的程度取决于程序的种类。

注意:许多 Oracle 提供的程序包(如 UTL_FILE、UTL_RAW、DBMS_LOB)已经用 C 实施,而仅使用 PL/SQL 来提供 API。这些程序包不大可能从纯编译中获得显著的性能提高。同样,仅调用 SQL 语句的 PL/SQL 程序单元也可能几乎得不到加速。不过,纯编译的 PL/SQL 往往至少和相应的解释代码一样快。编译的代码将与解释的代码执行相同的库调用,因此它们的行为是完全相同的。

 

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

如何实现 PL/SQL 纯编译?

如果未使用纯编译,则将每个 PL/SQL 程序单元编译成一种中间形式的机器可读码 (MCode)。MCode 存储在数据库字典中,并在运行时进行解释。

使用 PL/SQL 纯编译,将 PL/SQL 程序编译成无需运行时解释的纯机器码,从而加快运行速度。

PL/SQL 使用命令文件 $ORACLE_HOME/plsql/spnc_commands 和支持的操作系统 C 编译器和链接器,将生成的 C 代码编译并链接到共享库中。共享库存储在数据字典中,这样便可将它们自动备份并防止它们被删除。这些共享库文件被复制到文件系统中,并在调用 PL/SQL 子程序时被加载和运行。如果在关闭数据库时从文件系统中删除了这些文件,或者如果您更改了这些库所在的目录,则自动重新提取这些文件。

 

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

Oracle 数据库 10g 有什么新特性?

这个特性现在需要更少的设置和维护。

不需要用相同的代码类型设置来编译程序包主体及其规范。例如,可以对程序包主体进行纯编译,而对程序包规范进行解释编译。
经过纯编译的子程序存储在数据库中,并在需要的时候自动提取相应的共享库。对于备份共享库、清理原有共享库等事项,您无需操心。
而且简化了纯编译的初始化参数和命令的设置。唯一需要的参数是 plsql_native_library_dir。不再使用与编译器、链接器和 make 实用程序相关的参数。$ORACLE_HOME/plsql/spnc_commands 命令文件用于进行纯编译。它包含了用于编译和链接的命令和选项,从而不再依赖于 make 实用程序。不再需要或支持 plsql_native_make_utility 和 plsql_native_make_file_name 参数,但仍须像在 9.0.1 版和 9.2.0 版中一样指定 plsql_native_library_dir。这将指定用于生成共享库(在共享库被复制到数据库中之前)的目录的完整路径。
纯编译期间出现的所有错误都存储在 USER_ERRORS 字典视图中,可通过 SQL*Plus 命令 SHOW ERRORS 来查询。
开启和关闭纯编译是由一个单独的参数 plsql_code_type 来控制的,而不是 plsql_compiler_flags 参数的几个选项之一,现在不建议使用后者。
RAC 支持:现在在没有集群(共享)文件系统的 RAC 环境中也支持 PL/SQL 纯编译。
共享库清理:当删除(或在解释模式下重新编译)纯编译的 PL/SQL 模块时,现在将删除(在 plsql_native_library_dir 指定的位置下的)共享库的文件系统副本。在 RAC 配置中,将从所有实例中删除共享库。


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

什么对象可以进行 PL/SQL 纯编译?

任何存储的 PL/SQL 模块都可以进行纯编译。匿名的 PL/SQL 程序块不能进行纯编译。

注意:Oracle 支持以下类型的存储 PL/SQL 模块:

FUNCTION
PACKAGE
PACKAGE BODY
PROCEDURE
TRIGGER
TYPE
TYPE BODY

 

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

共享库 (DLL) 的命名惯例是什么?

纯编译为每个单元创建一个共享库。例如,将为程序包规范和程序包主体分别创建一个共享库。以下是生成的一些名称:

PKG__SCOTT__S__54682.so
PKG__SCOTT__B__54683.so

生成的名称一般包含单元名称、模式名称、对象类型和对象编号。不过,这是一个可能会变更的内部惯例,用户不应依赖这种文件名格式规范。

文件名的扩展名在不同平台上可能不同。例如,在 Solaris 上共享库的后缀为 .so,而在 HP-UX 上共享库的后缀为 .sl。

返回页首
共享的动态可链接库是否可以移植?

不,绝对不行!共享库包含特定于平台的目标代码。

注意:在解释方式下创建的 MCode 也是特定于平台的。

 

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

在部署端是否需要 C 编译环境?

是的。没有商量的余地。PL/SQL 单元可能需要在部署端进行编译,PL/SQL 纯编译依靠一个 C 编译器来生成目标(机器)代码。

例如,各种操作(如向表中增加一列)可能导致与该表相关的 PL/SQL 单元的自动失效。对这种单元的后续访问将触发重新编译。在这种情况下,在纯编译模式下再次重新编译该单元需要 C 编译环境。

另一种情况是需要在部署数据库上应用补丁。可能需要在部署环境中重新加载和编译个别程序包。

 

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

哪些 Oracle 参数与纯编译相关?

plsql_native_library_dir
plsql_native_library_subdir_count
plsql_code_type
返回页首
plsql_native_library_dir 参数

这个参数指定保存共享库 (DLL) 的 OS 副本的目录位置。

当对一个模块进行纯编译时,将在这个位置创建共享库,然后将其复制到数据库字典表 (ncomp_dll$) 中。虽然共享库的主副本驻留在数据库中,但还将共享对象在文件系统中进行了物化,以便能够动态地将它们加载到 Oracle 的地址空间中。

注释 1:用户 (DBA) 决不能在系统启动并运行时从 plsql_native_library_dir 中手动删除共享库,因为这些 DLL 可能被映射到了 Oracle 进程上。只有在系统关闭的时候才能安全地删除共享库的 OS 副本。

注释 2:在 RAC 配置中,必须在每个实例中设置这个参数。不要求各实例拥有共享文件系统。在每个实例上,必须将 plsql_native_library_dir 设置为指向一个实例本地目录。或者,如果 RAC 配置支持共享(集群)文件系统,您可以将(共享文件系统上的)一个公共的目录用于所有实例。

注释 3:您必须创建这个目录。Oracle 不会自动为您创建这个目录。

注释 4:甲骨文公司在单实例或 RAC 配置中都不为 plsql_native_library_dir 提供 NFS 挂载目录支持。这是因为 NFS 在写或删除文件时会导致一些不可预测的时序问题。

 

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

plsql_native_library_subdir_count 参数

一些文件系统可能无法在单个目录中处理大量的文件;您可能需要在 plsql_native_library_dir 指定的目录下创建子目录来存储纯编译生成的共享库。

如果您使用的是生产品质的文件系统(例如 Veritas),那么您可能无需创建子目录。然而,如果您使用的是 vanilla 文件系统,那么它可能无法在单个目录中轻松地处理大量的文件。对于拥有大约 70,000 个 PL/SQL 对象的 Oracle 应用程序,在使用 vanilla 文件系统时,我们使用了大约 500 个子目录。

这个参数的默认值是 0。为了指示 Oracle 将共享库分配到不同的子目录中,而不是将所有共享库存储在 plsql_native_library_dir 指定的单个目录中,请将 plsql_native_library_subdir_count 参数设为您要创建的子目录数。

例如,要使用 500 个子目录,则将以下行添加到您的初始化参数文件中:

plsql_native_library_subdir_count=500

然后您必须创建 500 个子目录(在 plsql_native_library_dir 参数指定的目录下),目录名称即为 d0、d1、d2、..、d498、d499。

 

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

plsql_code_type 参数

plsql_code_type 参数决定对 PL/SQL 代码是进行纯编译还是解释。默认设置是 INTERPRETED。要启用 PL/SQL 纯编译,请将 plsql_code_type 的值设为 NATIVE。如果您想以 NATIVE 方式编译整个数据库,那么甲骨文公司建议您在系统级或在初始化参数文件中设置 plsql_code_type。

使用以下语法设置该参数:

对于纯编译模式:
alter session set plsql_code_type='NATIVE'
或者,
alter system set plsql_code_type='NATIVE'
对于解释模式:
alter session set plsql_code_type='INTERPRETED'
或者,
alter system set plsql_code_type='INTERPRETED'


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

spnc_commands 文件的格式

$ORACLE_HOME/plsql 目录中的 spnc_commands 文件包含了编译和链接各个程序的命令模板。一些特殊的名称(如 %(src))被预先定义,并用相应的文件名进行了替换。变量 $(ORACLE_HOME) 也自动被 Oracle 主目录的位置替换。您可以包含命令行(以 # 字符开始)。该文件包含解释所有特殊符号的注释。

spnc_commands 文件根据特定的操作系统为 C 编译器包含了一个预先定义的路径。(每种操作系统支持一种特定的编译器。)在大多数情况下,您不需要更改这个路径,但如果系统管理员将编译器安装在了另一个位置,您可能就需要更改它了。

这种方法意味着(与 9.0.1 版和 9.2.0 版不同)这里与 make 实用程序不存在依赖关系。

 

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

纯编译生成的共享库存储在哪个字典表中?

纯编译生成的共享库被作为 BLOB 存储在数据库中的 ncomp_dll$ 字典表中。

SQL> connect / as sysdba;
Connected.
SQL> describe ncomp_dll$;
Name         Null?Type
 ------------   --------------    --------
OBJ#         NOT NULL          NUMBER
VERSION                    NUMBER
DLL             BLOB
DLLNAME                    RAW(1024)

 

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

当删除一个"纯编译"单元时,文件系统上的共享库将发生什么?

直到 9.2.0,未发生任何事情。不会对共享库进行垃圾收集。

在 10.1.0 版中,当删除或在解释模式下重新编译一个纯编译的单元时,将从 ncomp_dll$ 字典表以及从 plsql_native_library_dir 参数指定的文件系统位置中删除该共享库。在 RAC 配置的情况下,将从每个实例的 plsql_native_library_dir 位置中删除该共享库。

 

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

如何更改 plsql_native_library_dir?

如果您需要更改该参数的值,建议采取以下步骤:

关闭数据库实例
将初始化参数文件中的 plsql_native_library_dir 设置更改为指向新的位置。
重新启动数据库实例。Oracle 将根据需要自动将共享库提取到这个新的位置中。
注释 1:虽然可以通过 alter system..命令来更改这个参数,但 Oracle 不建议这么做。

注释 2:一旦执行了上述步骤,就可以安全地删除与 plsql_native_library_dir 先前的设置对应的原有目录了。Oracle 不会 自动从原有位置删除共享库。

 

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

是否还有其他场合需要手动删除共享库?

没有。

唯一的例外是当您想修改 plsql_native_library_dir 的设置的时候。请按照先前所述步骤更改 plsql_native_library_dir。然后就可以从原有位置删除共享库了。

 

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

如果误删除了一个共享库该怎么办?

这可能导致非预期的内部错误和问题。建议的补救办法是关闭然后重新启动实例。Oracle 将根据需要自动从 ncomp_dll$ 字典表中提取共享库。

 

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

甲骨文公司是否建议混合使用纯编译单元和解释单元?

虽然支持混合使用,并且决不会导致错误的行为,但当纯编译单元调用解释单元时,需要付出一定代价。如果纯编译单元频繁调用解释单元,那么您可能牺牲一些潜在的性能改善。

如果将所有模块纯编译成共享库的时间是个问题,那么一种选择是考虑将程序包和类型规范在解释模式下进行编译,而将所有其他类型的 PL/SQL 单元(程序包主体、类型主体、过程、函数和触发器)在纯编译模式下进行编译。因为规范一般没有太多可执行代码,因此就运行时性能而言您不会有太大损失。

 

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

在操作系统升级时,是否需要重新生成纯编译的共享库?

假设您已经确定甲骨文支持这种类型的 OS 升级,而无需重新生成普通的 Oracle 可执行文件,那么 PL/SQL 纯编译产生的共享库也将可用,而无需重新生成。

 

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

在应用程序部署期间是否能够使用纯编译模式来节省时间(通过提供预先生成的 NCOMP 共享库)?

不能!即使共享库是在与最终部署环境相同的平台上生成的,您也无法这么做。您也无法在解释模式下利用 MCode 这么做。

其原因与编译模式无关。基于 PL/SQL 的应用程序在数据库中的安装需要在数据库中加载和编译 PL/SQL 模块,以便可以正确解析所有的外部引用。

 

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

纯编译测试:简单测试

创建一个目录,用于存储纯编译生成的共享库,并在初始化参数文件中将 plsql_native_library_dir 参数设置为指向该目录路径。例如,您需要在初始化参数文件中添加如下内容:
plsql_native_library_dir=/home/kmuthukk/oracle10g/dbs/ncomp_libraries


还可以在初始化参数文件中设置 plsql_native_library_subdir_count 并创建子目录。
确保 spnc_commands 文件中使用的 C 编译器 -- 以及链接器(如果适用) -- 的 OS 路径是正确的。
系统参数完整性检查: 必须指定 plsql_native_library_dir。plsql_native_library_subdir_count 是可选的,可以保留为 0(如果您不打算使用基于子目录的模式)。最好在初始化参数文件中指定这些参数。
SQL> show parameters plsql_native
NAME         TYPE  VALUE
------------------------------------ ----------- ------------------------------
plsql_native_library_dir      string  /home/kmuthukk/oracle10g/dbs/ncomp_libraries
plsql_native_library_subdir_count    integer  0

对 PL/SQL 模块进行纯编译

要在纯编译模式下加载和编译新的模块,必须将 plsql_code_type 参数设为 NATIVE。
例如,尝试在 SQL*Plus 中执行以下操作:

connect scott/tiger
alter session set plsql_code_type='NATIVE';

create or replace procedure my_test is
begin
dbms_output.put_line('hello world');
end;
/
show errors;

set serveroutput on;
execute my_test;

验证步骤:

看一下 plsql_native_library_dir 指定的位置。应找到一个与 MY_TEST 过程相对应的新的共享库。在 Solaris 上,DLL 可能有一个类似 MY_TEST__SCOTT__P__54765.so 的名称。
[要阅读有关共享库命名惯例的更多信息,请点击此处。]


此外,通过执行以下查询,确保该模块的 plsql_code_type 设置的确为 NATIVE:
select name, type, plsql_code_type
from user_plsql_object_settings
where name = 'MY_TEST';

如果您想手动地对特定的已经加载的模块进行纯编译,那么您可以使用 alter ... compile .. 语句。
假定您有一个程序包 MYPKG,其规范和主体是用解释方式编译的。现在,如果您只想对其主体进行纯编译,那么可以执行以下命令:

alter package MYPKG compile body plsql_code_type=native reuse settings
  
上面的 reuse settings 子句指示编译器应当保留该单元所有以前的设置 -- 被显式覆盖的设置(如在上述示例中设定的 plsql_code_type 的设置)除外。

如果从上面的语句中删除了 body 子句,如:

alter package MYPKG compile plsql_code_type=native reuse settings
... 那么规范和主体都将在纯编译模式下重新编译。不过请注意,重新编译规范将造成依赖于该规范的其他模块失效。

 

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

将数据库中的所有 PL/SQL 单元进行纯编译

在试图将数据库中的所有 PL/SQL 单元进行纯编译之前,在一个简单的测试案例上试验一下(如上一部分所述)。这将帮助确保您的纯编译设置是正确的。

操作系统文件描述符限制
$ORACLE_HOME/rdbms/admin 目录下的两个 SQL*Plus 脚本(dbmsupgnv.sql 和 utlrp.sql)用来在纯编译模式下重新编译所有的 PL/SQL 模块。它们使用并行作业(取决于可用的 CPU 的数量)。在这种情况下,许多作业同时生成 C 文件并试图编译它们。因此不将文件描述符的最大数的 OS 限制设得过低非常重要。

例如,在我的 Solaris 计算机上:

% ulimit -a
time(seconds)        unlimited
file(blocks)         unlimited
data(kbytes)         2097148
stack(kbytes)        8192
coredump(blocks)     3
nofiles(descriptors) 64

vmemory(kbytes)      unlimited

注意描述符的最大数(值为 64)。这在生产环境中太少了,因为有许多作业要进行并行纯编译。您可能想查看您计算机上的限制,并将其提高。例如:

在 Solaris 上,需要编辑 /etc/system 文件,使之包含:

set rlim_fd_max=4096
set rlim_fd_cur=1024

在 HP-UX 上,相应的参数位于 /stand/system 文件中。

maxfiles        2048
maxfiles_lim    2048


系统表空间
因为 DLLs 的主副本存储在字典表中,因此您可能需要比以前更多的 SYSTEM 表空间。如果您遇到了与 SYSTEM 表空间的空间限制相关的错误,那么您将需要执行如下操作:

alter tablespace system
add datafile '/home/kmuthukk/oracle10g/dbs/mydata10.dbf'
size 500 m;

.. 或在数据文件上设置 autoextend。


以纯编译模式重新编译所有的模块(dbmsupgnv.sql 和 utlrp.sql)

关闭所有的应用程序服务(包括 Forms Processe、Web Server、Reports Server 和 Concurrent Manager Server)。在关闭所有的应用程序服务之后,确保中断了所有与数据库的连接。

关闭数据库的 TNS 监听器,以确保不会创建新的连接。

关闭数据库(以正常模式或即时模式)。

确定您已在初始化参数文件中设置了 plsql_native_library_dir。如果没有,则请查看上一部分所述的步骤。

在初始化参数文件中将 plsql_code_type 设为 NATIVE。
plsql_code_type=NATIVE


在 UPGRADE 模式下启动数据库(使用 STARTUP UPGRADE 或 ALTER DATABASE OPEN UPGRADE)。

运行以下查询,获取无效对象的数量。这个值将在稍后用于作比较,以确保重新编译没有由于纯编译故障而产生额外的无效对象。
Select count(*)
From all_objects
Where status='INVALID';

以下查询将为您展示,纯编译模式和解释模式分别编译了多少对象。
SELECT plsql_code_type, count(*)
FROM dba_plsql_object_settings
GROUP BY plsql_code_type;

您可能看到一些对象的 plsql_code_type 为 NULL。这些是特殊的内部对象,可以忽略。


cd(更改目录)至 $ORACLE_HOME/rdbms/admin。

运行 $ORACLE_HOME/rdbms/admin/dbmsupgnv.sql(作为 SYS 用户)。
注释 1: dbmsupgnv.sql 使所有的 PL/SQL 模块失效并更新字典表,将它们的 plsql_code_type 设置修改为 NATIVE。这一阶段必须在数据库上没有其他活动进行时完成;因此,我们要求在运行 dbmsupgnv.sql 期间必须以 UPGRADE 模式启动数据库。

注释 2:如果您要从数据库的一个低版本向数据库高版本升级(例如,升级到 Oracle 数据库 10g),那么您必须首先运行升级脚本,然后运行 dbmsupgnv.sql。例如,如果您正从 9.2.0.3 升级到 10.1.0,那么请首先运行升级脚本 (u0902000.sql),然后关闭数据库并运行 dbmsupgnv.sql 脚本。


在运行 dbmsupgnv.sql 之后提交更改
SQL> commit;


关闭数据库。

以正常模式重新启动数据库。

运行 $ORACLE_HOME/rdbms/admin/utlrp.sql 脚本(作为 SYS 用户)。这一步骤将重新编译所有的 PL/SQL 模块。[在这个过程期间,您可能想定期查看 plsql_native_library_dir 指定的目录,以确保编译过程的确创建了新的共享库。]
注意:这一步骤是可重新启动的。如果由于任何原因这个步骤异常终止,那么您只需重新启动这个步骤(即重新运行 utlrp.sql 脚本)。它将重新编译余下的任何无效的 PL/SQL 模块。


在编译成功完成之后,您应当验证无效对象数少于或等于重新编译之前的无效对象数。您可以使用以下查询获取无效对象数。
Select count(*)
From all_objects
Where status='INVALID';

再运行以下查询,以确保没有遗漏解释的模块:

SELECT plsql_code_type, count(*)
FROM dba_plsql_object_settings
GROUP BY plsql_code_type;

About this Entry

This page contains a single entry by Sky published on July 30, 2008 4:01 AM.

Oracle 11g Linux单机STANDBY配置 was the previous entry in this blog.

Hashing in Oracle is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.