监控 DB2 9.7,第 1 部分: 用新的 DB2 9.7 监控表函数模拟数据重置

本文描述一种对 IBM® DB2® for Linux®, UNIX®, and Windows® 中各种监控表函数返回的监控数据执行每会话重置的简单方法。这在功能上与 DB2 系统监控快照 API 中的重置功能相当。本文的下载文件包含两个脚本,它们为 DB2 9.7 中引入的众多监控表函数实现这种方法。本文的重点是 DB2 9.7 中引入的新的监控表函数,但是可以对 DB2 中报告监控数据的其他表函数轻松地应用同样的方法。

Scott Walkty, 软件开发人员, IBM

Scott Walkty 是 DB2 Monitoring and Workload Manager 团队的软件开发人员。他是 Workload Manager 解决方案最初的开发人员之一,过去两年从事 DB2 的各种监控改进。他曾经在 DB2 Tools 团队工作过五年。Scott 拥有 University of Manitoba 的计算机科学硕士学位。



2010 年 12 月 13 日

背景和动机

DB2 for Linux, UNIX, and Windows Version 9.7 引入了许多新的监控 SQL 表函数,它们以前缀 MON_ 开头(例如,MON_GET_CONNECTIONMON_GET_TABLE,它们分别报告连接和表的监控数据)。这些新的表函数可以替代 9.7 版之前的快照监控表函数,它们效率更高、更轻量。另外,它们报告无法通过系统监控接口获取的许多新的监控元素,包括一整套耗时元素,数据库服务器在这些元素上花费时间处理请求。这些新的监控接口报告系统总体数据(聚合为事务、连接、服务类和工作负载)以及特定对象的数据(例如索引和表)。

与其他 DB2 监控接口(比如 GET SNAPSHOT 等快照命令或 db2GetSnapshot 等快照 API)相比,使用 SQL 表函数执行监控有许多好处。在支持 SQL 的任何语言中都可以使用 SQL 接口,而且因为监控数据以表行的形式返回,这个接口支持 SQL 语言丰富的查询功能。例如,只需在列出监控表函数返回的应用程序数据的查询中添加 WHERE 子句,就可以搜索 CPU 消耗量最大的应用程序。

新的监控 SQL 表函数的缺点是没有每会话重置功能,现有的系统监控快照 SQL 接口也有这个缺点。它们报告的监控数据总是相对于激活数据库时。也就是说,在每个数据库分区上激活数据库时,从 0 开始计数,然后一直增加,直到无效为止。生成数据快照的命令行和 C API 接口可以对监控数据执行每会话重置。每会话意味着重置只对于执行重置的应用程序有效。应用程序执行重置之后,它会看到相对于重置时刻的监控数据(重新从 0 开始计数)。执行监控的其他应用程序不受每会话重置影响,它们仍然访问相对于激活数据库时的值。每会话重置让不同的应用程序可以得到不同的监控数据视图,因为所有监控应用程序不必在同一时间重置数据。

监控数据重置功能在许多场景中都有用。例如,假设希望查明在下午 1 点到 2 点之间在 TEST.EMPLOYEE 表上执行了多少次表扫描。如果使用快照监控命令行接口,可以像下面这样做:

  1. 在下午 1 点,连接数据库服务器并重置监控数据:
    RESET MONITOR FOR DATABASE <dbalias>
  2. 在下午 2 点,在同一连接上,执行以下命令查询所有表的监控数据。因为使用同一连接而且在下午 1 点执行了重置,所以监控数据覆盖从下午 1 点到 2 点的时间段。
    GET SNAPSHOT FOR TABLES ON <dbalias>
  3. 搜索返回的监控数据(数据以文本报告的形式返回)并寻找 TEST.EMPLOYEE 表的数据。报告的表扫描次数表示在下午 1 点到 2 点之间在这个表上执行的扫描。

对 SQL 表函数返回的监控数据执行每会话重置

通过使用几个用户定义函数(UDF)和过程扩展 DB2 9.7 的基本功能,就可以轻松地对 SQL 表函数返回的监控数据执行每会话重置。实现每会话重置需要的东西如下:

  • 感兴趣的每个函数的每会话基线数据存储。基线数据由在执行重置时捕捉的监控计数器值组成。
  • 用于捕捉基线数据(即执行 “重置”)的接口。
  • 用于计算出相对于基线的差值的接口。

下面是可以满足这些需求的推荐方法:

  1. 使用为基线数据的每会话存储创建的全局临时表(CGTT)(对于每个表函数,使用一个 CGTT)。CGTT 的定义像常规表一样存储在编目中,当一个连接第一次使用 CGTT 时对它进行实例化。使用 CGTT 的每个连接拥有自己的 CGTT 拷贝,而且只能看到自己拷贝中的数据。
  2. 使用一个存储过程捕捉基线数据。这个过程对监控表函数执行选择并把返回的数据插入 CGTT 中以取得基线。
  3. 创建一个表函数,它包装底层的监控表函数,产生相对于 CGTT 中存储的基线数据的差值。这个包装器函数的输入值与底层监控表函数相同,所以它也能够筛选返回的数据。它的输出与监控表函数的输出匹配,但是多一列。这一列包含取得基线时的时间戳(也就是说,包装器表函数报告的监控时间段是从基线时间戳到当前时间戳)。对于不报告计数器值的任何输出列(例如基于状态的输出),包装器直接返回底层监控表函数产生的数据,不做修改。

    注意:如果 CGTT 中不存在基线数据,包装器函数直接返回底层监控表函数产生的数据。

下面的脚本说明如何按上述方法对 MON_GET_TABLE 表函数返回的监控数据执行每会话重置,此函数报告数据库中特定表的监控数据。

清单 1 给出一个简单的 UDF,它处理对象的基线数据不存在或者发生计数器翻转的情况(这种情况不太可能出现,但是能够处理)。

清单 1. 用于处理无基线情况的 UDF
CREATE FUNCTION MONDELTA.METRIC_DELTA( CURR BIGINT, 
                                       BASELINE BIGINT )
RETURNS BIGINT
LANGUAGE SQL
CONTAINS SQL
DETERMINISTIC
RETURN CASE WHEN BASELINE IS NULL
              THEN CURR
            WHEN BASELINE <= CURR
              THEN CURR - BASELINE
            ELSE
              CURR + (9223372036854775807 - BASELINE)
            END

清单 2 定义用于存储表的基线监控数据的 CGTT。

清单 2. 用于存储基线监控数据的 CGTT
CREATE GLOBAL TEMPORARY TABLE MONDELTA.TABLE_BASELINE AS
     (SELECT CURRENT TIMESTAMP AS BASELINE_TIMESTAMP, 
             TABSCHEMA, 
             TABNAME,
             MEMBER,
             DATA_PARTITION_ID,
             TABLE_SCANS,
             ROWS_READ,
             ROWS_INSERTED,
             ROWS_UPDATED,
             ROWS_DELETED,
             OVERFLOW_ACCESSES,
             OVERFLOW_CREATES,
             PAGE_REORGS
       FROM TABLE(MON_GET_TABLE('','',-1)) AS T) DEFINITION ONLY
   ON COMMIT PRESERVE ROWS
   NOT LOGGED 
   ON ROLLBACK PRESERVE ROWS

清单 3 提供用于捕捉表的基线监控数据的过程。

清单 3. 用于捕捉基线监控数据的过程
CREATE PROCEDURE MONDELTA.GET_TABLE_BASELINE()
LANGUAGE SQL
BEGIN
   DELETE FROM MONDELTA.TABLE_BASELINE;
   INSERT INTO MONDELTA.TABLE_BASELINE 
      SELECT CURRENT TIMESTAMP, 
             TABSCHEMA, 
             TABNAME,
             MEMBER,
             DATA_PARTITION_ID,
             TABLE_SCANS,
             ROWS_READ,
             ROWS_INSERTED,
             ROWS_UPDATED,
             ROWS_DELETED,
             OVERFLOW_ACCESSES,
             OVERFLOW_CREATES,
             PAGE_REORGS
      FROM TABLE(MON_GET_TABLE('','',-2)) AS T;
END

清单 4 定义一个包装 MON_GET_TABLE 表函数的表函数。正如上面提到的,包装器函数的输入和输出与 MON_GET_TABLE 函数相同,但是输出中增加一个基线时间戳列。如果基线存在,返回的所有计数器值相对于基线。如果基线不存在,这个表函数与 MON_GET_TABLE 等效。

清单 4. MON_GET_TABLE 的包装器函数,它返回相对于基线监控数据的监控元素
CREATE FUNCTION MONDELTA.GET_TABLE_DELTA( 
    TABSCHEMA VARCHAR(128),                                          
    TABNAME VARCHAR(128),
    MEMBER INTEGER)
RETURNS TABLE (
       BASELINE_TIMESTAMP          TIMESTAMP,
       TABSCHEMA                   VARCHAR(128),
       TABNAME                     VARCHAR(128),
       MEMBER                      SMALLINT,
       TAB_TYPE                    VARCHAR(14),
       TAB_FILE_ID                 BIGINT,
       DATA_PARTITION_ID           INTEGER,
       TBSP_ID                     BIGINT,
       INDEX_TBSP_ID               BIGINT,
       LONG_TBSP_ID                BIGINT,
       TABLE_SCANS                 BIGINT,
       ROWS_READ                   BIGINT,
       ROWS_INSERTED               BIGINT,
       ROWS_UPDATED                BIGINT,
       ROWS_DELETED                BIGINT,
       OVERFLOW_ACCESSES           BIGINT,
       OVERFLOW_CREATES            BIGINT,
       PAGE_REORGS                 BIGINT)
LANGUAGE SQL
NOT DETERMINISTIC
NO EXTERNAL ACTION
READS SQL DATA
RETURN SELECT 
    B.BASELINE_TIMESTAMP,
    A.TABSCHEMA,
    A.TABNAME,
    A.MEMBER,
    A.TAB_TYPE,
    A.TAB_FILE_ID,
    A.DATA_PARTITION_ID,
    A.TBSP_ID,
    A.INDEX_TBSP_ID,
    A.LONG_TBSP_ID,
    MONDELTA.METRIC_DELTA(A.TABLE_SCANS, B.TABLE_SCANS),
    MONDELTA.METRIC_DELTA(A.ROWS_READ, B.ROWS_READ),
    MONDELTA.METRIC_DELTA(A.ROWS_INSERTED, B.ROWS_INSERTED),
    MONDELTA.METRIC_DELTA(A.ROWS_UPDATED, B.ROWS_UPDATED),
    MONDELTA.METRIC_DELTA(A.ROWS_DELETED, B.ROWS_DELETED),
    MONDELTA.METRIC_DELTA(A.OVERFLOW_ACCESSES, B.OVERFLOW_ACCESSES),
    MONDELTA.METRIC_DELTA(A.OVERFLOW_CREATES, B.OVERFLOW_CREATES),
    MONDELTA.METRIC_DELTA(A.PAGE_REORGS, B.PAGE_REORGS)
FROM TABLE(MON_GET_TABLE(TABSCHEMA,TABNAME,MEMBER)) A
             LEFT OUTER JOIN MONDELTA.TABLE_BASELINE B
             ON (A.TABSCHEMA = B.TABSCHEMA AND
                 A.TABNAME = B.TABNAME AND
                 ((A.DATA_PARTITION_ID = B.DATA_PARTITION_ID) OR
                  (A.DATA_PARTITION_ID IS NULL AND B.DATA_PARTITION_ID IS NULL)) AND
                 A.MEMBER = B.MEMBER)

清单 5 是重置/清除表的基线监控数据的过程。可以使用此过程清除表函数的基线数据,让相应的差值函数返回的数据再次与数据库激活时间(即相对于 0 的差值)相对应。

清单 5. 用于重置/清除 MON_GET_TABLE 的基线监控数据的过程
CREATE PROCEDURE MONDELTA.RESET_TABLE_BASELINE()
LANGUAGE SQL
BEGIN
   DELETE FROM MONDELTA.TABLE_BASELINE;
END

现在,再次以前一节中的场景为例,即希望查看在下午 1 点到 2 点之间在 TEST.EMPLOYEE 表上执行的表扫描次数。现在可以这样做:

  1. 在下午 1 点,连接数据库服务器并取得表数据的基线:
    CONNECT TO <dbalias>
    CALL MONDELTA.GET_TABLE_BASELINE()
  2. 在下午 2 点,在同一连接上,使用 GET_TABLE_DELTA 表函数查询 TEST.EMPLOYEE 表的监控数据。这个表函数返回的监控数据相对于在下午 1 点取得的基线。
    SELECT TABLE_SCANS FROM TABLE(MONDELTA.GET_TABLE_DELTA( 'TEST', 'EMPLOYEE', -2)) AS T

注意,在上面的示例中,使用表函数提取感兴趣的表的数据比搜索快照输出中的数据简单得多。


样例脚本

本文的 下载 部分包含一个 zip 文件的链接,其中包含两个 DB2 脚本:mondelta.db2mondeltadrop.db2

mondelta.db2 脚本对下面的监控表函数应用上述包装器函数:

  • MON_GET_TABLE
  • MON_GET_INDEX
  • MON_GET_BUFFERPOOL
  • MON_GET_TABLESPACE
  • MON_GET_CONTAINER
  • MON_GET_WORKLOAD
  • MON_GET_SERVICE_SUBCLASS
  • MON_GET_UNIT_OF_WORK
  • MON_GET_CONNECTION

执行 mondelta.db2 脚本会在数据库中创建以下对象:

  • 辅助函数:
    • MONDELTA.METRIC_DELTA
  • 用于存储基线监控数据的 CGTT:
    • MONDELTA.TABLE_BASELINE
    • MONDELTA.INDEX_BASELINE
    • MONDELTA.BUFFERPOOL_BASELINE
    • MONDELTA.TABLESPACE_BASELINE
    • MONDELTA.CONTAINER_BASELINE
    • MONDELTA.WORKLOAD_BASELINE
    • MONDELTA.SERVICE_SUBCLASS_BASELINE
    • MONDELTA.UNIT_OF_WORK_BASELINE
    • MONDELTA.CONNECTION_BASELINE
  • 用于捕捉基线监控数据的过程:
    • MONDELTA.GET_TABLE_BASELINE
    • MONDELTA.GET_INDEX_BASELINE
    • MONDELTA.GET_BUFFERPOOL_BASELINE
    • MONDELTA.GET_TABLESPACE_BASELINE
    • MONDELTA.GET_CONTAINER_BASELINE
    • MONDELTA.GET_WORKLOAD_BASELINE
    • MONDELTA.GET_SERVICE_SUBCLASS_BASELINE
    • MONDELTA.GET_UNIT_OF_WORK_BASELINE
    • MONDELTA.GET_CONNECTION_BASELINE
  • 报告相对于基线数据的差值的包装器表函数:
    • MONDELTA.GET_TABLE_DELTA
    • MONDELTA.GET_INDEX_DELTA
    • MONDELTA.GET_BUFFERPOOL_DELTA
    • MONDELTA.GET_TABLESPACE_DELTA
    • MONDELTA.GET_CONTAINER_DELTA
    • MONDELTA.GET_WORKLOAD_DELTA
    • MONDELTA.GET_SERVICE_SUBCLASS_DELTA
    • MONDELTA.GET_UNIT_OF_WORK_DELTA
    • MONDELTA.GET_CONNECTION_DELTA

    注意:要想寻找对相应监控接口(例如与 MONDELTA.GET_TABLE_DELTA 对应的 MON_GET_TABLE)的所有输入参数和输出列的描述,请查阅 DB2 Version 9.7 Information Center(见 参考资料 中包含的链接)。每个包装器表函数在输出中添加一个名为 BASELINE_TIMESTAMP 的新列,其他输出列相同。

  • 用于重置基线监控数据的过程:
    • MONDELTA.RESET_TABLE_BASELINE
    • MONDELTA.RESET_INDEX_BASELINE
    • MONDELTA.RESET_BUFFERPOOL_BASELINE
    • MONDELTA.RESET_TABLESPACE_BASELINE
    • MONDELTA.RESET_CONTAINER_BASELINE
    • MONDELTA.RESET_WORKLOAD_BASELINE
    • MONDELTA.RESET_SERVICE_SUBCLASS_BASELINE
    • MONDELTA.RESET_UNIT_OF_WORK_BASELINEi
    • MONDELTA.RESET_CONNECTION_BASELINE

mondeltadrop.db2 脚本丢弃 mondelta.db2 脚本创建的对象。

用以下命令执行脚本:

db2 –td@ –f mondelta.db2
db2 –td@ –f mondeltadrop.db2

注意:

  • 要想成功地运行 mondelta.db2 脚本,必须定义一个用户临时表空间。如果不这样做,脚本就无法定义差值表函数要使用的 CGTT。
  • 样例差值表函数和 CGTT 基于监控表函数的 DB2 9.7 Fix Pack 1 版本。在以后的补丁包或版本中可能会添加新的监控元素。要想报告 DB2 9.7 Fix Pack 1 之后添加的新监控元素的差值,需要手动更新样例差值表函数和 CGTT。

按照前一节中的示例描述的方式使用这些 DB2 脚本创建的对象:

  1. 通过调用 GET_<object>_BASELINE 过程捕捉基线数据。如果在已经捕捉了基线之后调用基线过程,就会把老的基线替换为新的。
  2. 对相应的 GET_<object>_DELTA 函数执行选择,获取相对于最后一次捕捉基线时的监控数据。如果基线不存在,计数器值相对于数据库激活时。
  3. 使用相应的 RESET_<object>_BASELINE 过程重置基线数据。

对 SQL 表函数返回的监控数据执行全局重置

只需简单地修改本文描述的方法,就可以支持全局重置功能。全局重置意味着跨所有会话共享基线监控数据。因此,在取得基线时,所有会话的监控数据都会重置,而不只是当前会话。要想调整此方法以支持全局重置,只需修改表创建语句,让它们创建常规表而不是 CGTT。

清单 6 延续前面描述的示例。它演示如何修改表创建语句以实现全局重置。

清单 6. 实现全局重置的表创建示例
CREATE TABLE MONDELTA.TABLE_BASELINE AS
     (SELECT CURRENT TIMESTAMP AS BASELINE_TIMESTAMP, 
             TABSCHEMA, 
             TABNAME,
             MEMBER,
             DATA_PARTITION_ID,
             TABLE_SCANS,
             ROWS_READ,
             ROWS_INSERTED,
             ROWS_UPDATED,
             ROWS_DELETED,
             OVERFLOW_ACCESSES,
             OVERFLOW_CREATES,
             PAGE_REORGS
       FROM TABLE(MON_GET_TABLE('','',-1)) AS T)

在监控应用程序不保持到数据库服务器的持久连接的情况下,全局重置是有好处的。例如,如果使用 cron 每小时运行一次脚本以捕捉和重置监控数据(换句话说,监控应用程序捕捉每小时的系统监控数据历史),就没有到数据库服务器的持久连接。在这种场景中,每会话重置不适用,因为监控应用程序并不是一直连接数据库服务器。因此,基线数据的生命期一定要比连接长。

全局重置的主要缺点是,在有多个监控应用程序连接数据库服务器的情况下,它会无意间造成副作用。不同的应用程序(可能来自不同厂商)可能有不同的需求。例如,一个应用程序可能每小时监控数据,而另一个应用程序可能每天监控数据。如果执行每小时监控的应用程序执行全局重置,那么每天查询一次监控数据的应用程序只能看到前一小时的数据。


结束语

本文描述了一种对各种监控表函数返回的监控数据执行每会话重置的简单方法。可以下载本文附带的脚本,这些脚本为 DB2 9.7 中引入的众多监控表函数实现这种方法。每会话重置在功能上与 DB2 系统监控快照 API 中的重置功能相当。本文还解释了如何修改脚本以支持监控数据的全局重置。


致谢

作者感谢 Paul Bird 和 David Kalmuk 审阅本文。


下载

描述名字大小
样例 DB2 脚本sampleScripts.zip11KB

参考资料

学习

获得产品和技术

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Information Management
ArticleID=600483
ArticleTitle=监控 DB2 9.7,第 1 部分: 用新的 DB2 9.7 监控表函数模拟数据重置
publish-date=12132010