IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope:Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  Information Management  >

将 DB2 UDB 应用程序迁移到分区数据库中

developerWorks
文档选项

未显示需要 JavaScript 的文档选项


级别: 初级

Bill Wilkins (wilkins@ca.ibm.com), Partner Enablement, IBM Information Management

2004 年 7 月 01 日

通过使用分区数据库,可以最大化 DB2 UDB for Linux、Unix 和 Windows 的可伸缩性(scalability)和容量(capacity)。本文讨论了什么是 DB2 分区,对使用这种功能的好处与代价作了一个概述,并且帮助您决定是否使用分区数据库(partitioned database),以及如何迁移到一个分区数据库中。本文的重点是应用程序的迁移,但是我们也会讨论对于分区数据库的设计、配置和操作等方面的考虑。

简介:什么是 DB2 分区数据库?

用 DB2 SQL Reference中的话说,“ 分区关系数据库是这样一种关系数据库——其中的数据是跨多个分区(又称作节点)管理的。这种跨分区的数据分离对大多数 SQL 语句的用户来说是透明的。"

不管是在一台服务器内,还是跨越多台服务器,要对一个 DB2 Enterprise Server Edition (ESE) 数据库进行分区,需要具备 Database Partitioning Feature (DPF) 功能。DPF 实际上是一种许可(license),并不要求在 ESE 上安装任何附加的产品。

注解

在 DB2 Version 8 之前,对分区的支持是通过 DB2 Enterprise-Extended Edition (EEE) 提供的,这是一种可安装的产品。

本文基于带有 Fixpak 5 的 DB2 V8.1 for Linux、Unix and Windows。 将来,在 DPF 方面还会有所改变,其中有些改变已经被宣布出来,这在后面会讨论到。

如果您对于 DPF 的基础知识还不熟悉,那么应该阅读 DB2 Information Center 关于分区的资料(请参阅 参考资料);或者阅读 developerWorks 文章 "对具有数据库分区功能的 DB2 UDB for Linux, UNIX and Windows Version 8 的生动介绍",最好是一起阅读。

DPF 概念及术语

本文假设您理解 DB2 分区数据库环境的基本架构,但还是让我们先快速浏览一下 DPF 背后的一些关键概念。

通过向 DB2 实例添加一些系统,并在每个系统上安装 DB2,就可以将该实例指定为分区的(partitioned)。添加系统的过程对于 Unix(或 Linux)和 Windows 来说各不相同,但是最后得到的都是一个文件,即 db2nodes.cfg,该文件列出了实例上的一些系统,以及每个系统上的一个或多个分区。术语 节点(node)有时候会产生混淆。这个术语通常指的是在分区实例(partitioned instance)中的一个系统,但有时候又指一个分区本身。例如,在磁盘上一个数据库的目录结构中,可以看到名为 NODE0000、NODE0001 等等的一些目录。每个这样的目录都包含一些文件,这些文件属于由后缀(0,1,等等)指定的那个分区。

当在分区实例中创建一个数据库时,在 Create Database 命令执行时所在的分区中还将创建一些编目表(catalog table),之后,那个分区就被称作 编目分区(catalog partition)数据库分区组(database partition group)(也称 节点组(nodegroup))实际上是一个分区列表以及一个相关的 分区图(partitioning map),分区图是一个包含 4096 个条目的数组,每个条目的值对应于数据库分区组中的某一个分区号。每个数据库分区组在任何地方都可以包括实例中从一个分区到所有分区的任意个分区。

当创建一个新数据库时,就定义了 3 个数据库分区组,其中最重要的数据库分区组是 IBMDEFAULTGROUP,该分区组包括了实例中的所有分区。用于编目表的数据库分区组是 IBMCATGROUP,该分区组只包含编目分区。用于用户数据的缺省表空间 USERSPACE1 是在 IBMDEFAULTGROUP 中创建的,因而跨越了所有分区。

如果在一个其数据库分区组只有一个分区的表空间中创建一个表,那么表中所有的行都将存储在那个分区中,就像在未分区(nonpartitioned)的数据库中一样。在本文中,我们把这样的表称作 未分区表(nonpartitioned table)。 如果一个表是在其数据库分区组有多个分区的表空间中创建的,那么表中的行将分散在这些分区上。并且每一行都完整地存储在某一个分区中。我们称这种表为 分区表(partitioned table),并称使用这种表的应用程序为 分区应用程序(partitioned application)

分区键(partitioning key)是指由一个或多个列组成的列集,每个分区表都必须定义(显式地或缺省地)一个分区键。对于这样的表,在决定将一个新行插入到哪一个分区时,可将该行的分区键值散列(hash)到分区图中的一个条目上,该条目就包含了要使用的分区号。

对于使用 DPF 情况下的数据库设计,关键的性能目标是 并置(collocation)。在并置的情况下,表的连接是在一个或多个分区中进行的,这与 定向(directed)连接或 广播(broadcast)连接不同,在后面这两种连接中,分别涉及发送到特定分区的行和发送到所有分区的行。在后面我们会对此加以讨论。

实现并置的一种方法是定义一个特殊的小表,定义时将其指定为 replicated,意即在每个分区中都将维护该表的一个完整拷贝,这样的话,在进行连接时就不必在分区间发送该表中的行。DPF 是一种 无共享(shared-nothing)环境,也就是说,分区之间不共享资源。每个分区都有其自己的数据子集,分区上的索引只能有对应于该分区中的行的条目。每个分区有自己的缓冲池、数据库配置参数设置、日志文件、管理器等等。如果将分区添加到某个已填充的分区数据库中,则可以对每个数据库分区组中的数据进行 再分配(redistribute),以使每个表分布在所有分区上的行数尽量均匀。

当一个应用程序连接到一个分区数据库时,它便被连接到某个特定的分区(可以由应用程序选择),那么这个分区就称作 协调分区(coordinator partition)。该应用程序的 协调代理(coordinator agent)运行在那个分区上,满足该应用程序的请求,在合适的时候访问数据库中的任意分区或者所有分区。





回页首


为什么使用分区数据库?

关于为什么要使用分区数据库,这里有三个主要原因:

  • 通过利用附加系统的硬件资源,增加数据容量,或者改善吞吐量或响应时间。
  • 为了充分利用 32-位系统上的内存。
  • 为了规避 DB2 对未分区表(以及分区表情况下的每个分区)中字节数和行数的限制。
DPF 主要带来的是利用硬件的能力,而且特定于 DPF 的功能为这种对硬件的利用提供了支持和优化,且不必添加独特的要求更改应用程序的功能。

利用多个系统

这是使用 DPF 的最常见的原因。有些应用程序工作负载会给本来就很大的单个系统强加太多的负担,使其不能以可接受的性能处理这些负载。例如,单个系统的 CPU 或内存容量可能不够,而即使升级该系统也仍然不够,或者升级的代价太昂贵。或者,数据量很大,以致在运行期间超出了系统的 I/O 能力,从而使得性能十分糟糕。

如果使用一个分区实例,向该实例添加新系统和分区,并对数据进行再分配以利用新的分区,那么 DB2 就可以利用额外的硬件资源。通过良好的数据库设计以及 DPF 的无共享方法,就可以大大改善性能和可伸缩性。这些改善在形式上可能是多种多样的,但是使人感兴趣的共同点就是增加了 OLTP 应用程序的吞吐量,而对响应时间也没有负面影响,并且减少了 DSS 查询的时间(或者说提供了不用增加时间就能处理更多查询的能力)。

让我们看一个例子,这是 DPF 的一个典型的最佳案例(best-case)场景。假设在系统 S1 上的一个单分区数据库中,表 T1 有 100M 行。我们运行 select count(*) from T1 ,该语句历时 N 秒钟后完成。然后,我们添加三个额外的系统(跟 S1 一样)到这个实例,每个系统上有一个分区,再将 T1 的数据库分区组在所有分区上进行再分配。如果再次运行同样的 SELECT 语句,就可以使用 4 个系统的 CPU、内存和磁盘 I/O 资源,而不是一个系统的资源,这时应该只需运行 (N * 0.25) 秒。

在单个 32-位系统上对内存的利用

32-位操作系统可以为单个进程或线程提供最大 4GB 内存的虚拟可寻址能力(virtual addressability)。由于每个 DB2 代理(作为一个进程或线程运行)需要访问它所连接到的数据库的 Database Global Memory,而 Database Global Memory 受32-位寻址能力的限制,因此可使用的最大内存是大约 1GB 到 3.3GB(取决于平台)的样子。DB2 缓冲池来自 Database Global Memory,缓冲池的大小通常是决定数据库性能的一个最大的配置因素,因此,Database Global Memory 大小的限制将大大制约性能。

记住,DB2 分区本质上是独立的数据库,有其自己的内存分配,每个分区都有其自己的 Database Global Memory,因而也使用额外内存。例如,假设有一个具有 8GB 内存的系统,如果在此系统上再添加一个分区(有时候也称 逻辑分区),那么就可以利用两倍的内存,从而提供更好的性能。注意,您可以将这个原因与前面提到的那个原因相结合,作为使用 DPF 的理由:即,您可以让多个系统中的每一个系统都具有多个分区,从而更好地利用内存。

规避限制

DB2 对于一个表和常规的 DMS 表空间在每个分区中可以拥有的数据量有一个限制。该限制是基于表的(或者,更确切地说是表空间的)页宽的,如下所示:

4K 页宽 8K 页宽 16K 页宽 32K 页宽
每个分区上的最大表大小(按 GB 计):64128256512
每个分区上的最大常规 DMS 表空间大小(按 GB 计):64128256512

另一个限制是每个表在每个分区上最多只能有 40 亿行,这反映了前面那个表的内容以及每页 255 行的限制。这些限制以及其他 DB2 限制都文档化在 SQL Reference的 Appendix A, "SQL Limits" 中。





回页首


分区数据库是否适合于应用程序?

现在您知道使用分区数据库的好处了,让我们讨论如何判断是否该使用分区数据库。当您读完本节,对应用程序当前和预计将来的行为进行一些分析,那么对于是否值得进行分区这个问题就相当清楚了。在某些情况下,可能需要按照 进行迁移中的内容使用应用程序的一个子集进行某种原型化。

考虑以上三大原因(在 为什么使用分区数据库?这一节中)中是否有任何原因在目前是成立的,或者在不久的将来成立。这要求对运行在未分区数据库上的应用程序有个基本的理解。如果应用程序当前根本不会在 DB2 上运行,那么我们建议首先让该应用程序在未分区的情况下运行,研究在使用了合理大小的数据库时该应用程序的性能。现在,让我们看看这 3 个原因。

这里的讨论是针对当前而言的,但是还应记得考虑将来可能发生的变化,例如数据量的增加。
  • 利用多个系统:
    要评估这种可能性,需要研究当前的性能,并判断限制在哪里。如果系统的容量足以让 CPU、I/O 和内存使用起来良好,那么再去添加额外的系统所能带来的好处就很少。即使有些资源已经快要耗尽,那也可以通过升级系统来解决,而无需添加更多的系统。为了评估将来的需要,应理解一些关键任务的耗时的计算方法,并尽量估计数据库的扩大对这些任务的影响。随着表或数据库的增长(假设没有进行调优),有些操作(例如表扫描和数据库备份)的耗时的增长非常接近线性,并且可以认为,通过使用多个系统并通过分治(divide and conquer)的方法,几乎可以线性地减少耗时。而在另一个极端,对于像使用主键的 SELECT 语句这样的任务,表的大小的影响非常小,因此对于这些任务不大需要使用多个系统。
  • 在单个 32-位系统上对内存的利用:
    为了让 DPF 通过支持在一个 32-位系统上使用多个分区而有所帮助,必须满足一些需求:
    • 必须有一个带有至少 2GB 内存的系统,但是通常需要 4GB 或更多内存。内存越多,DPF 就越可能有帮助。
    • 性能必须受到 I/O 或大型排序的很大约束。通过转而使用多个分区,让各分区有其自己的缓冲池,就可以利用更多的内存。然而,如果在系统运行时 CPU 的利用率几乎是 100%,并且很少发生 I/O 等待,那么使用更多的缓冲池空间没有任何好处,因为 I/O 量的减少是微不足道的;而且,添加分区到一个 CPU 限制的(CPU-bound)系统更可能损害性能,而不是有助于性能。如果大型排序是要关心的问题,则使用多个分区有助于诸如 Create Index 之类的任务,这种任务需在每个分区中并行地排序。另一方面,有些查询并不需要在多个分区上并行排序,在这种情况下,使用单独一个(而不是多个)更大的排序堆(sort heap)展开到各分区上,就足够管用了。
    • 即使 I/O 量很大,如果大多数 I/O 都是对 LOB 或 LONG 数据的直接读写(没有在缓冲池中进行缓冲)的话,使用多个缓冲池也不会减少 I/O。
    • 如果日志写操作是一个瓶颈,并且分区的日志文件是写到不同的磁盘上,那么使用多个分区就有帮助。
    如果转而使用 64 位的数据库服务器,就可以利用所有的系统内存。然而,这种方法在互操作性方面存在一些问题(V7 客户机不能连接到 64 位的 V8 服务器,有些第三方的软件也可能不支持 V8),因此这种方法对于某些客户环境来说可能不可行。但是,仍然可以考虑用 64 位数据库服务器作为在单个系统或一些分离的系统上使用分区数据库的替代方案。
  • 规避限制:
    对每个表运行 "runstats on table <schema>.<table>" (或 "reorgchk update statistics on table all" )。 接着运行像
    "select tabname, tabschema, tbspaceid, card, npages from syscat.tables order by tbspaceid, tabschema, tabname" 这样的一个查询,看看每个表使用了多少行和页,并按表空间分组显示。(为了发现当前所需的最少页数,可以在更新统计信息之前对表进行 REORG。) 您可能需要检查这些数字随时间的变化,以研究增长率。应确保同时查看了关于行和页的数字,因为一个页中的行数常常少于 255,因此在远远没有到达行数极限之前就会先遇上页数的极限。
    如果添加分区以避免这种限制是您使用 DPF 的 惟一原因的话,那么就值得先停一下,考虑是否可以转而使用更大的页宽,或者将表水平地或垂直地拆分到多个物理表中,通过 UNION 或视图将这些表“组合”起来。使用这些方法通常比使用 DPF 的代价要来得小。

对于分区表,CAPTURE (用于复制)是不允许的。更具体地说,只有当源表是未分区的,并且它驻留在编目分区上的时候,才可以捕捉更改。因此,如果应用程序对于作为复制源的大型表有严重的依赖,那么使用分区数据库就不合适。(随便提一下,如果使用的是 V8.1 Fixpak 2 之前的版本,那么还有一个限制,即在分区数据库中不能使用 IDENTITY 列和 SEQUENCE 对象。)

我们将在以后谈到如何设计一个使用分区的应用程序和数据库,但是下面这几条是这种设计工作背后的关键事实:

  • 每个表只能有一个分区键。
  • 对于大型任务的主要设计目标是让工作在所有分区上并行地完成。
  • 对于小型任务的主要设计目标是在尽可能少的分区上执行任务,理想情况下是一个分区。
  • 对于所有任务的共同目标是尽可能减少分区之间的通信。

这些事实很大程度上决定了最适合 DPF 和最不适合 DPF 的数据库类型:

  • 对于分区的一个理想场景是一条像 "select count(*) from BIG_TABLE" 这样的语句。如果将这个表放在所有分区上,则每个分区都可以计算该表在其上的行数,并将这个局部总数(subtotal)发送到协调分区,以便计算总和,而这里的通信成本比起每个分区上所做的工作来可以忽略不计。
  • 另一个非常合适的场景是,一个大表与几个非常小的很少更新的表相连接。大表是分区的,小表则被复制到每个分区上,这样就可以并置连接。
  • 一个合适的 OLTP 场景是,在一些关键事务中涉及多个表,但是这些表都有一个共同的用作分区键的列 C1,并且处理过程是基于 C1 的一个值的。这时,所有的处理工作都可以发生在一个分区内。一个例子就是有一组表 ORDER、STORE 和 SHIPMENT,每个表都以 STORE_ID 作为分区键。当有客户购买时,就会对 ORDER 表执行一条插入,更新 STORE 表中的行(列 TOTAL_SALES),并对 SHIPMENT 执行一条插入,所有这些处理都是在相同的分区内进行。
  • 不适合使用分区的是那些在连接时涉及很多大表和各种各样的表和列的 ad hoc 查询环境。在那些情况下,很难或者不可能选择表的分区键,使得所有大的查询执行起来没有很多的分区间通信。
  • 同样不适合使用分区的是那些有多条不能在单个分区内处理的非常小的语句。在这种情况下,分区间通信的开销比起这些语句的本地执行来就相当高,而如果使用分区的话(尤其是跨多个物理系统),响应时间就会大大恶化。不过,使用分区可以取得额外的吞吐量,这一点可能抵得上一定程度的响应时间的牺牲。
  • 大多数工作负载和一些特定的任务都处于刚才讨论的这两种极端之间,这些地方都需要通过原型来研究使用分区所带来的影响。

继续谈到前面的话题,要清楚,当从一个未分区数据库迁移到分区数据库时,不同任务的可伸缩性会有很大的差异。在正面的一端,随着将一个分区变为 N 个分区,像 "select count(*) from BIG_PARTITIONED_TABLE" 之类的查询以及 CREATE INDEX 和 REORG 之类的任务运行起来要快将近 N 倍(假设每个分区有相同的资源,并且数据量保持不变)。在负面的一端,(a) 分区会带来额外的工作,或者 (b) 必须连续地做一些工作。 同时包含这两种情况的例子是 Load,这在后面的 填充表一节中会更详细地讲到。如果要 load 到两个分区,则必须将数据分区(散列),并发送到适当的分区上,然后进行装载。前两步代表在该表是未分区表的情况下不会发生的额外工作,这种工作必须在进行实际装载之前完成。进行一些调优是可行的,然而使用两个分区比使用一个分区的 load 更慢的情况并不鲜见。另一个例子是分区数据库的 BACKUP 和 RESTORE。这些任务可以在每个分区上并行运行,不过编目分区必须总是在其他分区之前备份或恢复。谈论每种 DB2 任务超出了本文的范围,不过您应该考虑哪些任务对于应用程序是最关键的,并估计或者原型化(prototype)和测量使用分区对这些任务的影响。

您需要考虑使用 DPF 的成本。最明显的成本是(每个处理器)获得 DPF 许可的费用,这是 ESE 之外的费用;因为价格是变化的,我们没有特定的价格,但是可以假设 DPF 给每个处理器在 ESE 的费用之外又增加了大约 30% 的费用。其他的成本包括硬件和软件的配置,分区键和节点组的设计以及建立原型,各种形式的测试,以及由于分区环境复杂性的增加而导致的更高的操作成本。

在我们更具体地描述应用程序的迁移之前,您可能需要略读一下 附录 A:DB2 中一些适用于 DPF 的特性。 这个附录有一个关于 DB2 中与 DPF 相关的一些方面的完整列表,这将引起对是否适合使用 DPF 的更多思考。





回页首


进行迁移

假设您已断定更改应用程序及其相关的数据库以使用 DPF 是一件好事,或者至少您想作进一步的调查。那么,现在要遵循什么步骤来进行迁移呢?

这些步骤描述起来应该是针对更改整个应用程序和数据库的情况,但是首先选择一个重要的子集,然后使用下面的步骤对该子集进行原型化,这种想法总是好的。而且,大多数步骤并不是真正彼此独立的:在大多数情况下,在进行使用分区情况下的设计时,往往有一个求精的迭代过程。因此在理解分区与使用分区的应用程序的行为时,应保持简单性。

当完成初始的测试并进入到类生产(production-like)配置时,就需要带着不同的环境重新过一遍这些步骤。这里大多数的内容同时适用于测试环境和生产环境,但是对于这两种环境又分别有特殊的考虑。

  1. 决定使用什么硬件以及使用多少个分区。请参阅 选择系统和分区数
  2. 创建分区环境,包括数据库。请参阅 创建分区环境
  3. 设计数据库分区组,并决定对哪些表分区。请参阅 规划数据库分区组和表分区
  4. 规划表、表空间和数据库分区组之间的关联。请参阅 将表指派给表空间和数据库分区组
  5. 为分区表选择分区键。请参阅 选择分区键
  6. 创建数据库分区组、表空间和其他数据库对象。这一步包括更改 DDL 以反映前面步骤中作出的设计决定,然后执行该 DDL。尽量让未分区环境和分区环境下 DDL 之间的差异最小。由于可以在未分区环境中创建带有分区键的表,在大多数情况下可以将 DDL 的不同限制为数据库分区组和表空间定义。
  7. 更改应用程序以处理分区。理想情况下,应用程序设计者可以在以上步骤(更加面向 DBA 的步骤)的同时做这一步。请参阅 使应用程序适应分区环境
  8. 配置注册表、实例和数据库配置变量。请参阅 执行注册表、DBM 和 DB 配置
  9. 填充表。请参阅 填充表
  10. 执行功能测试。显然,功能测试是任何迁移项目的关键组成部分。在将应用程序迁移到分区环境的情况下,测试应该包括对未分区应用程序的标准测试的主要子集。由于 DPF 对应用程序来说是透明的,这种测试所能发现的问题应该很少。主要应该测试的是那些专门为支持分区而发生的变化,这里应同时使用单用户测试和多用户测试。完成了基本测试之后,就应该尝试产生错误情况,看看应用程序如何处理。显然,在分区数据库中有更多容易出错的地方,应确保在进入生产阶段之前知道如何处理这些错误。例如,尝试 "db2stop force dbpartitionnum 2",看看在分区 2 被关闭时会出现什么情况。请参阅 性能测试
在对应用程序进行了完整的测试之后,就应该将重心放到运行的问题上来。请继续阅读 运行及其他各方面的考虑

选择系统和分区数

如果你用作 DB2 非分区应用程序测试环境的系统的能力没有得到充分利用,那么可以出于简单起见而在那个系统上开始 DPF 工作。建议让系统至少有 4 个处理器和 1GB 内存。对于早先的原型化工作,一开始应该使用两个或 4 个分区。如果使用两个分区,那么设置成本就最小,并且仍然可以练习所有的分区。如果使用 4 个分区,则需要多一点的设置,但同时也将使您更好地了解可伸缩性问题,并且更接近最终的配置。为便于早期识别问题,更可取的起始配置应该是使用两台物理机器,每台机器上有两个分区。这样就迫使某些分区之间的通信要通过网络,从而使性能问题显露出来。如果在当前机器上没有容量,我们建议您减少硬件花费,而使用一个或两个小规模的系统,直到您知道分区应用程序是如何执行的。

另一个考虑是,应该使用两种类型的特殊专用分区(dedicated partition)中的任意一种,还是两种同时使用。这些考虑只有对于生产环境才需要,并且只有在分区数大于 10 的时候才应该考虑。第一种类型是专用编目分区,这种分区包含编目表,不包含用户数据。当 BACKUP 和 RESTORE 所需的时间是关键的时候,这种类型的专用分区相当常见。如前面所示,这两种操作都必须在开始处理其他分区之前先处理编目分区。由于编目分区上没有用户数据,因此它的备份和恢复就可以很快完成,并且可以最小程度地延迟对其他分区的(并行)操作的开始。如果存在对编目表的大规模的查询,这种方法也比较合适。

第二种类型的专用分区是协调分区。这种分区可以不止一个,其思想是,如果要按照例程通过一个或多个协调分区为用户连接转移大量的数据,那么就会消耗那些分区上的大部分 CPU,并降慢了数据访问速度。如果让分区什么也不做,只是充当协调者(coordinator),这样或许有一定的作用,但是需要仔细研究。如果真的要使用这些分区,则这些分区所在的节点所需的物理内存比起那些有相同数量的包含用户数据的分区的节点来要少。

虽然曾说过您可能需要某些特殊的分区,但应该强调的是,通常情况下并非如此。对于几乎每一种情况,我们强烈建议,在分区配置中每个系统在硬件、软件和数据库布局方面应该尽可能地相同。如果遵循这一策略,就可以避免出现混淆,并大大简化设置,在后面我们会更深入地讲到这一点。

完成应用程序的迁移和测试之后,应该用生产级数据量进行性能测试和压力测试(stress testing)。以下是关于那种测试的理想配置的一些经验法则:

  • 对于每 100-300GB 的原始数据(不包括索引)使用一个分区。
  • 一个表在每个分区上不超过 100GB。
  • 每个分区一个或两个 CPU。决不能出现分区数多于 CPU 数的情况。
  • 对于每个 CPU 有 3-4GB 的内存。
  • 对于每个 CPU 有至少 5 个磁盘,理想情况下这些磁盘应该是良好的存储子系统,例如 FAStT 或 ESS。 总存储容量至少应该是原始数据量的三倍。
  • 对于每 2 个 CPU 有一个 2GB 的光纤通道适配器(fibre channel adapter)。
  • 对于每 4 个 CPU 有一个 1GB 的以太网适配器(Ethernet Adapter)。让分区间的通信尽可能地快通常是获得最佳性能的关键。

以下是在考虑在生产环境中使用多少个分区时进行权衡的指南。
如果分区数较少(每几个分区对应更多的 CPU,每个分区上有更多的数据),则通常:

  • 能处理更多的并发查询。
  • 单个查询的运行时间会更长(因为每个分区上有更多的数据)
  • OLTP 响应时间会更短(因为分区间的通信更少了)。
如果使用更多的分区,则通常:
  • 单个查询的性能更好。
  • 实用程序(BACKUP、RUNSTATS、REORG 等等)的性能更好。
  • OLTP 吞吐量会更大,但是响应时间会更长。

创建分区环境

有两种方法可以创建测试分区环境:迁移一个已有的环境,或者从头开始创建。最干净利落的做法是按照文档上的说明从头开始,但是首先还是让我们谈一谈将已有的单分区环境迁移到 DPF 的这种方法。

为了迁移一个已有的环境,需要考虑实例和数据库。第一步是备份或删除已有的数据库,具体是备份还是删除取决于您是否想要继续使用该数据库。接着安装 DPF 的许可密钥(以便允许使用分区)迁移,并运行 db2iupdt 以更新已有的实例,使其支持分区,从而完成已有实例的迁移。完成了已有实例的迁移之后,任何已有的数据库就都可以使用了。如果您想使用另一个不同系统中的单分区数据库(取决于平台兼容性的一般约束),那么可以恢复数据库。除非改变了表空间容器,否则不需要使用重定向恢复(redirected restore),但是单分区 DPF 实例必须以 0 作为其分区号。

安置好已有的数据库之后,就需要遵循一些步骤在附加系统上准备和安装 DB2 ESE 和 DPF,就像作为 "from scratch" 安装的一部分一样,以相同的方式创建分区(请参阅后面的内容)。最后,需要创建或改变数据库分区组,并使用命令 REDISTRIBUTE 将分区表再分配到需要的分区上。

如果是从头开始创建一个分区环境,则可以参阅 Quick Beginnings for DB2 Servers中的完整细节,其中还有一些附加信息,尤其是在 Installation and Configuration Supplement 中给出的关于使用响应(response)文件进行安装的信息。 当分区数较多时,为简化安装,建议使用响应文件这种方法。

关于建立分区环境的一些关键方面是:

  • db2nodes.cfg 文件是要设置的最关键的文件。在 Administration Guide: Implementation,话题 "Creating a node configuration file" 中给出了关于该文件的信息。 您很有希望有一个高速的分区间的通信接口。在这种情况下,db2nodes.cfg (在 UNIX 中)的第 4 列被用来为每个分区提供高速网络名称,以便在分区间的通信当中使用。(第一列给出用于与分区进行客户机-服务器通信的名称)。当第 4 列为空时,那个名称也用于分区间的通信。)
  • DB2 本身安装在每个节点上,但是 sqllib 目录(该目录包含 db2nodes.cfg 和其他文件)必须处在每个节点都能访问到的一个(共享的)位置上。在 UNIX 中,sqllib 在实例的 NFS 中的主目录下。
  • 服务(services)文件(例如 UNIX 下的 /etc/services)是另一个关键的文件。实际上,每个节点都将有其自己的服务文件版本,在每个版本中对应于分区实例的条目必须相同。
  • 当创建分区数据库时, 决不能使用 NFS 或网络驱动器作为路径来创建服务文件。类似地,所有表空间容器和日志文件必须处于相对于它们的分区的本地磁盘上。不过,为了符合通过使分区尽可能一致而达到简化的目的,在每个分区上使用相同的目录名和结构是有益的。
  • 如果您关心哪个分区会是编目分区,那么可以在运行 CREATE DATABASE 命令之前先登录到那个分区。

规划数据库分区组和表分区

这里有一些考虑:

  • 几乎总要为小的表创建至少一个单分区的数据库分区组。
  • 几乎总要为大的表使用至少一个由所有分区组成的数据库分区组。这个数据库分区组可以是缺省的 IBMDEFAULTGROUP。
  • 分区数越多,就越可能存在一些对单分区来说太大、而要展开到所有分区上又太小的表,那么就越需要创建包含数个分区、但不是全部分区的数据库分区组。例如,如果将一个 100MB 的表展开到 16 分区上没什么意义,但是对于单个分区来说这个表又太大了,因此就需要将这个表放在包含数个分区的一个数据库分区组中。可以有数个数据库分区组,每个数据库分区组包含不同的分区子集。这种方法存在一点风险,那就是很容易导致不同分区上的负载不均衡,从而违背了使用分区的目的,因为使用分区就是为了利用所有可用的硬件。后面会讲到另一点风险。
  • 要发生并置,表必须处于相同的数据库分区组内。那么,为什么不为所有分区表使用一个数据库分区组呢?这里主要有几点原因:
    • 小的表根本就不应该分区,而中等大小的表又不应该展开到太多的分区上:这两种错误都会得不偿失。经验法则是,不要将行数少于100,000、大小小于 20MB 的表分区。
    • REDISTRIBUTE 命令对整个数据库分区组进行操作。如果把所有分区表放在一个数据库分区组中,则运行 REDISTRIBUTE 命令时将在一次执行中处理所有的表,这样您可能会不耐烦。这种设置也限制了在对某些表进行分区,而对其他表不分区时的灵活性。因此,如果知道有几组表永远不会连接到一起,那么最好将每一组表放到一个单独的数据库分区组中(即使数据库分区组包含相同的分区也没关系:可以存在任意个有相同一组分区的数据库分区组,并且任何特定的分区都可以处于任意个数据库分区组内)。
    • 如果选择使用专用编目分区或协调分区(在 选择系统和分区数中曾讨论过),那么就需要相应地定义数据库分区组,方法是忽略掉分区列表中那些用于用户数据和系统临时数据的数据库分区组。
  • 另一件要考虑的事情是复制哪些(未分区的)表。首先入选的是那些更新不多而又经常与大表进行连接的小表或中等大小的表,但是在对分区数据库运行查询之前,常常难于知道应该复制哪些表。下面是对复制表的一个示例定义:
    "create table t1_rep as (select * from t1) data initially deferred refresh deferred in all_partition_tbspc replicated"
    要记得创建适当的索引,并对复制的表执行 RUNSTATS。注意,如果基表被复制到一个包括基表的分区的表空间中,则那个分区将有该表的两份拷贝(原身和复制品)。

    也可以复制物化查询表(Materialized query tables,MQT)。MQT 是预先计算(pre-computed)的查询结果,通常从大的表聚合而来,一般都相当小,因此,如果 MQT 可以用于连接的话,这种表很适合复制。上述考虑同样适用于要复制的小型基本。

将表指派给表空间和数据库分区组

由于表要指派给表空间,表空间又要指派给数据库分区组,所以应该在记住上述考虑的同时规划分配。例如,应该将那些想要放在不同数据库分区组的表放在不同的表空间中,这样,在决定使用多少表空间的时候,应该使用偏多的表空间,以允许在将这些表空间指派给数据库分区组时有最大的灵活性(可以将任意数量的表空间指派给一个数据库分区组)。另一点要记住的是 DMS 表空间的大小限制:您需要避免将表组到相同的表空间中,以免撞上这一限制。

如果您在设计一个未分区应用程序,但是又想让将来要支持分区时更加容易,那么试着像使用了 DPF 那样来规划表/表空间/数据库分区组的分配。虽然没有 DPF 就不能定义多分区数据库分区组,但是仍可以创建最终需要的所有数据库分区组,当使用了 DPF 的时候,再把分区加进到这些数据库分区组中。

当为多分区数据库分区组中的表空间选择容器名称时,应使用简单的名称,并且除了分区号以外这些名称在每个分区上应该一致。例如, "/db2data/large_tblspace_Pn",其中下标 "n" 是指分区号。这样一来,就可以在 CREATE TABLESPACE 中使用 " $N" 语法,以大大简化语句,就像下面的语句中一样,该语句将在其执行时所在的每个分区上创建一个如上命名的容器:
"create tablespace ts1 managed by database using (file '/db2data/large_tblspace_P $N' 10000)" .
("$N" 之前的空格是必需的,并且在置换过程中会被移去。)

选择分区键

如果给定特定的 DPF 工作负载和配置,则最大的性能因素是对表的分区键的选择。然而,可能不存在绝对或明显最佳的一组分区键,可能某一组分区键对某些查询是最佳的,而另一组分区键对于其他查询也是最佳的,因此必须考察整体工作负载,并知道进行优化时什么才是最重要的。可能还需要尝试多种不同的方案,才能做出最佳选择。

虽然选择分区键的任务目前看来还很沉重,而且要提供大量的时间,但还是有些好消息要告诉大家。DB2 即将发行的 "Stinger" 版将包括新的 DB2 Partition Advisor。在 Stinger 的预览版中(请参阅 参考资料),Advisor 被描述为“一种对一个或多个服务器上的数据库自动进行分区或优化性能的新工具。在过去,为了取得更高的性能,管理员可能要花几个星期的时间来微调群集的和分区的数据库,而通过使用 Partition Advisor,就可以在几分钟内得到所需的性能。”

弄清楚一点很重要,那就是 DPF 分区是基于散列的,目的在于将行均匀地展开到一些分区和硬件资源上,以使性能最佳。以实现范围分区为目的来选择分区键是不明智的。让我们借用前面的一个例子来加以解释。假设我们有 4 个分区,表 T1 的分区键是列 C1,每个独特的 C1 值可能出现在很多行,令人感兴趣的查询是 select count(*) from T1 where C1 = ? 。 因为 C1 是分区键,有相同 C1 值的所有行都将在同一个分区,您可能会认为这样很好,因为有相同 C1 值的行“在一起”。然而,我们还须考虑到 SELECT 的性能。扫描行和对行计数的所有工作是使用一个分区的资源来完成的,而其他分区的资源这时就闲着(对于这个查询来说)。如果我们使用一个不同的分区键,比如说 C2,且 C2 的值与 C1 的值没有关联,则该查询执行起来几乎要快 4 倍。这将导致对于一个给定的 C1 值,每个分区保存大约 1/4 的行数。注意,如果对于每个 C1 值只有少量的行,那么 C1 倒很适合作为分区键。

下面是在选择分区键时的几点考虑。当可以使用 Advisor 时,对这些考虑的理解或许不是那么重要,但仍然十分有用。

  • 将一个表创建为分区表之后,就不能直接更改它的分区键。要更改其分区键,可以使用以下方法中的一种:
    1. 将表转储到一个文件,删除该表后再用新的分区键重新创建表,然后重新装载该表。
    2. 用临时名称和新的分区键创建该表的一个新版本,然后从旧表装载到新表(最快的方法是: "declare mycursor for select * from t1" ,之后接着 "load from mycursor of cursor replace into t1_new" ),接着删除旧表,将新表重命名为真正的名称,并重建索引,如果是第一次创建该表,则还要重复其他步骤。
  • 通过 ALTER TABLE 可以添加或删除分区键,但是这只对未分区表有效。
  • 那些处于表上定义的惟一性约束或主键约束中的列必须是分区键的一个超集(superset)。要了解更多关于此的信息,请参阅 SQL 语句下面的 ALTER/CREATE TABLE。
  • 数据类型:LOB 和 LONG 型的列不能作为分区键的一部分。就效率而言,整数类型的列是最可取的,其次是字符型,然后是小数。
  • 选择基数较大的分区键列,以避免表中的行在各分区上分布不均衡。如果无法达到这一点,则可以尝试使用取值分布比较均匀的列作为分区键列。填充好一个表之后,就可以使用像 DBPARTITIONNUM 实例HASHEDVALUE 实例中的那些查询来检查这个表在每个分区上有多少行,映射到分区图中每个条目的有多少行(出于再分配的目的)。除非有极大的表,否则每个分区上的基数之间存在的一点点百分比的差异可以忽略。
  • 选择具有一列或几列且这些列经常用于连接的分区键。
  • 分区键应该不包括经常更新的列。每当更新一个分区键时,DB2 都需要删除该行并重新将其插入到一个不同的分区,至于插入到哪个分区则通过散列新分区键值来决定。
  • 除非一个表不是很重要,或者不知道一个好的分区键选择是什么,否则不应该随缺省情况选择分区键。缺省的分区键是主键的第一列,如果没有这么一列,则选择有适合数据类型的第一列。
  • 确保理解并置和不同的连接类型;请参阅 Administration Guide: Performance、话题 "Join strategies in partitioned databases" 和 "Join methods in partitioned databases"。过些时候,在 性能测试一节中,我们将谈到对查询的访问计划和并置或 lack thereof 的研究。
  • 下面是对并置的一些要求。被并置的表必须:
    • 在相同的数据库分区组内,且这个数据库分区组不会被再分配(在再分配期间,数据库分区组中的表可能正使用不同的分区图,这时不能进行并置)。
    • 分区键必须有相同数量的列。
    • 分区键中相应的列必须是分区兼容的。
    • 如果不在相同的数据库分区组内,则必须是在包括相同分区的单分区数据库分区中。
  • 分区兼容性是在分区键中相应列的基本数据类型之间定义的。分区兼容的(partition-compatible)数据类型有一个特性,那就是对于两种不同类型的两个变量,假设变量有相同的值,则它们将通过相同的分区函数映射到相同的分区键索引。分区兼容性有以下特征:
    • 内部格式用于 DATE、TIME 和 TIMESTAMP。这些类型彼此不兼容,并且没有哪一个与 CHAR 或 VARCHAR 兼容。
    • 分区兼容性不受具有 NOT NULL 或 FOR BIT DATA 定义的列的影响。
    • 对于兼容数据类型的 NULL 值是一致处理的。而不兼容数据类型的 NULL 值可能产生不同的结果。
    • 可以使用 UDT 的基本数据类型来分析分区兼容性。
    • 分区键中具有相同值的小数是一致处理的,即使它们的标度(scale)和精度(precision)不一样也是如此。
    • 系统提供的散列函数将忽略字符串(CHAR、VARCHAR、GRAPHIC 或 VARGRAPHIC)的结尾空白。
    • 不同长度的 CHAR 或 VARCHAR 是兼容的数据类型。
    • 相等的 REAL 或 DOUBLE 值,即使它们的精度不同,也将被一致处理。

使应用程序适应分区环境

分区的使用对于 DB2 应用程序来说几乎完全是透明的。设置好表之后,已有的应用程序应该可以毫无改变地运行。

然而,我们还是要小心地区分主要由 SQL 语句组成的程序与发出 DB2 API 调用的应用程序,您可能会将这两种应用程序与数据库一起使用。您几乎肯定需要调整那些脚本/程序,使其适合分区环境。对于那些脚本最明显的要求是更改命令调用和一些 SQL 语句(它们显然只适用于未分区环境中的一个分区),以便适用于分区环境中的所有分区或特定的一些分区。在需要进行这种更改的大多数情况下,您需要使用相同的命令并通过 db2_all 调用它,使该命令操作于所有分区,这在 其他技巧 中有详细讨论。 在其他情况下,这些更改包括为命令或语句做副本并添加相关的子句,这个子句通常是 "ON DBPARTITIONNUM",以便可以在每个分区上不同地调用命令或语句的每个副本。还有一些情况下,您可能要改进脚本,以便引用 附录 A:DB2 中一些适用于 DPF 的特性中标题“编目视图”下面列出的特定于分区的一些项,例如某些编目视图列(例如分区键信息)。对于使用 API 调用的情况,可能需要指定一个表明分区号的参数,例如为 db2GetSnapshot API 指定 iNodeNumber 参数。

我们建议您回顾一下 附录 A:DB2 中一些适用于 DPF 的特性中的材料,根据这些材料分析脚本和 API 调用,作出适当的更改,然后进行测试。

回到应用程序的编程,有一些问题是肯定要处理的,尤其是在性能方面和故障诊断方面。在 Application Development Guide: Programming Client Applications (ADG: PCA) 的第 17 章("Programming Considerations for Partitioned Database Environments")中会完整地讨论一些最重要的问题。下面是对那些关键问题的概述:

  • FOR FETCH ONLY: 如果 SELECT 语句后面不会有对语句的更新或删除,那么在 SELECT 语句中使用该子句很重要。该子句将导致在将一些行发送到协调分区的时候先将这些行组成块,从而可以大大提高性能。
  • 定向分布式子段(Directed distributed subsection,DSS): 如前所述,在 OLTP 环境中应该努力使处理在尽可能少的分区上完成。有了 DSS,就可以将查询子段只发送到适当的分区,其思想是,您可以重写查询,以使 DSS 发生。一个例子就是将带有两个元素的 IN 列表的 SELECT 改为两个 SELECT 的一个 UNION,每个 SELECT 有一个 "=" 谓词。这里 UNION 中的每个 SELECT 将被定向到某一特定的分区,而不是像原先那个语句那样被发送到所有分区。如果减少响应时间是关键,则这种类型的更改是合适的,但是我们建议在将这种更改全面地应用到应用程序之前,应该进行性能测试。您需要对查询(及其重写后的版本)运行 Explain。
  • 本地旁路(local bypass): 这是分布式 DSS 的一种特殊情况,这里所有工作都是在协同分区中来做的。 请参阅 本地旁路
  • 缓冲插入(buffered insert):这种技术可以用来避免在插入行时产生每插入一行就进行通信的开销。 请参阅 如何优化插入
  • 抽取大量的数据:那些返回很大结果集的查询会因为所有行与协调分区的通信而慢下来。避免这一问题的一个方法是运行 db2_all 的使用中的那样一个命令,这种方法使用 DBPARTITIONNUM 函数只选择为当前分区的行。那些行可以写到单独的文件中,每个文件相对于它们的分区是本地的,或者写到一个组合文件中(例如在 NFS 中)。这些技术已经在 db2batch 命令中实现了,在批处理环境中执行大的查询时就可以使用这个命令,而不是使用命令行处理器(db2 command)。在 命令一节中提供了一些这方面的信息。
  • 创建模拟分区数据库环境(Simulated Partitioned Database Environment): 这里的建议是,在没有建立分区数据库环境的情况下创建带分区键的表,对这些表进行测试,以此来为分区作准备。这是一个有用的技巧,但是无助于识别在完整测试中将遇到的很小的一部分问题。
  • 故障诊断:在 ADG: PCA 中对这一话题有重要的、并且是广泛的讨论。其中一些关键之处是:
    • 如果一个分区碰到严重错误,则事务就进入 indoubt状态,需要手动解决。
    • 为解决经历严重错误后出现的不一致的问题,可能需要重新启动个别的分区。
    • 一个应用程序只能收到一个 SQLCA。根据手册中列出的规则,这个 SQLCA 中的值是从来自所有相关分区的 SQLCA 合并而来的。

这里有两个小小的可应用性的问题:

  • 下面是摘自前面提到的那本书第 18 章的内容:“当在分区数据库环境中遇到一个故障时,则出故障的那个数据库分区中所有声明过的临时表都不再可用。随后若对那些不可用的声明过的临时表进行访问,将返回一个错误(SQL1477N)。当应用程序碰到一个不可用的声明过的临时表时,可以删除该表,或者通过在 DECLARE GLOBAL TEMPORARY TABLE 语句中指定 WITH REPLACE 子句来重新创建该表。”
  • 下面是摘自 Application Development Guide: Programming Server Applications 中话题 "Scratchpads for UDFs and Methods" 的内容: 如果运行使用 scratchpad 的 UDF,缺省情况下每个分区(即每个代理)有一个单独的 scratchpad,并且不存在通过“全局 scratchpad” 对 scratchpad 数据的共享。如果应用程序指望有一个单独的 scratchpad,则可以使用 DISALLOW PARALLEL。这将导致 UDF 运行在一个单独的分区上,但是在性能上可能要打折扣。

分区应用程序的另一个目标是,如果可以在每个分区内对行进行处理,则应避免将行取到协调分区然后再对其进行处理。例如, 一个 SQL 存储过程可能会选择一行,并且在找到该行时对其进行更新,否则就插入该行。对于一个分区数据库,这样做就非常慢。而在此情况下较快的方法是使用 MERGE 语句,该语句通常可以并行地在每个分区上进行处理。

执行注册表、DBM 和 DB 配置

对于整个分区实例,有一组注册表变量和 DBM 配置参数,但是对于每个数据库,每个分区也有其自己的一组 DB 配置参数。

大多数注册表变量和配置参数的使用都应该采用像未分区环境一样的标准。而其他一些注册表变量和配置参数或者是特定于分区环境的,或者是在分区环境中有不同的作用,这在 注册表和环境变量以及 配置参数 中会进行讨论。 如果一个给定的节点上有多个分区,那么就需要调整缓冲池、数据库管理器和数据库配置参数,以便适合于共享节点的内存。(每个分区实质上是作为实例的一个独立的副本来运行的,在实例的每个分区上的“副本”中可以有多个数据库。)

一致地配置每个分区是可取的。如果选择使用专用的编目分区或协调分区,则是例外。(在 选择系统和分区数中曾对此作了讨论)。 在那种情况下,通常需要通过使用较小的缓冲池来节省内存,因为不管哪种类型的专用分区都需要进行很多的数据页 I/O。

与分区环境相关的主要变量是针对性能的 DB2_VI_ENABLE、DB2_FORCE_FCM_BP 和 FCM_NUM_BUFFERS,以及针对功能的 DB2_PARTITIONEDLOAD_DEFAULT。后者将在 填充表中进行讨论。

填充表

用数据填充表是大多数数据库的一个关键任务。不管是在未分区环境下还是在分区环境下,LOAD 命令(或者 db2Load API)都是目前最快的方法,但是这种方法也有一些局限性,这些局限性在使用 INSERT 语句或 IMPORT 实用程序时是不会碰到的。在对分区数据库使用上述任何一种方法时,都有其特殊的考虑。从终端用户的角度来看,INSERT 和 IMPORT 实际上忽略了正在使用 DPF 的事实,但是对于它们又有重要的性能方面的考虑: 请参阅 如何优化插入。 现在,让我们继续谈论 LOAD。

Data Movement Utilities Guide and Reference 的 "Chapter 4. Loading Data in a Partitioned Database Environment" 中曾对分区环境中的 LOAD 进行了广泛的讨论。 其中关键的几点是:

  • 像 INSERT 和 IMPORT 一样,必须应用散列算法将被装载的行定向到适当的分区。为了使 LOAD 的速度和灵活性最大化,行的散列(分区)是在一个独立于行的实际装载的阶段完成的。
  • LOAD 命令有 PARTITIONED DB CONFIG 参数,用于指定分区选项。其中最重要的一个选项是 MODE,这一参数告诉 LOAD 您想要处理哪些阶段。最常见的、也是缺省的选项是 PARTITION_AND_LOAD,该选项指的是同时散列和装载行。在其他选项中,PARTITION_ONLY 和 LOAD_ONLY 可用于将散列和装载分成两个阶段,从而在两这个阶段分别用每个分区上的行创建和使用一个独立的文件。 (另外还有其他的 MODE 选项,如果 LOAD 对于您来说比较重要,我们建议您阅读关于这方面的资料。)
  • 其他选项,即 PARTITIONING_DBPARTNUMS 和 OUTPUT_DBPARTNUMS,分别让您选择在分区阶段使用哪些分区以及在装载阶段装载到哪些分区。这样就可以将 CPU 活动分散到各节点上,以及通过让不同的 LOAD 命½½令并发地装载到不同的分区来增加并发性,从而可以优化性能。要记住的一点是,在不同于装载分区的其他分区上进行分区时,需要在两个分区之间发送行,这样会损失通过最大化 CPU 利用率而赚到的性能。我们建议用不同的方法进行实验,以确定适合您环境的最佳方法。
  • 分区阶段的输出将显示有多少行被指派到每个分区,同时表明诸多分区之中是否存在数据倾斜(data skew)。
  • DB2 V8 与 V7 在分区环境中装载的方式上有所不同。概括地说,在 V7 中每个分区文件包含一个头部,该头部标识了此文件属于哪一个分区。这个文件之后必须装载到适当的分区,否则会发生错误。当装载到分区数据库中的一个未分区表时,需要使用 MODIFIED BY NOHEADER 来防止由于缺少头部而导致的错误。在 V8 中,不需要分区文件头部,缺省情况下,会在所有分区上尝试 LOAD,即使没有指定 PARTITIONED DB CONFIG 参数也是如此。DB2_PARTITIONEDLOAD_DEFAULT 注册表变量允许将 V8 中 LOAD 的缺省行为改为 V7 模式,这样就可以不作修改地对 V8 运行 V7 脚本。
  • 清单 7. 示例 LOAD 命令中给出了 LOAD 命令的一个示例。

如何优化插入

至此您应该知道,在 DPF 环境中,每一个要插入的行都必须将其分区键映射到一个分区号。然后必须将该行从协调分区发送到其他分区,这一传送过程大大增加了每条插入的时间。主要有两种方法可以减少这种开销:

  1. 缓存插入: 在这种技术中,行在被发送到目标分区之前会先缓冲起来,这样实际上就减少了通信开销。为了激活缓冲插入,可以用 INSERT BUF 选项 PREP 或 BIND 应用程序;对于 IMPORT,可以 PREP 或 BIND import 包 db2uimpm.bnd(在 sqllib/bnd 中)。如果在没有干扰活动的情况下插入大量的行,则应该使用缓冲插入。只所以需要在没有干扰活动下进行的原因是,DB2 服务器一次只能报告对于整个块的错误,因此在异常情况下有很多特殊的考虑。在 Application Development Guide: Programming Client Applications 的第 17 章 "Programming Considerations for Partitioned Database Environments" 中,在对缓冲插入的广泛讨论当中对这些考虑作了描述。 要了解关于使用了 IMPORT 的缓冲插入的信息,请参阅 Data Movement Utilities Guide and Reference
  2. 对于插入还有另一种可能性,这在 本地旁路中有描述。

本地旁路

本地旁路这个术语用于描述能够在协调分区内满足一个完整的请求的 DB2,这对于 OLTP 和一些“批处理”工作负载的最佳性能来说都是极为理想的。如前所述,在 Application Development Guide: Programming Client Applications的第 17 章中对本地旁路作了描述。

要发生本地旁路,需满足三点要求:

  1. 必须标识出适当的协调分区,以便在语句中使用。这里主要的步骤是使用 sqlugrpn API 将一个分区键值转换成一个分区号。在此之前,可以使用 sqlugtpi API 获得一个表的分区键信息,作为 sqlugrpn 的输入。另一种做类似工作的方法是使用 "partition_only" 模式下的 LOAD 命令创建一些文件, 其中这些文件是根据散列算法按照分区拆分开的。
  2. 应用程序必须连接到做这一工作时所在的分区上,这个分区由来自 sqlugrpn 的分区号指出。这可以通过显式地建立一个新连接来完成,或者更可取的是让客户机预先建立和维护一组连接,每个分区一个(或多个)连接,并在每次新的事务中选取合适的一个连接。这里有一些不同的机制可用于连接到一个特定的分区:
    • SET CLIENT CONNECT_DBPARTITIONNUM 命令; 例如,请参阅 清单 3. 连接或附加到特定的分区
    • 在 CLI/ODBC 中,SQL_ATTR_CONNECT_NODE 连接属性或 CONNECTNODE 配置关键字(设置成分区号,或者对应于编目分区的 SQL_CONN_CATALOG_NODE)
    • 对于 Java,参见 参考资料中引用到的 Connection Router。
  3. SQL 语句必须与在单个分区中所做的所有工作兼容。这要求这些语句本身是适合的(即决不能通过本地旁路执行 "select * from partitioned_table"),同时所有语句中涉及的行的分区键值要映射到相同的分区;即,必须有并置。例如,如果查询 "select * from t1, t2 where t1.c1=? and t1.c1=t2.c2" 被并置的话,则只能对相同的分区执行 "select * from t1 where c1=?" 和 "select * from t2 where c2=?"。
    另一个例子是 "insert into t1 select * from t2":如果 t1 和 t2 有相同的分区键,那么该语句将本地执行,这样肯定会比 t1 和 t2 有不同分区键的情况要快得多。
    注意可能在不同分区上发生的隐式处理。例如,如果在分区 1 上执行一条 INSERT 语句,这将导致一个触发器被触发,或者在一个或多个其他分区上进行外键约束检查。在这两种情况下,都应该尽量小心设计分区键,使相关的表并置在一起,但是对于给定的多种可能的表之间的交互,要做到在不简化应用程序的情况下使本地旁路 100% 地发生是比较困难的。即使不能在协调分区内处理 100% 的活动,也应该试着让尽量多的工作在协调分区内完成。

性能测试

在功能测试之后,或者理想情况下与之并行,就需要评测分区应用程序和数据库的性能。分区环境中的很多性能测试都应该想未分区环境下那样实施:运行应用程序工作负载,收集终端用户感知的吞吐量和响应时间性能指示器,以及 DB2 和操作系统监控数据,然后,如果结果不恰当,则作出适当的响应。

对较差性能作出响应的大多数方法与未分区数据库的情况是一样的,例如加大缓冲池、采用足够的排序堆以及确保最佳的访问计划。主要的差别在于迁移之后要关注的分区问题。那些不同之处归结起来无非是 (a)分区之间的通信:发生的通信有多少,是不是因为访问计划不好,如何修改访问计划,以及 (b)当添加数据和/或分区时应用程序是如何伸缩的。很快我们就会对这几点作进一步的考察,但是首先还是让我们考察一下有哪些可用的工具,以及应该如何使用这些工具:

  • DB2 快照:快照可以在捕捉不同级别的信息:DBM、数据库、表空间、表、缓冲池,以及应用程序。计数器主要用于调优缓冲池大小和配置参数之类的东西。它们还可以揭示是否存在并发问题。快照还可以在 Dynamic SQL Snapshot 中显示语句级的性能数据,通过扫描这种快照可以发现哪些语句被执行得最多,或者花去的总时间最长。那些语句显然是要重点关注并深入研究的语句缺省情况下,只需在附加到的分区上拍快照,因此强烈建议您使用 GLOBAL 选项,以得到整个实例或数据库的聚集视图。
  • DB2 事件监控: 这可以提供详细的死锁信息,但是很可能无论如何都不会碰到死锁,所以事件监控的主要价值通常在于捕捉每条 SQL 语句的耗时以及某些性能方面的统计信息。在分区环境中,语句级监控只能是每个监视器对应于一个分区地进行,由于这种复杂性,您应该至少监控协调分区,理想情况下还应监控其他分区,以获得完整的图像。
  • DB2 Explain:通过快照和事件监控,应该可以找到执行得最差的语句,然后用 Explain 来研究这些语句。记住并置的作用,以及其他两种类型的连接:定向和广播。请参阅 Administration Guide: Performance,话题 "Join methods in partitioned databases" ,以查看不同连接方法的实例。
  • db2batch:这是一个很好的工具,可用于运行单独的查询,以及步骤耗时和快照信息。建议使用
    "db2batch -d sample -f my_stmts.sql -i complete -o p 5"
    对于不同的输出位置(这里指的是分区),还应该考虑 "-p" 选项。而且,查询的性能取决于查询是从哪个分区上运行的,因此可以尝试以不同的分区作为协调分区(使用 SET CLIENT 命令)来通过 db2batch 运行查询。
  • 操作系统工具:显然,这些工具是与平台有关的,但是您将需要使用像 AIX 的 vmstat、iostat 和 netstat 之类的工具来提供关于 CPU、内存、磁盘利用率的信息。对于分区环境,使用一个带有多个窗口的、能同时显示所有或很多分区的监控器是非常有用的。这样就可以看到每个分区在何时被激活,并很快地说出正在处理的并行活动有多少。
  • RUNSTATS: 在进行任何大的性能测试之前,不要忘了对所有表运行 RUNSTATS。

分区间通信: 通过使用上述工具,可以找到和分析最糟糕的 SQL 语句。在研究由于那些语句而导致的通信时,主要信息来源是 DBM 快照的 FCM 使用部分。理想情况下,可以使用 db2batch 来运行各条查询,并捕捉快照信息。这样做可以避免被来自其他工作的活动所迷惑。您在快照中所要寻找的是在分区之间发送的大量缓冲区,这主要是对定向连接或广播连接的一个反映。您可以采取一些行动,例如创建复制的表,或者使用不同的分区键,然后再次尝试。
除了研究各条查询之外,将多用户活动在较长一段时期内对 FCM 的使用累加起来,并查看哪些分区做了最多的工作,这也是非常有趣的事。 要查看关于 DBM 快照(没有 GLOBAL 选项)的一个例子,请参阅 清单 6. DBM 快照:FCM 的使用和分区状态.

可伸缩性测试: 这是一个非常重要的任务,因为提高性能或可伸缩性通常正是您使用分区的原因。在研究可伸缩性时,建议一开始分别在一个、两个和 4 个分区上实现应用程序,看看各种情况下性能有什么不同。这里要决定的是,在添加分区时是保持数据总量不变,还是让每个分区有相同数量的数据。这两种情况都是值得研究的,具体采用哪一种决定取决于哪一种更能匹配应用程序的使用。另一个要做出的决定是使用什么硬件,以及分区在哪儿。显然,如果在一个分区上运行,并且应用程序运行时 CPU 的利用率达到将近 100%,则在相同节点上添加 3 个或更多的分区将更可能地导致重大的 CPU 瓶颈,并且不能得到关于可伸缩性的真实描述。因此,理想情况是,采用 4 个相同的系统,让系统之间的通信尽可能最快。这意味着要进行适当的可伸缩性研究,就可能需要相当大的投资。

在经历可伸缩性练习时,应记住不能期望所有任务都有好的可伸缩性。例如,如前所述,LOAD 在两个分区的情况下实际上比一个分区的情况下运行得更慢,因为必须做一些额外的工作。





回页首


运行及其他各方面的考虑

备份和恢复

当完成了测试的时候,在将应用程序投入生产之前,应该确保有一个稳固的备份和恢复策略。在 Data Recovery and High Availability Guide and Reference中,尤其是是主题 "Recovering from Transaction Failures in a Partitioned Database Environment" 和 "Recovering from the Failure of a Database Partition Server" 中提供了关于这方面的指南。

BACKUP 和 RESTORE 显然是非常重要的命令,与大多数命令一样,这两个命令也是一次只对单个分区进行操作。如前所述,这两个命令在开始在其他分区上运行之前,必须首先处理完编目分区。尽可能快地运行这两个命令的最好方法是使用如下所示的两个命令:

	db2_all '<<+0<   db2 BACKUP DATABASE wsdb TO /dev3/backup'
	db2_all '||<<-0< db2 BACKUP DATABASE wsdb TO /dev3/backup'   

按照 "<<+0<" 的要求,第一个命令只对分区 0 运行。
按照 "<<-0<" 的要求,第二个命令对除分区 0 以外的所有分区运行,并且由于 "||" 的原因,该命令是在那些分区上并行地运行。

在分区数据库中,编目分区是特别关键的,您需要非常小心地经常进行备份,确保表空间和日志磁盘没有单点失败。

高可用性(High Availability,HA):HA 这个术语用于描述(或多或少)不会出人意料地陷入崩溃的系统。对 HA 的详细讨论超出了本文的范围,但还是应该考虑一些事情:

  • HA 在未分区和分区数据库环境中是类似的,但是对于后者来说,通常有多个物理系统可以使用,并且这些物理系统通常用于让 DB2 运行在 mutual takeover 模式下。 例如,如果有一个节点 A,该节点上有分区 0 和 1,有一个节点 B,该节点上有分区 2 和 3,那么您通常会设置 HA,使得在节点 B 崩溃时,可以使用节点 A 取代节点 B,反之亦然。每个节点有两倍的负载,因而性能较差,但是整个数据库仍然是可用的。
  • 如果有一个或多个分区崩溃(甚至没有 HA),则可以运行 DPF,但是崩溃的分区上的数据将不再可用。您永远不必担心 DB2 给出不正确的结果,因为如果不能访问在满足某个查询时需要访问的一个分区的话,DB2 将返回一个错误(例如 SQL1229)。即使只有编目分区可用,对于编目视图的一条 SELECT 语句仍将成功。
  • 请参阅 Data Recovery and High Availability Guide and Reference以了解更多关于 HA 的信息。

持续维护

当分区数据库进入生产后,大多数持续维护(ongoing maintenance)任务跟未分区数据库的情况是一样的。RUNSTATS、REORGCHK、REORG、BACKUP 和性能监控都应该执行,并且执行的频率应该与未分区数据库的情况一样。

这里需要不时地应用修复包(fixpak),在分区环境中的一个不同之处是,需要将修复包安装在每一个节点上,而不是只安装在一个节点上。

当表随着时间而增长时,有些表可能会变得越来越不均衡,这里指的是表在每个分区上的行数。为了检测这一状况,我们建议偶尔运行一下 DBPARTITIONNUM 实例中的一些查询。如果一些大的表在某些分区上的行数比在其他分区上的行数大 10% 左右,则需要再分配这些表的数据库分区组。

要了解关于再分配的信息,请参阅 Administration Guide: Performance中的 "Chapter 11. Redistributing Data Across Database Partitions"。在那里可以看到关于再分配过程中所发生的事情的细节。关键的几点是,当对每个表进行再分配时,该表将被上互斥锁,表中的行先删除后插入(需在分区之间进行行内容的通信),对删除和插入进行日志记录,当表完成时还要进行一次 COMMIT。换句话说,在进行再分配时,会有相当大的开销,因而消耗的时间会比较长。不管是在失去行的分区中,还是在得到行的分区中,都必须确保有足够的日志空间。刚才提到的那一章就提供了这方面的指南。

随着表的持续增长,负载会超出资源的承受能力,性能就会变得不如人意。一种解决方案是添加额外的系统到分区实例(采用像一开始创建实例那样的方式),在那些系统上创建新的分区,更改已有的数据库分区组以反映新的分区,然后进行再分配。关于这一过程的细节在 Administration Guide: Performance的 "Chapter 10. Scaling your configuration" 中。这样做会花去大量的时间,尤其是再分配,这在刚才已讨论过。再分配的开销会随着要再分配的行的数量而变化(这是显然的),而再分配的行数又部分地取决于添加的分区所占的百分比,因此如果其他地方相同的话,从 8 个分区变为 16 个分区(+50%)比起从 24 个分区变为 28 个分区(+17%)来需再分配的行数要多得多。我们建议用少量的数据尝试进行一些再分配,看看在目前环境中这些再分配过程要花多长时间。

另一种替代添加分区和进行再分配的方法是往已有的配置中添加额外的硬件,尤其是 CPU,而不是添加分区。这种方法避免了再分配,但是不能重复。另一种替代再分配的方法是使用 EXPORT 或 High Performance Unload 工具将表内容清空到文本文件中,删除表,更改数据库分区组,再重新创建表,然后重新装载。在数据仓库环境中,会例行公事地从头开始重新装载数据,因此不必首先卸载数据。

还有两种可能遇到的情况是,分区数多于所需的分区数,或者因为升级到更强大的硬件而需要合并分区。刚才提到的那一章("Scaling your configuration")也谈到了分区的消除。基本上,首先必须将数据从分区中清除,这可以通过采用与添加分区相同的方法(再分配或者卸载/重新装载)来完成。对于合并分区的另一点考虑是,如果在使用一个有很多 LPAR 的大型 RS/6000 系统,那么最好是将多个分区放入到一个 LPAR 中,而不是将分区展开到多个 LPAR 上。这样做的好处是,可以使用 DB2_FORCE_FCM_BP 注册表变量使得相同 LPAR 中的分区之间的通信可以通过共享内存来完成,而不是使用 LPAR 之间通信时需用到的 UNIX sockets,从而提高了性能。在合并分区情况下,另一点要考虑的是,您可以简单地 re-cable 磁盘,将数据库移到新的硬件上,而不必进行再分配或卸载/重新装载。

其他技巧和话题

  • 有了分区实例之后,通过运行 db2sampl 命令创建样本数据库就可以建立一个相当小的实验环境。所有用户表都将被分区,并且这些表的分区键是缺省的。
  • 决定一个数据库的编目分区的快捷方法是在服务器上运行 LIST DB DIRECTORY ;请参阅后面的 实例
  • 为了查看哪些分区是活动的,可以拍一个 DBM 快照。要查看这样的一个例子,请参阅 清单 6. DBM 快照:FCM 的使用和分区状态
  • 在 DPF 环境中要使用的两个极其重要的命令是 db2_all 和 rah。db2_all 允许对一个实例中的所有 分区执行一个命令,而 rah 则是对实例中的所有 系统做相同的事情。(如果每个分区都在其自己的系统上,则这两个命令的效果是一样的)。这两个命令还允许在分区/系统的子集上运行命令,以及在多个分区/系统上并发地运行命令。这些命令不仅对于运行 DB2 命令十分方便,而且也便于运行操作系统命令来完成一些诸如创建目录(mkdir 命令)和查看信息(ls 或 dir,例如查看日志文件)之类的任务。要了解更多关于这些命令的信息,可以搜索 Information Center 或参阅 Adminstration Guide: Implementation 的 Appendix C。
  • 确保理解了 DB2 命令和 SQL 语句的作用范围。通常,对命令的调用只适用于一个分区,可以通过查看 Command Reference对命令的讨论中标题 "Scope:" 下面的内容来检查这一点。另一方面,SQL 语句通常会影响所有分区,不过有一些例外,例如 CREATE BUFFERPOOL 和 CREATE TABLESPACE,这两种语句可以选择对一个子集的分区运行。
  • 下面是两个非常重要的例子,说明了为什么需要知道命令的作用范围:
    • UPDATE DB CFG 命令一次只适用于一个分区,如果不理解(或记住)这一点就会给很多 DBA 带来不幸的事。例如,为了设置每个分区上的 SORTHEAP 值,就必须使用 db2_all,如下所示:
      " db2_all db2 update db cfg for sample using SORTHEAP 10000"
    • db2empfa 命令将为 SMS 表空间激活多页文件分配,因而可用于提高性能,但是这个命令只能应用于它在执行时所针对的那个分区,因此应该像下面这样来运行:
      " db2_all db2empfa sample" .
  • LIST APPLICATIONS 命令缺省情况下会给出单分区输出,这样会令人费解。对此可以使用 "global" 选项。
  • 分区号出现在不同的位置。下面是一些例子:
    • 在 AIX 上 "ps -ef" 的输出:如果分区 0 和 1 在节点上,则输出将为系统控制进程显示针对这两个分区的 "db2sysc 0" 和 "db2sysc 1",并显示含有像 "db2dlock (SAMPLE) 0" 之类内容的一些行,在这种情况下,这些行标识了分区 0 上 SAMPLE 数据库的死锁检测进程。
    • db2start 和 db2stop 对于每个分区有一行输出,其中在 timestamp 后面的那一列表明了分区号。
    • 在 db2diag.log 和 notify 文件条目中,分区号是像 "Node:002" 这样报告的。
  • 让 DB2 写大量的诊断输出(例如写到 db2diag.log)并不可取,但是如果有这个必要的话,则应该考虑设置 DIAGPATH 参数(DBM),使其指向对于每个分区是本地的一个位置。这将避免写消息时的网络传输开销,但是却必须查看多个文件才能获得完整的映像。(另一方面,当消息只属于每个文件的一个分区时,对消息的理解就更加容易,并且不同的分区常常会有相同的消息)。
  • 为使用一个特定的分区,可以使用 SET CLIENT 命令或设置 DB2NODE 环境变量。请分别参阅 命令注册表和环境变量
  • 对于 DPF,稳定性问题最常见的源头是分区之间的通信问题。应该尽力打造最健壮的通信配置。
  • RUNSTATS: 在分区数据库中,只有关于该命令运行时所在分区的统计信息才会被收集,再对这些统计信息加以推断(前提是行在各分区上是均匀分布的)以反映整个数据库。这意味着,SYSCAT.TABLES 中的 CARD 列通常不包含该表中确切的行数。让行均匀地分布在各分区上总是有益的。
  • DB2 Integrated Cluster Environment (DB2 ICE) for Linux “是一个完全集成的、高性能的、经过预先测试的解决方案,它结合了最佳性能(best-of-breed)软件、硬件和服务。该解决方案提供了一个高性能的、可靠的数据管理系统,这种数据管理系统可以从 2 个节点扩展到 1,000 个节点。”这种解决方案是建立在使用 DPF 的 DB2 分区的基础上的。要了解更多信息,请参阅 参考资料中的链接。





回页首


结束语

在本文中,一开始我们回顾了什么是分区,接着讨论了分区数据库的一些好处,已经如何决定是否将应用查询迁移到分区环境。然后我们谈到了在迁移过程中要经历的一些步骤。本文还给出了各种其他的信息,以帮助您理解分区独有的一些方面。我们希望本文有所帮助,并祝您在 DB2 分区之旅中好运。

致谢

我要感谢 Greg Holton 审校了这篇文章,并提出了一些极好的建议。





回页首


附录 A:DB2 中一些适用于 DPF 的特性

在此附录中,我们列出了各种 SQL 语句、函数、命令以及 DB2 的其他一些特性,这些特性或者是特定于 DPF 的,或者是与 DPF 有着特殊的关系。

SQL 语句

为什么分区键必须是惟一索引列的一个子集?
您是否想知道为什么会有这样的约束?假设不存在这样的约束,表 T1 有分区键 (C1,C2),在 C1 上有一个惟一索引(实际上这是不允许的,因为该惟一索引不是分区键 (C1,C2) 的超集)。如果有一行的 (C1,C2)=(1,100),且该行在分区 1 上,当插入 (C1,C2)=(1,500) 的一个新行时,通过散列其分区键将导致该行被指派到分区 2。要是允许这样的 INSERT 成功完成的话,那么就存在两个 C1 的值都为 1 的行,这实际上就是一种重复键的违例情况。问题是,DB2 只能通过扫描每个其他的分区(除了分区 2)、看是否有相同的键来防止出现上述违例的情况,这样就增加了每条 INSERT 的开销。因此,需要存在这么一个约束,因为低的开销就意味着确保惟一性。
  • ALTER/CREATE BUFFERPOOL -- ALTER 允许更改一个或所有分区中缓冲池的大小,或者添加一个缓冲池到新的数据库分区组。
  • ALTER/CREATE DATABASE PARTITION GROUP -- ALTER 允许添加分区到一个数据库分区组,或者从数据库分区组删除一个分区。
  • ALTER/CREATE TABLE -- 下面这些考虑适用于 DPF 环境:
    • 通过 ALTER 可以添加或删除分区键,但是只适用于未分区表(该限制是对分区表必须总是有一个分区键这一事实的反映)。为未分区表定义一个分区键是允许的,这样一来,不管是在分区环境中还是在未分区环境中,都有表定义的一个相应版本。
    • 您应该使用 PARTITIONING KEY 子句为分区表指定分区键,而不是使用缺省的选择。也可以为未分区表定义分区键,这同样是出于刚才提到过的那些原因。
    • 在表上定义的任何惟一性约束或主键约束中的一组列必须是分区键的一个超集,如果有这种约束的话。例如,如果表 T1 的分区键包含列 C1 和 C2,那么 T1 上任何惟一性约束或主键约束(或惟一性索引)必须包括 C1 和 C2。换句话说,如果一个表上已经有一个主键和/或惟一索引,则对分区键的选择就只能局限于在主键和所有惟一索引的列中。 要了解更多信息,请参阅 侧栏
    • IDENTITY 列可以是分区键的一部分。至于 DPF 环境中如何处理 IDENTITY 或 SEQUENCE 值,也请参阅 CREATE SEQUENCE (下面)。
    • 利用 REPLICATED 关键字可以将一个表创建为 replicated (如 DPF 概念及术语中定义)。
    • 在 CREATE 语句中,如果使用 INDEX IN 或 LONG IN 子句,则索引或 long 表空间必须与基本表位于同一个数据库分区组内。
    • 对于除编目分区以外的某个分区中的分区表或未分区表,"DATA CAPTURE CHANGES" 子句是不允许的。
  • ALTER TABLESPACE -- 如果一个分区中的表空间目前还没有容器,则可以使用该语句添加容器到该分区的 SMS 表空间。也可以用未分区数据库情况下的相同方法更改一个 DMS 表空间,不管这里是在一个或多个特定的分区上更改。
  • CONNECT TO <DATABASE> IN EXCLUSIVE MODE ON SINGLE DBPARTITIONNUM - 就像对于未分区数据库的情况一样,可以使用 "IN EXCLUSIVE MODE" 将对数据库的访问限制到那些有相同授权 ID 的用户,不过,在添加可选的 "ON SINGLE DBPARTITIONNUM" 子句时,这种方式只能限制该连接的协调分区。
  • CREATE EVENT MONITOR -- 使用 "ON DBPARTITIONNUM <partition number>" 子句将导致监视器运行在指定的分区上,并将其输出也写到这个指定的分区上。如果指定了 LOCAL,或者保留缺省设置,则只有当前运行的分区的事件才会被记录下来。如果指定了 GLOBAL,则所有分区上的事件都将被记录,但是只有对于死锁事件才支持这一点。如果指定了 WRITE TO TABLE,那么所有分区的工作都被记录在表中。
  • CREATE FUNCTION -- 因为每个函数通常都需要运行在每个分区上,因此每个外部函数的主体都应该在数据库每个分区上提供的一个目录中。
  • CREATE INDEX -- 惟一性约束或主键约束与分区键之间的关系(在 ALTER TABLE 下有描述)。
  • CREATE PROCEDURE -- 与函数一样,每个外部存储过程的主体都应该在数据库每个分区上提供的一个目录中。
  • CREATE SEQUENCE -- SEQUENCE 和 IDENTITY 值被本地缓存到每个分区上,当缓存耗尽时(缺省 size = 20),就要从编目分区获得一组新的值。较大的缓存可以减少这方面的开销,但是又倾向于增多无用值的数量。
  • CREATE TABLESPACE -- 除非您想将表空间创建到缺省的 IBMDEFAULTGROUP(或者对于系统临时表的 IBMTEMPGROUP),否则需要指定将表空间创建到哪个数据库分区组。一次可以为单个分区或一组分区定义容器,为了简化乏味的语句编写,可以使用一个数据库分区表达式: " $N" 将被 CREATE TABLESPACE 语句执行时所在的每个分区的分区号替代。
  • DECLARE GLOBAL TEMPORARY TABLE -- 这些临时表可以被分区。
  • INSERT -- 通过为大量的数据使用缓冲插入,可以大大提高性能。请参阅“缓冲插入”。

SQL 函数

  • DBPARTITIONNUM (在 V8 中的名称为 "NODENUMBER",不过以前的名称仍可以被识别) -- 使用这个函数获得一个行所在的分区号。这里必须指定表中一列的列名,但是不管列名如何输出都是一样的。要查看示例用法,请参阅 DBPARTITIONNUM 实例
  • HASHEDVALUE (在 V8 中的名称为 "PARTITION") -- 使用该函数可以获得一个行所散列到的分区图索引(0-4095)。这里必须指定表中一列的列名,但是不管列名如何输出都是一样的。要查看示例用法,请参阅 HASHEDVALUE 实例
  • GENERATE_UNIQUE -- 这将生成一个保证是惟一的字符串,即使是在各分区上以及对于多行 INSERT 操作涉及的所有行都是惟一的。
  • SNAPSHOT_XXXX 函数 -- 大多数这样的函数(例如,SNAPSHOT_DATABASE)都有一个参数,允许指定您想要在其上执行该函数的分区号。大多数这样的参数都是缺省地指向当前分区的,但是也可以选择对所有分区运行该函数。

特殊注册表

  • CURRENT DBPARTITIONNUM -- 这将返回一个 INTEGER 值,用以标识语句的协调节点号。

SQLCA

  • SQLCA 提供了一条给定语句在所有分区上的活动的合并视图。对于所有的错误和警告,sqlwarn 字段包含从所有代理那里收到的警告标志。在 sqlerrd 字段中表明行数的值是来自所有代理的累加和。
  • 这里一个有趣的特殊字段是 sqlerrd(6)。对于一个分区数据库,它包含碰到错误或警告的分区的分区号。
  • 要了解更多信息,请参阅 SQL Reference, Volume 1 中的话题 "SQLCA usage in partitioned database systems"。

编目视图

  • SYSCAT.BUFFERPOOLDBPARTITIONS -- 缺省情况下一个分区上的 default a buffer pool has the same number of pages on each partition, 这个数字包含在 SYSCAT.BUFFERPOOLS 的列 NPAGES 中。对于使用非缺省大小的缓冲池的每个分区,在 SYSCAT.BUFFERPOOLDBPARTITIONS 中都有相应的一行。
  • SYSCAT.COLUMNS -- 列 PARTKEYSEQ 包含该列在表的分区键内的数字位置(如果该列不是分区键的一部分,则为 null 或 0)。
  • SYSCAT.DBPARTITIONGROUPDEF -- 包含一行,该行包括一个数据库分区组中每个分区的分区号和状态。
  • SYSCAT.DBPARTITIONGROUPS -- 对于每个数据库分区组都包含一行,包括指向其分区图的指针。
  • SYSCAT.EVENTMONITORS -- 列 DBPARTITIONNUM 表明监视器运行时和日志事件所针对的分区。
  • SYSCAT.PARTITIONMAPS -- 对于每个分区图都包含一行。
  • SYSCAT.TABLES -- 列 PMAP_ID 保存表所使用的分区图的 ID。如果表是 partitioned 的,则列 PARTITION_MODE 包含 "H" 值,如果表是 replicated 的,则该列包含 "R" 值。
  • SYSCAT.TABLESPACES -- 列 DBPGNAME 包含表空间的数据库分区组的名称。

命令

有很多命令没有包括在下面的 DB2 系统和 CLP 命令列表中,这些命令每次调用时只应用于单个分区。 通常这些命令都可以归为几种类型中的一种: (a) 只应用于应用程序附加到的或连接到的那个分区。例如, GET DATABASE CONFIGURATIONLIST TABLESPACES 。 (b) 有一个 DBPARTITIONNUM 选项指定运行该命令时所针对的分区号。例如, LIST APPLICATIONS AT DBPARTITIONNUM 4

  • db2_all、rah - 请参阅 其他技巧下关于上述类型的讨论。
  • db2batch - 基准测试工具。可以与 "-p" 选项一起使用各种值来为大的结果集的 SELECT 处理请求不同的优化技术,例如将输出写到每个分区上的本地文件中。
  • db2icrt - 创建一个实例。对于 Windows 上的 DPF 有一些特殊的选项。
  • db2iupdt - 在 Windows 上,这允许将一个实例从单分区的转换成多分区的。
  • db2nchg - 创建分区服务器配置
  • db2ncrt - 添加 DB 分区服务器(仅限于 Windows)。
  • db2ndrop - 删除 Db2 分区服务器(仅限于 Windows)。
  • db2start ... add dbpartitionnum - 启动 DB2 并添加一个新的分区到一个已有的实例和数据库。
  • DROP DBPARTITIONNUM VERIFY - 在删除一个分区之前验证它未被使用。
  • ADD DBPARTITIONNUM - 在实例中的每个数据库中创建一个新的空分区。
  • LIST DATABASE PARTITION GROUPS - 列出应用程序连接到的数据库中的数据库分区组。
  • LIST DBPARTITIONNUMS - 列出应用程序连接到的数据库中的分区号。
  • LOAD - 将数据装载到一个表中,要么是在一个分区上进行,要么是在表的分区的任何子集上进行。请参阅 填充表
  • RESTRIBUTE DATABASE PARTITION GROUP - 在数据库分区组的分区之间移动行。最常用于在添加分区之后平均地分布行。也可以用于在删除一个分区之前将行从中移出。
  • SET CLIENT - 使用它来设置分区,使其被随后的 CONNECT 或 ATTACH 使用。可以提供一个特定的分区号,或者为编目分区提供 CATALOG_DBPARTITIONNUM。
  • QUERY CLIENT - 如果通过 SET CLIENT 或对 DB2NODE 的设置而设置了 CONNECT_DBPARTITIONNUM 和 ATTACH_DBPARTITIONNUM 的值,则显示它们的当前值。

管理 API

实质上,这些 API 提供了跟相应的 DB2 命令相同的功能和选项。找找允许提供分区号的参数。与 DPF 特别相关的 API 有:

  • sqleaddn - 增加节点
  • sqledrpn - 删除节点校验
  • sqludrdt - 重新分布数据库分区组
  • sqlugrpn - 获取行分区号
  • sqlugtpi - 获取表分区信息

注册表和环境变量

这些变量都是 DPF 环境特有的。除了 DB2NODE 之外,所有变量都是注册表变量。

  • DB2NODE (环境变量) -- 在通过 CONNECT 或 ATTACH 连接或附加到一个特定分区之前设置该变量。
  • DB2_VI_ENABLE - 在 Windows 上(仅用于该平台),为提供更快的分区间通信,支持虚拟接口(Virtual Interface,VI)架构通信协议。
  • DB2_FORCE_FCM_BP - 在 AIX 上(仅用于该平台),通过使用共享内存而不是 UNIX socket,使得相同物理系统上的分区之间的通信更快。
  • DB2_PARTITIONEDLOAD_DEFAULT - 当命令中没有使用特定于 DPF 的参数时,该变量允许更改 LOAD 的缺省行为(load on multiple partitions or not)。
  • DB2_SORT_AFTER_TQ - 当在分区之间传送行时,影响优化器决定在发送端还是在接收端对行进行排序。

配置参数

这些变量(DBM 或 DB)在 DPF 环境中有着特殊的意义。记住,每个分区都有其自己的一组数据库配置参数,并且在每个分区上的这些变量都应该设置为相同的值。

  • CONN_ELAPSE (DBM), MAX_CONNRETRIES (DBM) - 当一个分区尝试连接到同一个数据库中的另一个分区时,DB2 将等待 CONN_ELAPSE 秒钟的时间,如果不成功就放弃这次连接并重新尝试。DB2 将尝试 MAX_CONNRETRIES 次,如果还不成功的话就停止尝试并发出一个错误。
  • FCM_NUM_BUFFERS (DBM) - 这个变量指定用于内部通信(消息)的 4KB 大小的缓冲区的数目,这种通信可以发生在数据库分区之间,也可以发生在数据库分内部。
  • MAX_TIME_DIFF (DBM) - 每个数据库分区服务器都有其自己的系统时钟。这个参数指定数据库分区之间允许的最大时间差,单位为分钟。
  • START_STOP_TIME (DBM) - 这个变量指定所有数据库分区服务器必须在这么长的时间内对一个 DB2START 或 DB2STOP 命令作出响应,单位为分钟。
  • COMM_BANDWIDTH (DBM) - DB2 计算这个值,这是分区之间通信的通信带宽,单位为 MB 每秒。您可以更改这个值,以便为 SQL 优化器上不同通信速度的效果建立模型。
  • DIAGPATH (DBM) - 请参阅早先关于通过将此参数设置为相对每个分区是本地的一个路径来减少开销的注解(参阅 其他技巧)。
  • CATALOGCACHE_SZ (DB) - 在分区数据库环境中存在于所有分区上的编目缓存。在非编目分区上的一个编目缓存就像是在编目分区节点上编目缓存中的信息的一个子集,因此,因此在非编目分区上 CATALOGCACHE_SZ 的值要小于编目分区上该变量的值。
  • APP_CTL_HEAP_SZ (DB) - 对于分区数据库,此变量指定为一个应用程序分配的共享内存区域的平均大小。对于每个分区的每个连接,都有一个应用程序控制堆。
  • DLCHKTIME (DB) - 在分区数据库环境中,此参数只适用于编目分区。在这样的环境中,只有在死锁检测器第二次检索到死锁时,才会标记死锁。
  • MAXAPPLS (DB) - 在分区数据库环境中,编目分区要求 MAXAPPLS 的值大于其他类型的环境中该变量的值,因为在分区数据库环境中,每个应用程序都需要到编目分区的一个连接。





回页首


附录 B:示例代码和输出


清单 1. DBPARTITIONNUM 函数的使用

-- Display the number of rows in the STAFF table in each partition
select dbpartitionnum(id) as PARTITION_NUMBER, 
       count(*) as NUMBER_OF_ROWS
  from staff 
  group by dbpartitionnum(id)
  order by dbpartitionnum(id)
-- sample output for the above on a 3-partition sample database (only 35 rows in table):
PARTITION_NUMBER NUMBER_OF_ROWS
---------------- --------------
               0             12
               1             10
               2             13 


清单 2. HASHEDVALUE 函数的使用

-- Display the number of rows in the STAFF table that hash to each entry in the partition map
select hashedvalue(id) as PARTITION_MAP_INDEX, 
       count(*) as NUMBER_OF_ROWS
  from staff 
  group by hashedvalue(id)
  order by hashedvalue(id);
-- sample output for the above (with only 35 rows, no index entry had more than one row hashing to it):
PARTITION_MAP_INDEX NUMBER_OF_ROWS
------------------- --------------
                443              1
                594              1
                647              1
                                    <== 30 lines omitted here (index values from 681 to 3859 with 1 row each)
               4001              1
               4017              1 


清单 3. 连接或附加到特定的分区 (使用 SET CLIENT 命令)以及显示状态

$ 
        db2 set client connect_dbpartitionnum catalog_dbpartitionnum
$ 
        db2 set client attach_dbpartitionnum 2
$ db2 connect to sample
$ db2 values current dbpartitionnum
1
-----------
          0   [in this example, the catalog partition is partition 0]
$ db2 query client
The current connection settings of the application process are:
                   CONNECT   = 1
                DISCONNECT   = EXPLICIT
   MAX_NETBIOS_CONNECTIONS   = 0
                  SQLRULES   = DB2
                 SYNCPOINT   = ONEPHASE
         CONNECT_DBPARTITIONNUM   = CATALOG_DBPARTITIONNUM
        
ATTACH_DBPARTITIONNUM = 2


清单 4. 确定一个数据库的编目分区

$ db2 list db directory
 System Database Directory
 Number of entries in the directory = 1
Database 1 entry:
 Database alias                       = P2DB
 Database name                        = P2DB
 Local database directory             = /notnfs/wilkins
 Database release level               = a.00
 Comment                              =
 Directory entry type                 = Indirect
        
 Catalog database partition number    = 2
      


清单 5. 在 AIX 上 db2_all 的使用

-- This command is executed on each partition in parallel, and each partition's output goes to a