分类:
2010-05-16 10:39:45
DB2 UDB 内存模型DB2 如何使用内存 |
级 别: 初级 Sylvia
Qi (),
DB2 UDB 支持专家, IBM 2004 年 8 月 01 日 本文将向您传授关于 DB2 如何使用内存的基础知识。而且,本文还将详细讨论 32 位内存体系结构的一些限制。对于 AIX、Solaris、HP-UX、Linux 和 Windows 这些受支持的每个平台,我们都给出了最常见的关于内存分配问题的例子。 请到 IBM 评估版软件下载 下载和试用 IBM 软件。
理解 DB2 如何使用内存,可以防止过度分配内存,并有助于对内存的使用进行调优,从而获得更好的性能。 本文将向您传授 DB2 内存使用的基础,以及共享内存和私有内存的概念。这些内容同时适用于 32 位和 64 位的系统。虽然对于 64 位系统有一些限制,但是在未来的一段时间内还不大可能触及这些限制。因此,我们将焦点放在影响 32 位系统的内存限制,并对之进行详细的讨论。 我们首先讨论一般情况下 DB2 如何使用内存,接着讨论内存管理如何随着平台(AIX、Sun、HP、Linux 和 Windows)的不同而变化,以及它们对 DB2 的影响。最后,我们将给出一些实际生活中客户处境/问题以及他们的解决方案的有意义的例子。 本文的内容适用于 DB2 version 8。
图 1中说明了 DB2 内存结构。这种内存结构在所有平台上都是一致的。 注意:在多分区环境中,下面的图适用于多分区实例中的每个分区。 DB2 在 4 种不同的内存集(memory set)内拆分和管理内存。这 4 种内存集分别是:
每种内存集由各种不同的内存池(亦称堆)组成。图 1 也给出了各内存池的名称。例如, locklist是属于数据库共享内存集的一个内存池。 sortheap是属于代理私有内存集的一个内存池。 我们将详细讨论每一种内存集。 每个 DB2 实例都有一个实例共享内存。实例共享内存是在数据库管理器启动(db2start)时分配的,并随着数据库管理器的停止(db2stop)而释放。这种内 存集用于实例级的任务,例如监控、审计和节点间通信。下面的数据库管理器配置(dbm cfg)参数控制着对实例共享内存以及其中个别内存池的限制:
instance_memory参数指定为实例管理预留的内存数量。默认值是 AUTOMATIC。这意味着 DB2 将根据监视器堆、审计缓冲区和 FCM 缓冲区的大小计算当前配置所需的实例内存数量。此外,DB2 还将分配一些额外的内存,作为溢出缓冲区。每当某个堆超出了其配置的大小时,便可以使用溢出缓冲区来满足实例共享内存区内任何堆的峰值需求。在这种情况 下,个别堆的设置是 软限制的,它们可以在内存使用的峰值期间进一步增长。 如果 instance_memory被设置为某一个数字,则采用 instance_memory与 mon_heap_sz、 audit_buf_sz和 fcm_num_buffers的和之间的较大者。这时,对实例内存就施加了一个硬性的限制,而不是软限制。当达到这个限 制时,就会收到内存分配错误。出于这个原因,建议将 instance_memory的设置保留为 AUTOMATIC。 如果 instance_memory被设为 AUTOMATIC,则可以使用下面的命令来确定它的值:
下面的输出表明有 42 MB 的内存被预留给实例共享内存集(10313 页 * 4096 字节/页):
instance_memory参数只是设置了实例共享内存的限制。它并没有说出当前使用了多少内存。要查明一个实例的内 存使用情况,可以使用 DB2 内存跟踪器工具 db2mtrk。例如,
上面的例子表明,虽然预留给实例共享内存集的内存有 42 MB,但在 db2mtrk运行时只用到了大约 21 MB。 注意: 在某些情况下,db2mtrk 显示的大小会大于指定给配置参数的值。在这种情况下,赋予配置参数的值被作为一种软限制,内存池实际使用的内存可能会增长,从而超出配置的大小。 每个数据库有一个数据库共享内存集。数据库共享内存是在数据库被激活或者第一次被连接上的时候分配的。该内存集将在数据库处于非激活状 态时释放(如果数据库先前是处于激活状态)或者最后一个连接被断开的时候释放。这种内存用于数据库级的任务,例如备份/恢复、锁定和 SQL 的执行。 图 2展示了数据库共享内存集内的各种内存池。括号中显示了控制这些内存池大小的配置参数。 完整的绿色方框意味着,在数据库启动的时候,该内存池是完全分配的,否则,就只分配部分的内存。例如,当一个数据库第一次启动时,不管 util_heap_sz的值是多少,只有大约 16 KB 的内存被分配给实用程序堆。当一个数据库实用程序(例如备份、恢复、导出、导入和装载)启动时,才会按 util_heap_sz指定的大小分配全额的内存。 主缓冲池 数据库缓冲池通常是数据库共享内存中最大的一块内存。DB2 在其中操纵所有常规数据和索引数据。一个数据库必须至少有一个缓冲池,并且可以有多个缓冲池,这要视工作负载的特征、数据库中使用的数据库页面大小等因素 而定。例如,页面大小为 8KB 的表空间只能使用页面大小为 8KB 的缓冲池。 可以通过 CREATE BUFFERPOOL 语句中的 EXTENDED STORAGE 选项“扩展”缓冲池。扩展的存储(ESTORE)充当的是从缓冲池中被逐出的页的辅助缓存,这样可以减少 I/O。ESTORE 的大小由 num_estore_segs 和 estore_seg_sz 这两个数据库配置参数来控制。如果使用 ESTORE,那么就要从数据库共享内存中拿出一定的内存,用于管理 ESTORE,这意味着用于其他内存池的内存将更少。 这时您可能要问,为什么要这么麻烦去使用 ESTORE?为什么不分配一个更大的缓冲池呢?答案跟可寻址内存(而不是物理内存)的限制有关,我们在后面会加以讨论。 隐藏的缓冲池 当数据库启动时,要分配 4 个页宽分别为 4K、8K、16K 和 32K 的小型缓冲池。这些缓冲池是“隐藏”的,因为在系统编目中看不到它们(通过 SELECT * FROM SYSCAT.BUFFERPOOLS 显示不出)。 如果主缓冲池配置得太大,则可能出现主缓冲池不适合可寻址内存空间的情况。(我们在后面会谈到可寻址内存。)这意味着 DB2 无法启动数据库,因为一个数据库至少必须有一个缓冲池。如果数据库没有启动,那么就不能连接到数据库,也就不能更改缓冲池的大小。由于这个原因,DB2 预先分配了 4 个这样的小型缓冲池。这样,一旦主缓冲池无法启动,DB2 还可以使用这些小型的缓冲池来启动数据库。(在此情况下,用户将收到一条警告(SQLSTATE 01626))。这时,应该连接到数据库,并减少主缓冲池的大小。 排序堆的阈值( sheapthres, sheapthres_shr) 如果没有索引满足所取的行的要求顺序,或者优化器断定排序的代价低于索引扫描,那么就需要进行排序。DB2 中有两种排序,一种是私有排序,一种是共享排序。私有排序发生在代理的私有代理内存(在下一节讨论)中,而共享排序发生在数据库的数据库共享内存中。 对于私有排序,数据库管理器配置参数 sheapthres指定了私有排序在任何时刻可以消耗的内存总量在实例范围内的 软限制。如果一个实例总共消耗的私有排序内存达到了这一限制,那么为额外传入的私有排序请求所分配的内存将大大减少。这样 就会在 db2diag.log 中看到如下消息: "Not enough memory available for a (private) sort heap of size size of sortheap. Trying smaller size..." 如果启用了内部分区并行性(intra-partition parallelism)或者集中器(concentrator),那么当 DB2 断定共享排序比私有排序更有效时,DB2 就会选择执行共享排序。如果执行共享排序,那么就会在数据库共享内存中分配用于这种排序的排序堆。用于共享排序的最大内存量是由 sheapthres_shr数据库参数指定的。这是对共享排序在任何时刻可以消耗的内存总量在数据库范围内的 硬限制。当达到这个限制时,请求排序的应用程序将收到错误 SQL0955 (rc2)。之后,在共享内存总消耗量回落到低于由 sheapthres_shr指定的限制之前,任何共享排序内存的请求都得不到允许。 下面的公式可以计算出数据库共享内存集大致需要多少内存: 数据库共享内存 = (主缓冲池 + 4 个隐藏的缓冲池 + 数据库堆 +实用程序堆 + locklist + 包缓存 + 编目缓存) + (estore 的页数 * 100 字节) + 大约 10% 的开销 对于启用了 intra_parallel 或集中器情况下的数据库,共享排序内存必须作为数据库共享内存的一部分预先分配,因而上述公式变为: 数据库共享内存 = (主缓冲池 + 4 个隐藏的缓冲池 + 数据库堆 +实用程序堆 + locklist + 包缓存 + 编目缓存 + sheapthres_shr) + (estore 的页数 * 100 字节) + 大约 10% 的开销
提示:
为了发现分配给主缓冲池的内存有多少,可以发出:
虽然大多数内存池的大小是由它们的配置参数预先确定的,但下面两种内存池的大小在默认情况下却是动态的:
将 maxappls设为 AUTOMATIC的效果是,允许任意数量的连接数据库的应用程序。DB2 将动态地分配所需资源,以支持新的应用程序。因此,包缓存和编目的大小可以随着 maxappls的值而变化。 除了上述参数以外,还有一个参数也会影响数据库共享内存的数量。这个参数就是 database_memory。该参数的缺省值是 AUTOMATIC。这意味着 DB2 将根据以上列出的各内存池的大小来计算当前配置所需的数据库内存量。此外,DB2 还将为溢出缓冲区分配一些额外的内存。每当某个堆超出了其配置的大小时,便可以使用溢出缓冲区来满足实例共享内存区内任何堆的峰值需求。 如果 database_memory被设为某个数字,则采用 database_memory与各内存池之和这两者之间的较大者。 如果 database_memory被设为 AUTOMATIC,则可以使用以下命令来显示它的值:
使用
db2mtrk 工具显示当前使用的内存量:
应用程序组共享内存 这种共享内存集仅适用于以下环境。(对于其他环境,这种内存集不存在。)
注意:当 max_connections的值大于 max_coordagents的值时,连接集中器便被启用。这两个参数可以在数据库管理器配置中找到。(使用 GET DBM CFG 显示数据库管理器配置。) 在以上环境中,应用程序通常需要不止一个的代理来执行其任务。允许这些代理之间能够彼此通信(相互发送/接收数据)很有必要。为了实现 这一点,我们将这些代理放入到一个称作 应用程序组的组中。属于相同应用程序组的所有 DB2 代理都使用 应用程序组共享内存进行通信。 应用程序组内存集是从数据库共享内存集中分配的。其大小由 appgroup_mem_sz数据库配置参数决定。 多个应用程序可以指派给同一个应用程序组。一个应用程序组内可以容纳的应用程序数可以这样计算: appgroup_mem_sz / app_ctl_heap_sz 在应用程序组内,每个应用程序都有其自己的 应用程序控制堆。此外,应用程序组共享内存中有一部分要预留给应用程序组共享堆。如下图所示:
例 1
可以计算出下面的值:
不要被 app_ctrl_heap_sz 参数迷惑。这个参数不是一个应用程序组内用于每个应用程序的各应用程序控制堆的大小。它只是在计算这个应用程序组内可容纳多少应用程序时用到的一个值。每 个应用程序的实际应用程序控制堆大小都是通过 图 3中给出的公式计算的,这个公式就是 ((100 - groupheap_ratio)% * app_ctrl_heap_sz)。 因此,groupheap_ratio 越高,应用程序组共享堆就越大,从而用于每个应用程序的应用程序控制堆就越小。
例 2
每个 DB2 代理进程都需要获得内存,以执行其任务。代理进程将代表应用程序使用内存来优化、构建和执行访问计划,执行排序,记录游标信息(例如位置和状态),收集统 计信息,等等。为响应并行环境中的一个连接请求或一个新的 SQL 请求,要为一个 DB2 代理分配代理私有内存。 代理的数量受下面两者中的较低者限制:
代理私有内存集由以下内存池组成。这些内存池的大小由括号中的数据库配置参数指定:
我们曾提到,私有内存是在一个 DB2 代理被“指派”执行任务时分配给该代理的。那么,私有内存何时释放呢?答案取决于 dbm cfg 参数 num_poolagents的值。该参数的值指定任何时候可以保留的闲置代理的最大数目。如果该值为 0,那么就不允许有限制代理。只要一个代理完成了它的工作,这个代理就要被销毁,它的内存也要返回给操作系统。如果该参数被设为一个非零值,那么一个代理 在完成其工作后不会被销毁。相反,它将被返回到闲置代理池,直到闲置代理的数目到达 num_poolagents指定的最大值。当传入一个新的请求时,就要调用这些闲置代理来服务该新请求。这样就减少了创 建和销毁代理的开销。 当代理变成闲置代理时,它仍然保留了其代理的私有内存。这样设计是为了提高性能,因为当代理被再次调用时,它便有准备好的私有内存。如 果有很多的闲置代理,并且所有这些闲置代理都保留了它们的私有内存,那么就可能导致系统耗尽内存。为了避免这种情况,DB2 使用一个注册表变量来限制每个闲置代理可以保留的内存量。这个变量就是 DB2MEMMAXFREE。它的默认值是 8 388 608 字节。这意味着每个闲置代理可以保留最多 8MB 的私有内存。如果有 100 个闲置代理,那么这些代理将保留 800MB 的内存,因此它们很快就会耗尽 RAM。您可能希望降低或增加这一限制,这取决于 RAM 的大小。 图 1展示了一个 DB2 实例的 DB2 内存结构。 图 4将展示在同一个系统上有两个实例并发运行的情况。虚拟内存包括物理 RAM 和调页空间(paging space)。共享内存“倾向于”留在 RAM 中,因为对它们的访问更频繁。如果代理闲置了较长的一段时间,则其代理私有内存将被调出。
至此,我们已经讨论了实例共享内存、数据库共享内存和应用程序组共享内存以及代理私有内存。但是共享内存和私有内存的意义是什么呢? 为了理解共享内存与私有内存之间的不同之处,首先让我们通过快速阅读 DB2 进程 model来了解一下 DB2 代理进程。在 DB2 中,所有数据库请求都是由 DB2 代理或子代理来服务的。例如,当一个应用程序连接到一个数据库时,就有一个 DB2 代理指派给它。当该应用程序发出任何数据库请求(例如一个 SQL 查询)时,该代理就会出来执行完成这个查询所需的所有任务 —— 它代表该应用程序工作。(如果数据库是分区的,或者启用了 intra-parallel,那么可以分配不止一个的代理来代表应用程序工作。这些代理叫做 子代理。) 每个代理或子代理都被当作一个 DB2 进程,它获得一定数量的内存来执行工作。这种内存被称作 代理私有内存—— 它不能与其他任何代理共享。之前我们曾提到过,代理私有内存包括一些内存池,例如应用程序堆大小、排序堆大小和语句堆大小。(参见 图 1) 除了私有内存(代理在其中使用 排序堆执行“私有”任务,例如私有排序)外,代理还需要数据库级的资源,例如缓冲池、 locklist和日志缓冲区。这些资源在数据库共享内存中(参见 图 1)。DB2 的工作方式是,数据库共享内存中的所有资源都由连接到相同数据库的所有代理或子代理共享。因此,该内存集被称作共享内存,而不是私有内存。例如,连接到数 据库 A 的代理 x 使用数据库 A 的数据库共享内存中的资源。现在又有一个代理,即代理 y 也连接到数据库 A。那么代理 y 将与代理 x 共享数据库 A 的数据库内存。(当然,代理 x 和代理 y 都有其自己的代理私有内存,这些代理私有内存不是共享的。) 这样的逻辑同样适用于实例共享内存和应用程序组共享内存。 下图展示了当两个 DB2 代理(代理 x 和代理 y)连接到数据库 A 时分配的 DB2 内存集。假设:
图 5 展示了在 RAM 中分配的以下内存集:
代理 x 和代理 y 共享相同的实例内存、数据库内存和应用程序组内存,因为它们属于相同的实例、相同的数据库和相同的应用程序组。此外,它们有其自己的代理私有内存。 每个 DB2 代理进程都有其自己的内存地址空间。在内存空间中的内存地址允许代理访问物理 RAM 中的内存。我们可以把这些地址看作指向 RAM 的指针,如 图 5所示。对于任何 DB2 进程,这个地址空间必须能够容纳上述所有 4 种内存集。 前面已提到,ESTORE 是用于扩展缓冲池的大小。那么您可能要问,为什么不创建一个更大的缓冲池呢。答案是:因为地址空间受到限制,地址空间可能容不下一个更大的缓冲池!在此情 况下,需要定义一个较小的地址空间能够容纳的缓冲池。如果有过量的物理内存,那么可以用该内存来配置 ESTORE。 那么,我们怎么知道一个 DB2 代理的地址空间是多大呢?地址空间的大小取决于当前的实例是 32 位的实例还是 64 位的实例。我们将在下一节对此进行解释。
如果有一个 64 位的 DB2 实例,则意味着 DB2 使用的是 64 位的内存体系结构。在这种体系结构中,对于所有平台,每个进程的地址空间都是 2 的 64 次方,或者 18,446,744,073 GB。这是一个相当巨大的内存。将所有 DB2 内存集放入这个地址空间应该没有问题。 另一方面,如果有一个 32 位 DB2 实例,则对于所有平台,地址空间只有 2 的 32 次方,或者 4 GB(Linux/390 平台除外,在此平台下地址空间实际上只有 2 的 31 次方。不过,在本文中我们不讨论 Linux/390 中的 DB2)。所以,不管物理 RAM 有多大,要使一个 DB2 进程能够访问它所需的所有资源,包括实例共享内存、数据库共享内存、应用程序组共享内存、它自己的代理私有内存以及用于内核的内存等,所有这些资源必须能 放入到 4GB 的地址空间内。 这就导致了两个非常重要的问题:
虽然 4GB 的地址空间限制适用于所有平台,但是对于上述问题的回答却与平台有关。例如,在 AIX 系统上可以分配给 DB2 数据库的最大数据库内存数就与 Solaris 系统上的数据库不同。接下来的几节将讨论不同的平台对 DB2 中的内存配置有何影响。 注意: 本文的后续部分只针对 32 位内存体系结构。我们即将讨论的问题不适用于 64 位的体系结构。
在 32 位的 AIX 上,4GB 的可寻址内存空间被拆分为 16 个段,每段 256MB。 图 6展示了用于一个 DB2 代理进程的 32位 内存地址空间。(假设 DB2_MMAP_READ 和 DB2_MMA_WRITE 这两个 DB2 注册表变量都被设为 NO。如果这两个变量没有设为 NO,则表示方法会有点不同。我们将在后面解释。) 段 0 - 预留给 AIX 内核。 段 1 - 预留给 db2sysc 进程。 段 2 - 预留给代理私有内存。 段 3 - 预留给实例共享内存。 段 4 到段 B - 数据库共享内存始于段 4,这些段必须紧挨在一起。所有这 8 个段(2GB)可能都被用于数据库共享内存。但是,下面的每种配置都会从数据库共享内存中拿出一个段(256MB)。 注意: 对于下面的每种配置,DB2 将从数据库共享内存中拿出一个段,这个段始于段 B。
然而,如果您不想使用 loopback 解决方案,还有一种方法可以迫使 DB2 选择段 E 来用于代理/本地应用程序通信,这样数据库共享内存就不受影响(即不会减少)。请参阅后面的解释。
段 C - 预留给 DB2 跟踪使用程序。 段 D 和 F- 预留给 DB2 共享库 段 E - 在默认情况下,这个段是不用的。不过,如果设置 DB2_MMAP_READ=NO 和 DB2_MMAP_WRITE=NO,那么该段用于 DB2 代理以及本地应用程序之间的通信(如 图 6所示)。这将有效地为数据库共享内存一个段。 注意: 为了最大化数据库共享内存的空间,应使用以下注册表变量设置:DB2_FORCE_FCM_BP=NO (该值是默认值),DB2_MMAP_READ=NO,DB2_MMAP_WRITE=NO。 需要从这种结构中了解到的最重要的事情是:
这些限制规定了我们该如何配置数据库共享内存集中的每个内存池。可以使用前面给出的公式来计算数据库共享内存。得到的总和不能超过这个 限制。 注意: 内存可以分配,释放,也可以当数据库正在运行时在不同区域之间交换。例如,您可以减少 locklist 而增加相同数量的任何一个给定的缓冲池。 在 AIX 上,除了 db2mtrk 工具外,还可以使用 svmon 工具来监控 DB2 代理进程的内存消耗情况(需要 root 权限)。这个命令是: "svmon -P PID",其中 PID是 DB2 代理(db2agent 或 db2agentp)的进程 ID。 图 7 展示了 svmon 对于一个名为 db2agent 的进程的示例输出。与这个 db2agent 进程相关的数据库有如下特征:
从 图 7 中观察到的一些情况:
观察到的这些情况与 图 6中说明的相匹配。 我们早先说过, 图 6中的段 #2 是用于代理私有内存的。实际上,并非完全如此。 确切地说,段 #2 被预留给数据和堆栈。数据包含用户数据(即代理私有内存)。而堆栈则包含要执行的指令。在这个 256M 的段中,数据是从地址 0x20000000 向下增长的。而堆栈是从地址 0x2FFFFFFF 向上增长的。为了不让数据和堆栈有冲突,设置它们的限制就十分重要。如果数据和堆栈真的有冲突,那么实例就会崩溃,并产生信号 4 或信号 11。 数据和堆栈的限制是在 ulimits(/etc/security/limits)中设置的。使用 SMIT 将它们设置成如下值:
上述值以 512 字节为单位,其中对于数据是 240MB,对于堆栈是 16MB。当使用 "ulimit -a"
显示这两个限制时,显示的值以 1K 字节为单位,而不是以 512 字节为单位。因此这两个值变为:
注意: /etc/security/limits 包含了以 512 字节为单位的限制,而不是以 1 KB 为单位的限制。 初始化数据库共享内存失败可能产生如下问题:
下面的例子展示了会导致问题的不恰当配置。 例 1 考虑以下配置:(所有页的大小为 4KB)
限制:在此配置中,对于数据库共享内存的限制是 2GB 或 8 个段。 计算:使用公式计算数据库共享内存,我们得到:
注意: 我们会将 4 个隐藏缓冲池排除在计算之外,因为它们太小了,不会产生明显的不同。 问题: 这超出了 2GB 的限制。当激活数据库或第一次连接到数据库时,您将收到如下警告:
在 db2diag.log 中,您将看到有消息说 DB2 将利用隐藏缓冲池启动。要解决这个问题,可以减少主缓冲池的大小。 例 2 考虑如下配置: (所有页的大小都是 4K)
限制: 1.5GB (6 个段)。一个段用于应用程序组内存,因为 INTRA_PARALLEL 是 ON。另一个段用于本地连接,因为 DB2_MMAP_READ 和 DB2_MMAP_WRITE 都被设为 YES (该值是默认值)。 计算:
问题: 这超出了 1.5GB 的限制。当尝试激活数据库或第一次连接到数据库时,您将得到以下错误消息:
要解决这一问题:
例 3 考虑如下配置: (所有页的大小都为 4K)
限制: 以下各占一个段:Intra-parallel ON,本地连接,以及 fenced UDFB。这样还剩下 5 个段,或者 1.25G 给数据库共享内存。 计算:
问题: 引用了 fenced UDF 的查询就会失败,并返回错误 sql10003C。要解决这个问题,可以在 unfenced 模式下运行 UDF,或者将数据库共享内存减少至不多于 5 个段。
在 AIX 中,每个段的大小都是 256MB,而在 Solaris 中则有所不同,其内存段的大小不是固定的。32 位 Solaris 可寻址内存结构如 图 9所示。 从 0x0 到 0x00010000 之间的地址不能使用。第一个段始于 0x00010000,这个段被预留给 db2sysc 可执行程序和代理私有内存。在缺省情况下,这个段结束于 0x10000000,因而这个段总共是 256MB。(我们可以通过设置 DB2DBMSADDR DB2 注册表变量使这个段更大一些,见后面)。在这个段内,db2sysc 可执行程序只占用很小的一片内存,剩下的用于代理私有内存(200MB+)。 在默认情况下,实例共享内存的起始地址固定于
0x10000000。不过,也可以将其改为一个更高的地址,以便有更多的空间留给代理私有内存。例如,如果实例共享内存始于
0x12000000,而不是 0x10000000,那么就有 0x12000000 减去 0x10000000 即 32MB
的额外内存被用于代理私有内存。要做到这一点,可以将 DB2DBMSADDR DB2 注册表变量设为如下值:
注意: DB2DBMSADDR 值的范围是从 0x10000000 到 0x10FFFFFF,每次递增 0x10000。 实例共享内存的结束地址是不固定的。这意味着实例共享内存可能会很大。紧接着实例共享内存的是数据库共享内存。因为实例共享内存的结束 地址不固定,所以数据库共享内存的起始地址也是不固定的。 在数据库共享内存之后的是用于 DB2 跟踪、共享库和堆栈(即将执行的指令)的内存。在 AIX 中,堆栈和代理私有内存共享相同的内存段,而在 Solaris 中则有所不同,其中代理私有内存和堆栈分别使用不同的内存段。因此,这里不存在两者之间发生冲突的风险。 由于这些内存段的大小不是固定的,我们只能估计实例共享内存和数据库共享内存的大小为:
注意这个数量同时还包括实例共享内存和数据库共享内存,并且它们的内存段必须是邻接的。 为了看一看实际中如何使用内存,可以对 db2sysc 或 db2agent 进程的 ID 运行 /usr/proc/bin/pmap 命令(以 root 的身份)。 注意: 在 Solaris 中,当使用 "pe -ef | grep instance_name" 命令显示 DB2 进程时,所有进程都作为 db2sysc 进程显示。可以使用 db2ptree命令来以“真实”名称显示 DB2 进程。 图 9展示了 pmap 命令的一个示例输出。 从 图 10 中可以观察到下面一些情况:
注意: 在 32 位 Solaris 中,我们通常将 DB2 数据库共享内存限制在大约 3.5 GB。 如果设置了 DB2DBMSADDR 注册表变量,那么实例共享内存将从该变量指定的地址开始。下面的例子展示了这一点是如何实现的。 例子 设置 DB2DBMSADDR 注册表变量:
获得 db2sys 进程的进程 ID
以 root 的身份 cd 到 /usr/proc/pmap,并对任何 db2sysc 进程运行 pmap:
pmap 输出:
注意,实例共享内存现在从 0x12000000 开始,而不是从默认地址 0x10000000 开始。代理私有内存的大小(由 'heap' 标出)从 220MB ( 图 10)增加到了 252 MB。(0x12000000 - 0x023F6000 = 0xFC0A000 = 264282112 (十进制) = ~252MB) 您可能注意到, 图 9中给出的 4GB 地址空间没有包括任何内核内存。这就对了,在 Solaris 中,内核有其自己的地址空间,该地址空间与进程的地址空间是分开的。这样就将更多的空间留给了其他内存集,例如数据库共享内存。 虽然与 AIX 相比,在 Solaris 中我们有更大的地址空间用于数据库共享内存(在 AIX 上是 2GB),但是在 Solaris 上所有共享内存都是固定在物理 RAM 中。如果 RAM 比较小,那么对于可以并发运行的数据库数目就有很大的影响。请参阅 “Sun Solaris 中与分配数据库共享内存有关的常见问题”一节中的例 2。 需要从这种结构中了解到的最重要的事情是:
没有充分配置内核参数以及初始化数据库共享内存失败可能导致如下失败:
在 Solaris 系统中,DB2 提供了一个叫做 db2osconf的工具。该工具根据系统的大小对内核参数的值给出建议。对于一个给定的系统,建议的值要足够高,以便能 够容纳最合理的工作负载。 最常见的未能正确设置的内核参数是 shmmax。该参数按字节指定系统中可以分配的共享内存段的最大大小。如果把 DB2 配置成创建大于这个值的数据库共享内存,那么请求就会失败。其他要知道的内核参数是 shmseg和 shmmni。 Solaris 中另一个与分配数据库共享内存有关的常见问题是由于这样的一个事实导致的,即共享内存段的所有页都是固定在物理 RAM 中的。如果在 RAM 中没有足够的空闲页可用,或者没有足够的可以被 OS 为满足数据库段而调出的其他页,那么启动数据库的请求就会遭到失败。 下面的例子展示了会导致问题的不恰当配置。 例 1 考虑如下配置: (所有页的大小都为 4K) 服务器:
数据库:
限制: DB2 发出的任何创建大于 shmmax 值(这里是 2GB)的数据库共享内存集的请求都将失败,并返回一个 out of memory type 错误。 计算:
问题: 可能仍然可以激活数据库或者连接到数据库。但是,尝试运行一个应用程序时可能返回如下错误消息:
这是 DB2 经常返回的一个错误。不过,如果不是在 AIX 服务器上,而是在 UNIX 服务器上,那么这就很可能与内存资源问题有关。特别地,可能是内核参数没有进行适当的调优。 为解决这个问题,可以适当地配置内核参数。设置:
例 2 考虑如下配置: (所有页的大小都为 4K) 服务器上的物理 RAM 是 1 GB。 数据库 A:
数据库 B:
限制: 因为共享内存是固定到物理 RAM 的,所以这种内存不会被换出。因此,我们只能分配最多 1GB (可用的物理 RAM)的共享内存给数据库使用。 计算:
问题: 假设数据库 A 是激活的。至少有 898MB 的共享内存固定在物理 RAM 中。当尝试激活数据库 B
时,就会碰到如下错误消息:
如果同时启动数据库 A 和数据库 B,我们将请求至少 1.55GB (898MB + 656MB) 的可用物理 RAM,以便同样地将共享内存固定在 RAM 中。显然,1GB 的 RAM 不够。为解决这一问题:
在这种情况下,按照上面的数据库配置参数,在任何时刻启动某一个数据库(数据库 A 或数据库 B)是可行的,但是不能同时启动两个数据库。这是必须增加更多的物理 RAM。 这个问题在 AIX 中不会出现,因为在 AIX 中共享内存没有固定在物理 RAM 中。在这种场景中,可以启动数据库 B。但是,这意味着数据库 A 的数据库内存必须调出。当一个应用程序访问数据库 A 时,又得将数据库 B 的数据库内存调出。您可以想象未来要发生的调页次数有多少。 例 3 考虑如下配置: (所有页的大小都是 4K) 服务器:
数据库:
计算:
问题: 数据库共享内存是 1.66GB。这个数小于 3.35GB 的数据库共享内存限制。shmmax 参数设置得比较恰当。但是在启动数据库时可能返回下面的错误消息!您可以在 db2diag.log 中看到如下错误消息:
shmat 是一个 UNIX 函数,它将与 shmid(另一个 Solaris 函数)所标识的共享内存相关的共享内存段附加到调用进程的数据段上。shmat 失败于 0x000000C,即 ENOMEM 或可用数据空间不足以容纳共享内存段。 换句话说,不能激活数据库或连接到数据库,因为没有足够的共享内存来满足请求。 那么该怎么办呢?答案在于我们分配 ESTORE 的方式。每个 ESTORE 段必须是一个连续的块。在我们的例子中,我们试图为 ESTORE 分配很大的一块(连续的)内存(1.6GB)。即使有 16GB 的 RAM,它也可能会被分段,从而没有一块连续的 1.6GB 的内存。 为解决这个问题,在数据库配置文件中像下面这样设置 ESTORE 的值:
通过设置上面的 ESTORE 值,实际上我们仍然试图为 ESTORE 分配总共 1.6 GB 的内存。然而,我们将尝试为 ESTORE 分配 10 块 160MB 的内存。这样分配的连续空间就要小得多,因此最有可能解决问题。
在 32-位 HP-UX 平台上,默认 HP-UX 内存管理是基于象限(quadrant)的,其中每个进程都有其自己的空间。每个进程(包括 DB2 代理)可以寻址最多 4GB 的内存。4G 的可寻址内存被拆分成 4 个象限,每个象限大小为 1G,如 图 11所示。 象限 1 (1GB) 被预留给程序文本(可执行代码)。 象限 2 (1GB) 被预留给全局程序数据。 象限 1 和象限 2(减去内核内存和其他进程的内存)可一起用于私有内存。象限 1 和 2 存在了 n 次(每个进程一次) 象限 3 (1GB) 被预留给全局共享内存。 象限 4 (0.75GB) 被预留给全局共享内存。最后 0.25GB 用于 I/O 映射。 系统范围共享内存的限制是 1.75GB。也就是说,所有共享对象(不仅仅是 DB2)都被映射到象限 3 和 4 上由所有进程共享的一个单独的空间内。这个共享内存空间分为象限 3 中的 1GB 和象限 4 中的 0.75GB。但是,共享内存段不能跨象限“拆分”,而应该保证是一个连续的地址空间。根据进程的不同,DB2 分配各种不同的段。每个段只能映射到象限 3 或象限 4 中的某一个象限,但是不能同时映射到这两个象限。因此,DB2 允许分配的最大共享内存段在象限 3 中的大小为 1GB,在象限 4 中的大小为 0.75GB。实际上,很可能不会分配这么大的内存段,因为之前可能已经分配了一些小的内存段(或者是由 DB2 分配的,或者是由其他进程分配的)。这意味着数据库共享内存集实际上被限制在大约 0.75 到 1GB。这比其他 UNIX 平台上可用的任何数据库共享内存集要少得多。 注意: 缺省的 HP-UX 内存架构使用 SHARE_MAGIC 内核可执行程序。随着 HP-UX 10.20 内核的更改,可以通过编译应用程序使之使用 SHMEM_MAGIC 内核可执行程序,可以将全局共享内存的限制从 1.75G 增加到 2.75GB。不过,目前还没有计划使用 SHMEM_MAGIC 可执行程序编译 DB2 代码。 对于 HP-UX 11.0 或更新的版本,有一种方法可以以内存窗(Memory Windows)的形式绕过 1.75GB 的共享内存限制。内存窗允许每个进程从共享内存中定义最多 1GB 的惟一的全局空间。放在这个惟一空间内的共享内存段只能被相同内存窗中的进程访问。但是不同的应用程序或同一应用程序的不同实例可以放在不同的内存窗中, 并消耗系统上更多的可用物理内存。 内存窗使象限 3 能够作为 /etc/services.window 中定义的一个进程组的私有共享内存空间。在内存窗的 DB2 实现中,每个实例都被映射到一个单独的内存窗。换句话说,每个 DB2 实例都可以有自己的象限 3 内存空间。象限 4 仍然用作全局共享内存区,其中任何共享进程都可以分配段。注意,如果象限 4 有可用的空间,那么共享库总是首先被映射到象限 4。于是,每个实例的可用共享内存就变为 1GB 加上象限 4 中仍然可用的空间。不过,共享内存段依然不能跨越象限边界。根据段的类型和大小,DB2 可以指定在象限 4 中创建段。数据库共享内存集仍然有大约 1GB 的限制。因此,内存窗的好处是允许我们为一个 DB2 实例分配整个象限(象限 3,大约 1GB)。然而,如果没有内存窗,则所有实例都必须共享最多 1.75GB 的系统范围的共享«内存。 提示: 如果为每个实例都创建一个数据库,就可以有效地为所有数据库分配最多 1GB 的数据库共享内存。 注意: 内存窗只是为 32 位应用程序扩展了系统范围的虚拟容量。通过扩展虚拟容量,可以使用更多的底层 RAM。在不使用内存窗的情况下,不管 RAM 有多大,最多只能消耗 1.75GB 的物理内存。 要了解有关如何为 DB2 启用内存窗的更多信息,请参阅 。
没有充分配置内核参数或者初始化数据库共享内存失败可能导致以下失败:
最常见的未能正确设置的内核参数是 shmmax。请参考 Quick Beginnings,以了解如何根据物理 RAM 设置适当的值。SHMMAX 按字节指定系统上可以分配的最大共享内存段。如果将 UDB 配置为创建一个大于该值的数据库共享内存集,则请求将遭到失败。其他需要了解的内核参数是 shmmni。 获得关于修改内核参数的信息。 下面的例子展示了会导致问题的不正确的配置。 例 1 考虑如下配置: (所有页的大小都为 4K) 服务器:
数据库:
计算:
问题: 可能仍然可以激活数据库或连接到数据库。但是,当尝试连接到数据库时,在 db2diag.log
中可能间歇地出现如下错误消息。
注意,对数据库共享内存的计算表明,我们仍然没有超出 1GB 可用共享内存的限制。接下来我们看一看内核参数的设置。查看 "Quick Beginnings for DB2 Servers" 以获得对 32 位 HP-UX 平台上调优内核参数的建议。在查看这本书时,实际上您会发现其中没有写到 shmseg 参数。这意味着您应该使用默认设置。shmseg 的默认设置是 120。(由于 SHMSEG 内核参数被设得太低,有些共享内存段不能适当地分配。) 要解决这个问题,需适当地配置内核参数。
例 2 考虑如下配置: (所有页的大小都为 4K) 服务器:
数据库 A:
数据库 B:
限制: 由于物理 RAM 只有 1GB,数据库共享内存集只能映射到它在物理上可以使用的空间,即 1GB + 交换空间。 计算:
问题: 分别激活或连接到数据库 A 或数据库 B 是可行的,但是不能并发进行。尝试同时激活这两个数据库将产生如下错误消息:
如果同时启动数据库 A 和数据库 B,需要至少 1.52GB (616MB + 902MB) 的共享内存。将数据库 A 映射到象限 4 (~0.75GB 可用共享内存)和将数据库 B 映射到象限 3 (~1GB)应该没有问题。但是,这一次,我们会受到物理内存的限制。显然,1GB 的 RAM 不足以处理 1.52GB 的共享内存映射。此外,SWAP 空间也被设置得太低。要解决这一问题:
例 3 考虑如下配置: (所有页的大小都为 4K) 服务器:
数据库 A:
数据库 B:
数据库 C:
限制: 由于物理 RAM 只有 1GB,数据库共享内存集只能映射到它在物理上可以使用的那些空间,即 1GB + 交换空间。 计算:
问题: 启动数据库 A 和数据库 B 成功。但是启动数据库 C 时失败,并返回如下错误:
在这里,物理内存不是问题,因为我们有足够多的 RAM(6GB)。进一步的测试得处了以下启动组合:
组合 (A + B) 和组合 (B + C) 获得成功,因为它们初始化数据库所需的共享内存总量分别是 1.5GB (894 + 608) 和 1.46GB (608+850)。这低于 1.75GB 的共享内存限制,并且每个数据库共享内存段可以安全地连续映射到一个象限。而其他组合将失败于 SQL1478W,因为它们要么超出了 1.75GB 共享内存限制,要么不能在一个象限内为一个数据库分配连续的共享内存段。 要解决这一问题:
图 12中展示了 32 位 Linux/Intel 上的 4GB 可寻址内存。 从 0x08048000 到 0x0FFFFFFF 的段是预留给 db2sysc 可执行程序的。 从 0x10000000 到 0x3FFFFFFF 的段是实例共享内存,总共是 0.75GB。 在默认情况下,DB2 共享库始于 0x40000000。 在低地址装载共享库,而将更多的空间留给数据库共享内存,这是可行的。(这也意味着用于实例共享内存的空间将更少。但是由于实例内存与 数据库内存相比通常比较小,因此这样可以获得好处。)例如,如果在低于 0x40000000 的地址装载共享库,那么就可以在 0x38000000 装载数据库共享内存。如果在低于 0x2a000000 的地址装载共享库,那么就可以在 0x30000000 装载数据库共享内存,这样就有超过 2GB 的空间留给数据库共享内存。这些更改要求重新编译内核,我们在本文中不会对此加以讨论。请参考 Linux 手册中内核重编译那一节,以了解更多信息。 不过,对于 Redhat Advanced Server 和 SuSE SLES 8 上的 v8.1 FP2,DB2 将尝试自动将共享库重新分配到一个较低的地址,这样就不需要重新编译内核。在这些企业发行版中,有一个叫做 /proc/ pid/mapped_base 的文件包含了一个地址,共享库将从该地址装载到内存。在默认情况下,该地址是 0x40000000,但是我们可以把它降低一些(降到 0x20000000)。当 db2sysc 启动时,我们检查 mapped_base 文件是否存在。如果存在,则使用新的值并重新执行。然后,mapped_base 值发生了改变的进程中产生的每个进程都使用这个新值。 数据库共享内存(包括缓冲池)始于 0x50000000 (默认值)。它从 0x50000000 开始朝着堆栈方向向下增长。堆栈包含要执行的指令。堆栈从 0xC0000000 开始向上增长。 使数据库共享内存和堆栈不发生冲突,这一点很重要。我建议为堆栈使用 16MB 这么大的空间。在这样的情况下,我们有 ~1.73GB 用于数据库共享内存(从 0x50000000 到 0xC0000000,减去 16MB 的堆栈)。 在使用 "ulimit -a" 或 "ulimit -s" 显示限制时,这些值是以 1K 字节为单位显示的。
设置这个限制,使堆栈和共享内存地址空间不会相互冲突,这一点很重要。如果它们之间有冲突,那么实例就会崩溃,并发出信号 4 或信号 11。 最后 1GB 的内存 被预留给 Linux 内核。 您可能已经注意到,没有用于代理私有内存的段。这就对了,在 32 位的 Linux/Intel 中,没有预留特定的段给私有内存。代理私有内存是从共享库之间的任意空闲空间中分配的。 为了看一看这里是如何使用内存的,请参阅叫作 /proc/ PID/maps 的文件,其中 PID 是 db2agent 进程的进程 ID。 图 12展示了 maps 文件的示例输出。 从 图 13 中可以观察到下面一些情况:
提示: 阅 读此文,了解如何设置 AWE ,以及 AWE 是干什么的。 需要从这种结构中了解到的最重要的事情是:
没有充分配置内核参数或者初始化数据库共享内存失败可能导致以下失败:
最常见的未能正确设置的内核参数是 shmmax。请参考 Quick Beginnings ,了解如何根据物理 RAM 设置适当的值。 shmmax按字节指定了系统中可以分配的最大共享内存段。如果将 UDB 配置成创建大于这个值的数据库共享内存集,那么该请求将遭到失败。其他要指定的内核参数是 shmmni。请参考 这 个链接,以获得更多关于修改内核参数的信息。 下面的例子展示了会导致问题的不恰当配置。 例 1 服务器:
数据库:
限制: SHMMAX 被设为 32KB。DB2START 可以分配足够多的共享内存来成功地启动实例。 计算:
问题: 在 db2start 期间,将碰到以下错误:
要解决这个问题,可以将 SHMMAX 参数增加到一个更合理的大小,例如 2GB。 例 2 服务器:
问题: 如 SHMMAX 被设为 2GB,即 2147483648,那么一次 db2start 就会使其降至 268435456。如果将这个值减少 1 个字节,那么 db2start 不会作出任何变化。该问题在 v8 FP2, APAR 中 已得到修复。 对 SuSe 8.0 不会发生问题。
在 Windows 上,DB2 UDB 有着根本不同的体系结构。所有 DB2 操作都是以进程(db2sysc.exe)内的线程的形式实现的。Windows 进程模型最多可寻址 4GB 的内存,如 图 14中所示。 在 Windows 中没有真正的共享内存集的概念。在 UNIX 中,所有共享内存池通常都是某个内存集(例如缓冲池,dbheap,等等)的一部分,而在 Windows 中,这些共享内存池都是根据需要从 db2sysc 的私有内存集中分配的。 在非 Advanced Server Windows 环境(NT,2000 Pro/Standard)中,4GB 地址空间被分成 2GB 的用户空间和 2GB 的内核空间。这样可以有效地将 db2sysc 进程可访问的内存总数限制到 2GB。 在 Advanced Server 环境中,将 2GB/2GB 的内存拆分重新配置为 3GB 的用户空间和 1GB 的内核空间是可行的,如 图 14中所示。 为了允许内存使用量超出这些限制,必须在 boot.ini 文件中使用 /3GB switch。这里要警告的是,/3GB switch 只在 Windows 2000 Advanced Server、Windows 2000 DataCenter、Windows2003 Enterprise Edition 和 Windows 2003 DataCenter 中受支持。您可以在 Windows2000 Pro/Standard 中设置这个 switch,但是用户空间最多只能有 2GB,而内核空间缩小到 1GB,这会导致 1GB 的内存丢失。 注意: 为了有效地使用 /3GB switch,必须在 Windows 32 位环境中安装了最少 4GB 的物理 RAM。 为了克服 2GB (或 3GB)的用户空间限制,Windows 2000 Advanced Server 和 Windows 2003 Advanced Server 提供了对更大内存的支持。它们是 Address Windowing Extensions (AWE) 和 Physical Address Extension (PAE)。 AWE 是一种机制,用于通过一个可能更小的窗口访问内存中一个很大的部分。按照这种方法,如果某个大的内存池是线性的,那么它就可以被寻址。实际上,AWE 允许创建超过 2GB 限制的缓冲池。AWE 功能是通过使用 boot.ini 文件中的 /PAE switch 来实现的。/PAE 在 Windows2000 Pro/Standard 中有效,但如果不是使用 Windows 2000 Advanced Server、Windows 2000 DataCenter、Windows2003 Enterprise Edition 和 Windows 2003 DataCenter,则 Microsoft 不会允许你的操作系统与这样的 switch 一起运行。 提示: 这 篇文章很好地描述了如何设置 AWE,以及 AWE 是干什么的。 需要从这种结构中了解到的最重要的事情是:
DB2 的内存结构由 4 个内存集组成:实例共享内存、数据库共享内存、应用程序组共享内存(只有在数据库启用了 intra-parallel,或者启用了集中器,或者多分区的时候才适用)以及代理私有内存。每种内存集由各个内存池组成。例如,缓冲池和数据库堆是数 据库内存集内的两个不同的内存池。 每个实例有一个实例共享内存集,由这个实例中的所有数据库共享。每个数据库有一个数据库共享内存集,由连接到该数据库的所有代理共享。 每个应用程序组有一个应用程序组共享内存集,由属于该应用程序组的所有代理共享。每个代理(或进程)都有一个代理私有内存集。该内存集是由代理独占使用 的。 对于 32 位内存结构,不管系统有多少物理 RAM,在任何平台上任何进程的可寻址内存都是 4GB。而且,这 4GB 中有一部分要预留给操作系统。剩下的所有内存(亦称用户空间)是由应用程序(包括 DB2)使用的。 在配置 DB2 内存的使用时应记住,所有内存池应该能够纳入可寻址用户空间。这个空间的大小随平台的不同而不同。表 1 列出了每种 DB2 内存集的限制。应用程序组共享内存集没有列出,因为它是从数据库共享内存集中分配的。
注意:
对于 32 位内存结构,不管物理 RAM 有多大,实例、数据库配置都受到 4GB 可寻址空间的限制。不过,如果有足够的 RAM,只需每个实例或数据库都符合以上限制,就可以在系统中并发地运行多个实例或数据库。为了克服这一限制,应该考虑转换到 64 位的 DB2。
特别感谢 Philip Cho 和 Michael Cornish 为本文提供了技术审校。
|