内容


记录 DB2 UDB 的存储过程消息

一个用于动态记录 C 存储过程日志的框架

Comments

动机

我们可能会因为以下几个原因需要将信息、错误和调试信息写入日志文件中:

  • 测试、分析并验证业务逻辑。
  • 跟踪并查看详细的错误消息。
  • 通过动态打开详细日志或关闭日志来解决在产品环境中碰到的业务逻辑 bug。
  • 通过检查日志语句的执行时间来查明存储过程的性能问题。

我们可以对存储过程使用这个日志框架来满足以下需求:

  1. 在产品环境中动态打开或关闭全局或可选择的日志功能。
  2. 对日志文件进行归档,将当前日志文件维护到一个可管理的大小。
  3. 为每个存储过程和每个 DB2 代理确定日志语句。
  4. 为每个 DB2 代理分隔或合并日志文件。
  5. 确保日志文件本身不是资源敏感的。

除了本教程中提供的框架之外,还应该研究一下用来跟踪和配置 SQL PL 过程的高级方法,下面 参考资料 部分列出的文章中对这些方法进行了总结。

整体架构和设计

DB2 有自己的日志方法,可以将消息存放到 db2diag.log 文件中。DB2 还提供了一些工具来捕获并存放详细的跟踪信息,这样 DB2 开发人员就可以对所报告的问题进行隔离和解决。本文中给出的日志框架提供了将信息和调试消息捕获到文件中进行分析的能力,这样存储过程的开发人员就可以对这些问题进行隔离和解决。

由于 DB2 存储过程可以由不同的 DB2 代理并行执行,因此记录日志的存储过程的每个实例都必须读取所有的日志控制信息,并与其他过程一起共享这些信息。为了实现这个目的,我们使用了共享内存,每个日志过程实例都可以访问这些共享内存中的信息。

如何配置日志功能

要为某个存储过程配置日志功能,就需要使用存储过程 DB2.UPDATE_SP_CONFIG。我们可以使用这个过程来启动、停止和控制记录日志的功能,并设置共享内存中的参数,这样,所有正在执行的存储过程都可以使用相同的日志来控制信息。

为了启用或禁用用于每个存储过程的日志,或同时启用或禁用用于所有存储过程的日志,可以将这些信息保存到一个配置文件中,并在首次调用日志存储过程时将其加载到共享内存中。在更改日志配置参数时,共享内存中的信息就会获得更新,同时还会保存到配置文件中。(注意:这非常类似于 DB2 配置参数的更改,其中对某些参数的更改可以立即生效,有些则会等到实例或数据库重新启动之后才生效。在这个日志框架中,对存储过程的日志参数进行的任何更改都会立即生效)。

这个日志框架的一个目标是可以保守地使用系统资源,这样日志过程调用所耗费的资源都不会太多,或者不会产生很大负载。这个框架的设计目标是可以被上千个存储过程使用,而不会对性能产生太大影响。

对于每个日志调用,都需要确定这条消息是否将发送到日志文件中。我们需要根据日志标记名使用的散列值对具有链表 (linked list) 的散列表进行查询。对于那些共享相同散列值的名称来说,我们在相同的关键字级别上维护了一个链表,以方便查找相同的日志标记名的配置参数。使用这种具有链表的散列表可以提供最快速的查询功能。

如果对所有的存储过程都启用完整日志,那么日志文件本身可能会增长的非常快。通过对日志文件进行归档,可以将日志文件移动到另外一个位置进行分析、存储或删除。

如何启动日志

要在 DB2 的存储过程中启用日志功能,首先要使用两个参数调用 DB2.OPEN_LOG。第一个参数是日志标记名,第二个是一个句柄。为了尽量简单,可以使用存储过程名作为日志标记名,也可以对一组实现特定业务功能的存储过程使用一个通用的日志标记。例如,如果有一个嵌套过程,而该过程又调用另外 6 个 DB2 存储过程来获取最佳实现,那么可以对这 7 个 DB2 存储过程使用标记名 GBQR。DB2.OPEN_LOG 中的第二个参数是一个 CHAR 类型的句柄(48 个字节)。该句柄由两个 C 结构组成。其中一个包含标记名的共享内存的位置及其日志操作,第二个结构包含调用 DB2.OPEN_LOG 时的时间戳。

如何记录消息

日志框架为记录消息提供了另外两个存储过程:DB2.LOGINFODB2.LOGGER。每个存储过程都需要两个参数。第一个参数是从 DB2.OPEN_LOG 中获得的日志句柄,第二个是要记录的消息。如果共享内存中存在这个标记,则 DB2.LOGINFO 会记录一条信息消息。如果调试参数被设置为 Y,并且在共享内存中存在一个日志标记,那么 DB2.LOGGER 就会记录一条调试信息。

通常,我们会使用 LOGINFO 来记录详细错误和其他信息消息,并会使用 LOGGER 将调试信息截获到日志文件中。当不想将调试消息记录到日志文件中时,将这个日志标记的调试参数设置成 N 即可。要设置这个参数,可以使用存储过程 DB2.UPDATE_SP_CONFIG,该过程用来实现日志管理的工作。如果希望彻底关闭 LOGINFO 消息,那么通过将调试参数设置成 R 来永久删除这个标记,就可以将这个日志标记从共享内存中删除。

如何关闭日志

在这个存储过程的末尾,可以通过调用 DB2.CLOSE_LOG 来关闭日志。CLOSE_LOG 过程需要使用日志句柄作为输入参数。CLOSE_LOG 会在最后向日志中写入最后一条消息,内容是从启动日志到结束日志经过了多少时间。CLOSE_LOG 调用对于确定使用的执行时间非常有用。

日志框架的引用指南

日志标记的解释

第一个 DB2.OPEN_LOG 调用的功能是从共享内存中保存的日志标记来构建一个如 图 1 所示的链表散列表。对于每个从共享内存或初次调用使用的配置文件中读取的日志标记,OPEN_LOG 都会使用标记名和散列表的大小来生成一个散列值。对于大小为 10 的散列表来说,get_quotefind_zip_codeverify_zipput_culls 标记会共享同一个散列值 3。这个链表散列表包含有关共享内存日志标记指针的信息。共享内存的位置存储在句柄中,从 OPEN_LOG 到后续的 LOGGER 和 LOGINFO 调用都会传递这个句柄。

图 1. 链表散列表
链表散列表
链表散列表

这些日志方法都可以在 SQL-PL 存储过程(参见例子)或其他外部存储过程(COBOL、Java™ 等)中使用。如果正在使用用 C 语言编写的外部存储过程,那么既可以使用日志存储过程,也可以直接使用日志调用,后者可以跳过存储过程(参见例子)。

日志存储过程框架包含两个部分。第一个部分是 日志管理,用来获取或设置配置参数。第二个部分是 日志存储过程,可以在 SQL-PL 或任何其他外部存储过程中使用。

日志管理

用来进行日志管理的存储过程有 3 个。DB2.UPDATE_SP_CONFIG 存储过程用来设置并存储日志配置参数。DB2.GET_SP_CONFIG 存储过程将列出日志参数及其值。第三个存储过程是 DB2.GET_SP_LOG,它可以打印日志文件的内容。

更新存储过程的配置

DB2.UPDATE_SP_CONFIG 是用来设置配置文件和共享内存中某个标记名所使用的日志管理存储过程。通常,我们会在命令行中使用 CLP 或 DB2 命令编辑器来执行这个存储过程,以实现对日志参数的更新。

CALL DB2.UPDATE_SP_CONFIG(log_token_name, value);

log_token_name —— 日志标记名。此框架使用了两类日志标记:全局标记和用于单个或一组存储过程的私有标记。

全局标记 —— 这个框架使用了两个全局标记。

  1. GLOBAL_LOGGING —— 该值默认设置为 Y。在将这个参数设置为 N 时,LOGINFO 和 LOGGER 所使用的所有日志行为就会全部停止。利用这个参数,可以在运行时关闭所有存储过程的日志。
  2. LOG_SPLIT —— 该默认设置为 N。当将 LOG_SPLIT 设置为 N 时,日志框架就会将所有的语句全部记录到日志文件 sp.log 中。当多个不同的 DB2 代理同时执行某个存储过程时,可以在一个日志文件中看到所有的日志语句,但是可以通过寻找每个 DB2 代理所使用的惟一应用程序 ID 号来区分每个日志语句。当所有语句都被记录到一个文件中时,就可以使用 grep 对这个文件进行过滤,从而查看某个特定 DB2 代理所记录的语句。在将这个参数设置为 Y 时,日志文件就会对每个 DB2 代理进行分隔,每个文件的名称都是以这个 DB2 代理的应用程序 ID 开头的。

私有标记 —— 单个存储过程或一组存储过程可以采用某个标记名,来说明这个标记需要输出日志。

使用 DB2.UPDATE_SP_CONFIG 存储过程更新日志标记:

  • 要关闭全局日志:
    call DB2.UPDATE_SP_CONFIG('GLOBAL_LOGGING','N');
  • 要启用全局日志:
    call DB2.UPDATE_SP_CONFIG('GLOBAL_LOGGING','Y');
  • 要启用日志文件分隔功能:
    call DB2.UPDATE_SP_CONFIG('LOG_SPLIT','Y');

    注意:当启用日志分隔功能时,日志文件名就是从 db2 list applications 命令获得的应用程序 ID 的最后部分。例如,这个命令典型的输出如下所示:

    清单 1. db2 list applications 命令的示例输出
    Auth Id  Application    Appl.      Application Id                 DB       # of
             Name           Handle                                    Name    Agents
    -------- -------------- ---------- ------------------------------ -------- -----
    DB2INST1 db2jcc_applica 864        GA0A0A34.A905.051223231443     FALCON   1    
    DB2INST1 db2jcc_applica 867        GA0A0A34.A904.051223231442     FALCON   1    
    DB2INST1 db2jcc_applica 866        GA0A0A34.A906.051223231441     FALCON   1    
    DB2INST1 db2jcc_applica 862        GA0A0A34.A907.051223231440     FALCON   1

    如果上面的每个 DB2 代理都正在执行相同的存储过程,那么就会生成 4 个日志文件,其名称分别如下所示:
    051223231443.log
    051223231442.log
    051223231441.log
    051223231440.log

  • 要关闭日志文件分隔功能:
    call DB2.UPDATE_SP_CONFIG('LOG_SPLIT','N');
  • 要将 Debug 设置为 Y 来创建 TESTSP 日志标记:
    call DB2.UPDATE_SP_CONFIG('TEST_SP','Y');
  • 要将 Debug 设置为 N 来更新 TESTSP 日志标记:
    call DB2.UPDATE_SP_CONFIG('TEST_SP','N');
  • 要将 TESTSP 日志标记从共享内存和配置文件中删除:
    call DB2.UPDATE_SP_CONFIG('TEST_SP','R');

DB2.UPDATE_SP_CONFIG 存储过程对共享内存进行更新,并将所有的日志标记写入一个配置文件中。日志目录被命名为 splog,我们需要在 ~/sqllib 目录中创建它。可以采用两种方法来创建这个目录:

  • 如果 DB2 的用户与实例用户不同:
    • 在 sqllib 中创建 splog 目录,并将其属性更改为 777。
    • 在 splog 目录中,创建一个与数据库名称相同的目录。
    • 在这个数据库名称目录中创建一个 archive 目录。
  • 如果 DB2 fenced 用户与实例用户相同:
    • 这个框架将创建必需的文件和目录,因为日志框架具有创建这些目录和文件所需的权限。
    • 如果希望将 splog 文件夹与 sqllib 文件夹分开存放,可以为 splog 创建一个单独的挂载点,并创建一个指向这个挂载点的符号链接 ~/sqllib/splog。

日志管理存储过程 DB2.UPDATE_SP_CONFIG 会创建或更改 log 目录中的 .splogrc 配置文件。例如,如果数据库名为 “sample”,并且 DB2 fenced 用户与实例用户不同,那么我们可以先创建 sqllib/splog 目录,然后创建 sqllib/splog/sample 目录,最后再创建 sqllib/splog/sample/archive 目录。如果 DB2 fenced 用户与实例用户相同,那么这个框架就可以为我们自动创建这些目录。

清单 2. 配置文件
db2inst1@p595 /home/db2inst1/sqllib/splog/sample=>ls -al
drwxrwsrwx   3 db2inst1 db2grp1         512 Dec 22 13:53 .
drwxrwsrwx   3 db2inst1 db2grp1         512 Dec 21 23:45 ..
-rw-r--r--   1 db2inst1 db2grp1        6811 Dec 22 15:46 .splogrc
-rw-r--r--   1 db2inst1 db2grp1        5192 Dec 22 13:50 051222034739.log
-rw-r--r--   1 db2inst1 db2grp1        3904 Dec 22 15:45 051222195007.log
drwx--S---   2 db2inst1 db2grp1         512 Feb 09 1971  archive
-rw-r--r--   1 db2inst1 db2grp1      768700 Dec 22 15:46 sp.log

DB2.UPDATE_SP_CONFIG 存储过程会在 ~/sqllib/splog/YourDBName 目录中创建或更新 .splogrc 文件。

清单 3. 配置文件
$ cat .splogrc
# *************************************************************************
# ** (C) COPYRIGHT International Business Machines Corp. 2000 - 2005
# ** All Rights Reserved.
# **
# ** Vikram Khatri vkhatri(at)us.ibm.com
# ** Use it at your own risk. No warranties implied and no support available
# *************************************************************************
global_logging=Y
log_split=N
testsp=No

直接对配置文件进行编辑应该没有其他任何要求。即使直接编辑这个文件,也无法立即看到这些参数的效果,因为存储过程 DB2.UPDATE_SP_CONFIG 还需要对共享内存进行更新,DB2.LOGINFO 和 DB2.LOGGER 主要从这里获取日志参数。

在创建日志标记(例如 TESTSP)之后,就可以在调用 DB2.OPEN_LOG 启动日志功能时使用这个标记。在调用 OPEN_LOG 之后再调用 DB2.LOGINFO 只会检查共享内存中是否存在 TESTSP 标记。如果存在该标记,则会记录 LOGINFO 消息。DB2.LOGGER 会使用 TESTSP 标记检查 DEBUG 参数,并确定它是否被设置为 Y,如果是,则记录调试消息。

获取存储过程的配置

由于根本没有必要具有访问 .splogrc 配置文件的访问权限,因此可以利用 DB2.GET_SP_CONFIG 存储过程的帮助来查看日志标记的设置。

清单 4. 获取存储过程的日志参数
db2inst1@p595 /home/db2inst1/sqllib/splog/sample=>db2 "call db2.get_sp_config()" 
  Result set 1
  --------------
  SP_NAME              DEBUG_MODE
  -------------------- ----------
  TESTSP               No

获取存储过程日志文件

存储过程的日志文件保存在 ~/sqllib/splog/YourDBName 目录中。如果已经使用 LOG_SPLIT=Y,则会为每个 DB2 代理都生成一个日志文件。如果没有访问 DB2 服务器的登录访问权限,那么还可以通过使用 DB2.GET_SP_LOG 存储过程来获取日志文件的内容。这个过程需要使用日志文件名作为输入参数。如果 LOG_SPLIT 被设置为 N,那么可以使用 sp.log 作为日志文件名。否则,可以使用 db2 list applications 命令来确定应用程序的 ID,并使用应用程序 ID 的最后部分作为日志文件名。

清单 5. 获取存储过程的日志文件内容
db2inst1@p595 /home/db2inst1/sqllib/splog/sample=>db2 "call db2.get_sp_log('sp.log')" 
  Result set 1
  --------------
12-25-2005 22:29:39.839:[testsp:051226021028] Begin Stored Procedure *** [681574408]
12-25-2005 22:29:39.839:[testsp:051226021028] The database name from dbInfo is SAMPLE
12-25-2005 22:29:39.839:[testsp:051226021028] The HOME variable is /home/db2inst1
12-25-2005 22:29:39.839:[testsp:051226021028] The Application ID is *LOCAL.db2inst1.051226021028
12-25-2005 22:29:39.839:[testsp:051226021028] End   Stored Procedure *** Elapsed 0.000
12-25-2005 22:36:04.048:[testsp:051226021028] Begin Stored Procedure *** [681574408]
12-25-2005 22:36:04.048:[testsp:051226021028] The database name from dbInfo is SAMPLE
12-25-2005 22:36:04.048:[testsp:051226021028] The HOME variable is /home/db2inst1
12-25-2005 22:36:04.048:[testsp:051226021028] The Application ID is *LOCAL.db2inst1.051226021028
12-25-2005 22:36:04.048:[testsp:051226021028] End   Stored Procedure *** Elapsed 0.000

日志框架存储过程

在 SQL-PL 存储过程中可以使用 4 个日志存储过程来记录消息。表 1 给出了详细的内容。

表 1. 用来记录日志的存储过程
存储过程名语法参数
DB2.OPEN_LOGCALL DB2.OPEN_LOG (log_token_name, handle)

log_token_name —— 日志标记名。如果已经使用 DB2.UPDATE_SP_CONFIG 在共享内存中设置了这个值,那么将执行日志操作,否则将忽略它。

handle —— 在 SQL-PL 代码中声明的变量名。它需要声明为 CHAR(48) FOR BIT DATA 类型。

DB2.LOGINFOCALL DB2.LOGINFO (handle, message)

handle —— 调用 DB2.OPEN_LOG 时所设置的句柄。

message —— 希望记录日志的信息消息。

DB2.LOGGERCALL DB2.LOGGER (handle, message)

handle —— 调用 DB2.OPEN_LOG 时所设置的句柄。

message —— 希望记录日志的信息消息。

DB2.CLOSE_LOGCALL DB2.CLOSE_LOG (handle)

handle —— 调用 DB2.OPEN_LOG 时所设置的句柄。

将一切准备就绪

为了构建这个日志框架,需要数据库管理员帮我们设置存储过程的日志目录并创建一个用户临时表空间。

DBA 的操作

  1. 在 ~/sqllib 目录中创建一个 splog 目录。DBA 可以创建一个符号链接 ~/sqllib/splog,指向我们有权查看的日志文件的其他地方。

  2. 对 ~/sqllib/splog 设置适当的文件权限,这样 DB2 fenced 用户就可以具有对其进行操作所需的所有权限了。

  3. 在 ~/sqllib/splog 目录中创建一个名字与数据库名相同的目录。

  4. 在 ~/sqllib/splog/YourDBName 目录中创建一个 archive 目录。

  5. 创建一个用作全局临时表的用户表空间。日志管理存储过程 DB2.GET_SP_CONFIG 和 DB2.GET_SP_LOG 都会使用一个全局临时表将结果集返回给客户机。下面的脚本给出了一个例子,我们可以对其进行更改,从而在自己的环境中创建一个用户临时表空间。

    清单 6. 创建用户临时表空间
    CREATE USER TEMPORARY TABLESPACE dgtt IN DATABASE 
    PARTITION GROUP PG0 PAGESIZE 4096 MANAGED BY SYSTEM
    USING ('/stagefs/dbins17a/dgtt/dgtttbsp.tablespace') 
    ON DBPARTITIONNUMS (0)
    EXTENTSIZE 32
    PREFETCHSIZE AUTOMATIC
    BUFFERPOOL IBMDEFAULTBP
    DROPPED TABLE RECOVERY OFF;

开发人员的操作

这个日志框架已经在 64 位的 AIX® 5.3.1 和 64 位的 Linux™ 平台(Suse 9 和 RHEL 3)进行了测试。在这个特定的 UNIX® 32/64 位平台上,需要使用 ~/sqllib/samples/c 中的 bldrtn 和 embprep 程序,而不是使用本框架所提供的文件。特定于平台的 bldrtn 和 embprep 程序会使用正确的编译器和链接选项。

表 2. 日志源文件
文件名目的
splogger.sqc使用 C 编写的本地程序,以及日志方法使用的存储过程封装程序。
splogger.exp将使用共享库导出的方法名称。
spcatDB2 catalog 脚本,用于将外部共享库程序注册成 DB2 模式中的存储过程。
spalterDB2 catalog 脚本,用于对存储过程执行 ALTER 操作,将其更改为一个不同的共享库名,这样当 DB2 KEEPFENCED 被设置为 YES 时,DB2 就可以将新的库加载到内存中。
makefile编译源代码使用的 Makefile。
bldrtnmakefile 调用这个脚本来编译 C 源代码。请使用适合您的 UNIX 版的 bldrtn 文件(在 ~/sqllib/samples/c 目录中),而不是使用此处提供的文件。
embprepbldrtn 会调用这个脚本,通过 DB2 PREP 来预处理 sqc 文件,并使用 BIND 将其绑定到数据库上。请使用适合您的 UNIX 版的 embprep 文件(在 ~/sqllib/samples/c 目录中),而不是使用此处提供的文件。

通过 SQL-PL 存储过程来使用这个日志框架

下面这个 SQL-PL 存储过程 TESTSP 的例子显示了日志存储过程的用法。

清单 7. SQL-PL 中的日志框架
$ db2 CONNECT TO SAMPLE
$ db2 "call DB2.UPDATE_SP_CONFIG('TESTSP','Y')"
$ db2 CONNECT RESET
$ db2 -td@ -f testsp.sql
File testsp.sql
-----------------		
CONNECT TO SAMPLE
@
SET CURRENT SCHEMA = 'TEST'
@
DROP PROCEDURE TESTSP
@
CREATE PROCEDURE TESTSP()
LANGUAGE SQL
BEGIN
   DECLARE h  CHAR(48) FOR BIT DATA;
   DECLARE count INTEGER DEFAULT 0;
   CALL DB2.OPEN_LOG('TESTSP',h);
   SET count = count + 1;
   CALL DB2.LOGINFO(h,'this is a test loginfo '||CHAR(COALESCE(count,0))||' message');
   SET count = count + 1;
   CALL DB2.LOGGER(h,'this is a test logger '||CHAR(COALESCE(count,0))||' message');
   SET count = count + 1;
   CALL DB2.LOGINFO(h,'this is a test loginfo '||CHAR(COALESCE(count,0))||' message');
   SET count = count + 1;
   CALL DB2.LOGGER(h,'this is a test logger '||CHAR(COALESCE(count,0))||' message');
   SET count = count + 1;
   CALL DB2.LOGINFO(h,'this is a test loginfo '||CHAR(COALESCE(count,0))||' message');
   SET count = count + 1;
   CALL DB2.LOGGER(h,'this is a test logger '||CHAR(COALESCE(count,0))||' message');
   SET count = count + 1;
   CALL DB2.LOGINFO(h,'this is a test loginfo '||CHAR(COALESCE(count,0))||' message');
   SET count = count + 1;
   CALL DB2.LOGGER(h,'this is a test logger '||CHAR(COALESCE(count,0))||' message');
   CALL DB2.CLOSE_LOG(h);
END
@
CONNECT RESET
@
Call TESTSP Stored Procedure
----------------------------
				
$ db2 "call test.testsp(?)"
图 2. 日志文件的输出
日志文件的输出
日志文件的输出

通过 C 存储过程来使用日志框架

由于日志框架程序是使用 C 语言编写的,因此可以在 C 存储过程中直接使用它们,而不需要再调用日志存储过程。下面的示例 C 存储过程使用了日志 API。要使用这些日志方法,则需要将 splogger 共享库包含到自己的项目中。

我们需要在 C 存储过程中包含 logger.h,它定义了日志方法的原型。

清单 8. 日志框架 API —— logging.h 头文件
#define BYTEHANDLESIZE 48 		
void openLog(char *name, char *handle);
void closeLog(char *handle);
void setDBName(struct sqludf_dbinfo *dbInfo);
void logger(char *handle, char *fmt, ... );
void logginfo(char *handle, char *fmt, ... );
清单 9. 显示日志方法 API 用法的示例 C 存储过程
SQL_API_RC SQL_API_FN Snow
(
   char outMessage[100],
   sqlint16 *outMessageNullInd,
   char sqlstate[6],
   char qualName[28],
   char specName[19],
   char diagMsg[71],
   struct sqludf_dbinfo *dbInfo
)
{
  char byteHandle[BYTEHANDLESIZE];  
  char dbName[128] = "";
  setDBName(dbInfo);		  
  openLog("TESTSP", byteHandle);
  strncpy(outMessage, (char *)(dbInfo->dbname), dbInfo->dbnamelen);
  outMessage[dbInfo->dbnamelen] = '\0';
  logger(byteHandle, "The database name from dbInfo is %s", outMessage);
  strcat(outMessage, "::");  
  logger(byteHandle, "The HOME variable is %s", getenv("HOME"));
  strcat(outMessage, getenv("HOME"));
  strcat(outMessage, "::");
  strcat(outMessage, (char *)(dbInfo->appl_id));
  logger(byteHandle, "The Application ID is %s", (char *)(dbInfo->appl_id));
  *outMessageNullInd = 0;
  closeLog(byteHandle);
  return 0;  
}

除了日志 API 调用之外,还需要注意 setDBName 的用法。C 程序必须调用这个方法,因为它要从 dbInfo DB2 结构中获取数据库名来设置日志文件名。

请使用下面的 SQL 语句来注册这个示例存储过程。

清单 10. 注册示例存储过程
CREATE PROCEDURE DB.SNOW
(
  OUT   MSG       VARCHAR(100)
)
DYNAMIC RESULT SETS 0
DETERMINISTIC
LANGUAGE C
PARAMETER STYLE SQL
DBINFO
FENCED NOT THREADSAFE
NO SQL
PROGRAM TYPE SUB
EXTERNAL NAME 'splogger!Snow';

常见问题解答

问题:在使用 makefile 来编译源代码时,看到 “bldrtn not found” 错误,这是怎么回事?

解答:bldrtn 文件的权限可能被更改过了,这与将文件复制到系统中的方法有关。请使用 chmod 命令来更改 bldrtn、embprep 和 spcat 的权限。例如:
$ chmod +x embprep bldrtn spcat bld spalter

问题:当编译 splogger 时,在执行 spcat 的过程中会看到 “database connection does not exist” 错误,这是怎么回事?在 spcat 中我没有看到任何 connect 语句。连接应该在哪里进行呢?

解答:在 spcat 中没有 connect 语句,它假设连接数据库是隐式的。

清单 11. 如何使用隐式连接
$ db2set DB2DBDFT=<your database name>
$ db2stop force
$ db2start						
$ db2 activate db <your database name>

问题:我没有访问 DB2 的实例访问权限,应该如何编译和构建 splogger 呢?

解答:请更改 makefile,并为 ALIAS、UID 和 PWD 指定适当的值。在 spcat 和 spalter 中放置一条 CONNECT 语句。

问题:bld 脚本的用途是什么?

解答:bld 脚本用来将 splogger 编译成一个单独的程序,而不是一个共享库,这样做的目的是为了进行测试。它生成一个 splog 二进制文件,可以用该文件来执行 splogger.sqc 中的主程序。

问题:在运行 make 命令时,看到了 “make: 1254-004 The error code from the last command is 4.” 错误,这是怎么回事?

解答:这是 spcat 的一个正常结果。它对 DB2 日志存储过程执行 DROP PROCEDURE 命令,如果在数据库中不存在日志存储过程,就会看到上面的错误。下一次运行 spcat 时,就不会再看到这个错误。

问题:如果我对这个框架进行了一些更改,并且将在自己的 SQL-PL 存储过程中使用这些更改,那么应该如何使用这个日志程序呢?由于性能方面的原因,我们不能将 KEEPFENCED 设置为 NO,因为在我们的系统中还运行了其他外部 C 存储过程。

解答:请使用 CLP 命令 UPDATE DBM CFG USING KEEPFENCED YES 来设置 DB2 实例级参数 KEEPFENCED=YES。重新启动实例之后,这个外部存储过程库就会由 DB2 在首次调用存储过程时被加载到内存中。然后这些共享库会一直保存在内存中,直到重新启动实例为止。如果对这个外部库进行更改,并将这些更改复制到 ~/sqllib/function 目录中,那么 DB2 将不会使用它,因为原来的库已经被加载到内存中了。这不是 DB2 的限制,而是操作系统本身就被设计成这样。(不过在大型机上可以加载或卸载共享库。)

显然,我们希望在某个产品系统上将 KEEPFENCED 参数设置为 YES,如果必须对外部存储过程(包括 splogger)进行更改,那么有一种替代解决方案可以强迫 DB2 加载新的库。spalter 脚本就使用了这种方法。它对此共享库进行重命名,并使用新的库名称来更改存储过程。DB2 会加载这个新的库,但是旧的库仍然会一直保存在内存中,直到重新启动实例为止。

清单 12. 当 KEEPFENCED=YES 时对共享库进行更改
#! /bin/ksh
TOK=$(date +"%y%m%d%H%M%S")
SHLIBNAME=splogger$TOK
rm -f ~/sqllib/function/splogger*
cp -f splogger ~/sqllib/function/$SHLIBNAME
db2 -tv << !EOF
ALTER PROCEDURE DB2.UPDATE_SP_CONFIG EXTERNAL NAME '${SHLIBNAME}!UpdateSPConfig';
ALTER PROCEDURE DB2.GET_SP_CONFIG    EXTERNAL NAME '${SHLIBNAME}!GetSPConfig';
ALTER PROCEDURE DB2.GET_SP_LOG       EXTERNAL NAME '${SHLIBNAME}!GetSPLog';
ALTER PROCEDURE DB2.OPEN_LOG         EXTERNAL NAME '${SHLIBNAME}!OpenLog';
ALTER PROCEDURE DB2.CLOSE_LOG        EXTERNAL NAME '${SHLIBNAME}!CloseLog';
ALTER PROCEDURE DB2.LOGGER           EXTERNAL NAME '${SHLIBNAME}!Logger';
ALTER PROCEDURE DB2.LOGINFO          EXTERNAL NAME '${SHLIBNAME}!Loginfo';
!EOF

问题:在日志文件中,在 “Begin Stored Procedure *** [681574408]” 一行中有一个数字。这个数字是什么?

解答:这是 splogger 库所使用的共享内存的位置。当停止 DB2 时,就会释放 DB2 所使用的共享内存。对于 splogger 共享库来说,无法确定它何时会释放这个共享内存。不过可以使用这个数字来释放这个库所使用的共享内存。请使用 ipcrm -m 681574408 命令来释放这段内存。

问题:我正在使用这个框架,但却没有看到任何日志文件。日志文件应该在哪里呢?

解答:这是最常见的问题。您可以在 /tmp 目录中查找是否有日志文件。这与所使用的用户 ID 和 ~/sqllib/splog 目录的文件权限有关。请让 DBA 帮助您在 ~/sqllib 目录中创建一个 splog 目录,并将这个 splog 的全部权限授予 DB2 fenced 用户。您也可以在 DB2 fenced 用户的根目录中创建一个 splog 目录,并在 ~/sqllib/splog 中创建一个指向这个 splog 目录的软链接 (soft link)。日志存储过程被设计用来作为一个 FENCED 进程运行,它们都是用 DB2 fenced 用户 ID 来运行的。

问题:这个框架中的 hardw 方法是什么?它的用途是什么?

解答:这个日志框架被设计用于调试存储过程,hardw 方法用来调试这些日志程序。

问题:系统已经不再向文件中写日志了。在日志文件中我看不到任何新消息了。到底发生了什么?

解答:请验证包含 splog 目录的文件系统是否还有空间。

问题:在使用这个框架时还需要调整其他 DB2 参数吗?

解答:不需要。

问题:DB2.GET_SP_LOG 的输出一团糟。什么地方出问题了?

解答:存储过程 DB2.GET_SP_LOG 一次要从日志文件中读取 4000 个字节。请将输出结果重定向到某一文件中。

问题:我试图在 Windows® 平台上编译这个框架,却看到很多编译器错误。我应该怎样做呢?

解答:这个框架只在 AIX 和 Linux 平台上进行了测试。我正在使用 C# 将这个框架转换到 Windows 平台上。

问题:我试图在 Solaris 平台上编译这个框架,却看到很多编译器错误。我应该怎样做呢?

解答:这个框架还没有在 Solaris 平台上进行测试。请把错误告诉我,我会试着解决这些问题。

问题:当我试图在 Linux 平台上编译这个框架时,看到了很多编译器错误。我应该怎样做呢?

解答:将 bldrtn 和 embprep 文件从 ~/sqllib/samples/c 复制到您的目录中,并重新再次尝试。

问题:有任何计划要将这个框架加入到某个 DB2 产品中吗?

解答:到目前为止还没有。

问题:我可以对这个框架进行更改并在自己的产品中使用吗?

解答:当然。这是一个开放源码的存储过程日志器。请在每个源代码中维护 IBM 的版权信息。

问题:当我试图使用 ipcrm 命令来释放共享内存时,看到一个错误说不允许我删除这段共享内存。应该如何解决这个问题呢?

解答:日志器存储过程是使用 DB2 fenced 用户的身份来运行的。请以 DB2 fenced 用户的身份登录系统,并再次尝试删除这段共享内存。如果 DB2 fenced 用户是 nobody,请让系统管理员帮助您使用 ipcrm 命令来删除这段共享内存。

问题:我已经使用这个框架,存储过程已经进行了全面的调试,现在不需要任何日志调用了。但即使是几毫秒的时间,我也希望节省出来,这样可以用来改进性能。关闭日志功能最好的方法是什么?

解答:可以通过将 global_logging 设置成 N 来全局地关闭日志功能。然而,如果希望节省存储过程执行时的每一毫秒的时间,那么在确信 SQL-PL 和外部存储过程都已经准备好之后,就可以注释掉所有的 CALL DB2. 语句。我使用下面这个简单的 sed 脚本来注释掉这些调用,如果以后需要,可以再重新使用它们。

清单 13. 如果全局关闭日志功能
db2 "call db2.update_sp_config('global_logging','y')"
清单 14. 如果在 SQL-PL 存储过程中注释掉 CALL DB2. 语句
#!/bin/ksh
# Comment Logger Calls		
cat actsp.sql | sed 's/CALL DB2./--CALL DB2./' > sp.sql
db2 -td@ -f sp.sql 
#!/bin/ksh
# Uncomment Logger Calls		
cat sp.sql | sed 's/--CALL DB2./CALL DB2./' > actsp.sql
db2 -td@ -f actsp.sql

问题:我对增强这个框架的功能很感兴趣。此时我应该怎样做呢?

解答:这是一个提供 DB2 存储过程日志功能的开放源码框架。欢迎用户贡献自己的能力并提供反馈。

结束语

日志框架可以用来将日志信息和调试信息记录到日志文件中,并进行一些性能方面的评测。可以在产品系统中使用这个框架,在系统运行时激活或关闭存储过程的日志功能,它可以帮助我们理解和管理存储过程。

致谢

特别感谢 Burt VialPando 和 Nora Sokolof 对本文的审阅。


下载资源


相关主题

  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文
  • 文章 调优 SQL 过程developerWorks,2005 年 2 月)介绍了使用开放源码 SQL Profiling 工具对 SQL-PL 进行优化的提示和技巧。
  • 文章 SQL 过程语言:平台间的差异developerWorks,2004 年 12 月)概括了 LUW 和大型机上 SQL-PL 之间的区别。
  • 文章 SQL 过程跟踪developerWorks,2004 年 9 月)介绍了跟踪 SQL-PL 和外部过程使用的一个 API。
  • 文章 概要分析 SQL 过程developerWorks,2004 年 7 月)介绍了一个基于 GUI 的 Java 工具,可以使用该工具对执行过程进行配置,并可视化地呈现使用每个过程源代码所搜集到的数据。
  • 文章 SQL 过程的性能: 提示和技巧developerWorks,2003 年 1 月)介绍了如何提高 SQL-PL 存储过程性能的技巧和技术。
  • DB2 SQL Procedure Language for Linux, UNIX, and Windows 一书是有关 DB2 SQL-PL 语言的一个很好的参考资料。

评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Information Management
ArticleID=161498
ArticleTitle=记录 DB2 UDB 的存储过程消息
publish-date=05152006