【彭建军】IBM DB2 内存分配与使用策略 (上)
出处:根据 ChinaUnix 的相关文章整理。
【导读】本文将向您讲解 DB2 内存使用的基础,以及共享内存和私有内存的概念。这些内容同时适用于 32 位和 64 位的系统。
简介 理解 DB2 如何使用内存,可以防止过度分配内存,并有助于对内存的使用进行调优,从而获得更好的性能。
本文将向您传授 DB2 内存使用的基础,以及共享内存和私有内存的概念。这些内容同时适用于 32 位和 64 位的系统。虽然对于 64 位系统有一些限制,但是在未来的一段时间内还不大可能触及这些限制。因此,我们将焦点放在影响 32 位系统的内存限制,并对之进行详细的讨论。
我们首先讨论一般情况下 DB2 如何使用内存,接着讨论内存
管理如何随着平台(AIX、Sun、HP、Linux 和 Windows)的不同而变化,以及它们对 DB2 的影响。最后,我们将给出一些实际生活中客户处境/问题以及他们的解决方案的有意义的例子。本文的内容适用于 DB2 version 8。
DB2 内存结构概述
图 1中说明了 DB2 内存结构。这种内存结构在所有平台上都是一致的。 注意:在多分区环境中,下面的图适用于多分区实例中的每个分区。
图 1 - DB2 内存结构
DB2 在 4 种不同的内存集(memory set)内拆分和管理内存。这 4 种内存集分别是:
实例共享内存(instance shared memory)
数据库共享内存(database shared memory)
应用程序组共享内存(application group shared memory)
代理私有内存(agent private memory)
每种内存集由各种不同的内存池(亦称堆)组成。图 1 也给出了各内存池的名称。例如, locklist是属于数据库共享内存集的一个内存池。 sortheap是属于代理私有内存集的一个内存池。
我们将详细讨论每一种内存集。
实例共享内存
每个 DB2 实例都有一个实例共享内存。实例共享内存是在数据库管理器启动(db2start)时分配的,并随着数据库管理器的停止(db2stop)而释放。这种内存集用于实例级的任务,例如监控、审计和节点间通信。下面的数据库管理器配置(dbm cfg)参数控制着对实例共享内存以及其中个别内存池的限制:
实例内存( instance_memory)。
监视器堆( mon_heap_sz):用于监控。
Audit Buffer( audit_buf_sz):用于 db2audit 实用程序。
Fast Communication buffers ( fcm_num_buffers):用于分区之间的节点间通信。仅适用于分区的实例。
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,则可以使用下面的命令来确定它的值:
db2 attach to instance_name(其中 instance_name是实例的名称)
db2 get dbm cfg show detail
下面的输出表明有 42 MB 的内存被预留给实例共享内存集(10313 页 * 4096 字节/页):
Size of instance shared memory (4KB) (INSTANCE_MEMORY) = AUTOMATIC(10313)
instance_memory 参数只是设置了实例共享内存的限制。它并没有说出当前使用了多少内存。要查明一个实例的内存使用情况,可以使用 DB2 内存跟踪器工具 db2mtrk。例如,
db2start
db2mtrk -i -v
Memory for instance
FCMBP Heap is of size 17432576 bytes
Database Monitor Heap is of size 180224 bytes
Other Memory is of size 3686400 bytes
Total: 21299200 bytes
上面的例子表明,虽然预留给实例共享内存集的内存有 42 MB,但在 db2mtrk运行时只用到了大约 21 MB。
注意:在某些情况下,db2mtrk 显示的大小会大于指定给配置参数的值。在这种情况下,赋予配置参数的值被作为一种软限制,内存池实际使用的内存可能会增长,从而超出配置的大小。
数据库共享内存
每个数据库有一个数据库共享内存集。数据库共享内存是在数据库被激活或者第一次被连接上的时候分配的。该内存集将在数据库处于非激活状态时释放(如果数据库先前是处于激活状态)或者最后一个连接被断开的时候释放。这种内存用于数据库级的任务,例如备份/恢复、锁定和 SQL 的执行。
图2展示了数据库共享内存集内的各种内存池。括号中显示了控制这些内存池大小的配置参数。
图 2 - DB2 数据库共享内存
完整的绿色方框意味着,在数据库启动的时候,该内存池是完全分配的,否则,就只分配部分的内存。例如,当一个数据库第一次启动时,不管 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% 的开销
提示: 为了发现分配给主缓冲池的内存有多少,可以发出:
SELECT * FROM SYSCAT.BUFFERPOOLS
虽然大多数内存池的大小是由它们的配置参数预先确定的,但下面两种内存池的大小在默认情况下却是动态的:
包缓存: pckcachesz = maxappls * 8
编目缓存: catalogcache_sz = maxappls * 4
活动应用程序的最大数量: maxappls = AUTOMATIC
将 maxappls设为 AUTOMATIC的效果是,允许任意数量的连接数据库的应用程序。DB2 将动态地分配所需资源,以支持新的应用程序。因此,包缓存和编目的大小可以随着 maxappls的值而变化。
除了上述参数以外,还有一个参数也会影响数据库共享内存的数量。这个参数就是 database_memory。该参数的缺省值是 AUTOMATIC。这意味着 DB2 将根据以上列出的各内存池的大小来计算当前配置所需的数据库内存量。此外,DB2 还将为溢出缓冲区分配一些额外的内存。每当某个堆超出了其配置的大小时,便可以使用溢出缓冲区来满足实例共享内存区内任何堆的峰值需求。
如果 database_memory 被设为某个数字,则采用 database_memory 与各内存池之和这两者之间的较大者。
如果 database_memory 被设为 AUTOMATIC,则可以使用以下命令来显示它的值:
db2 connect to dbnameuser useridusing pwd
db2 get db cfg for dbnameshow detail
使用 db2mtrk 工具显示当前使用的内存量: db2mtrk -i -d -v (在 Windows 中,-i 必须指定。在 UNIX 中,-i 是可选的。)
Memory for database: SAMPLE
Backup/Restore/Util Heap is of size 16384 bytes
Package Cache is of size 81920 bytes
Catalog Cache Heap is of size 65536 bytes
Buffer Pool Heap is of size 4341760 bytes
Buffer Pool Heap is of size 655360 bytes
Buffer Pool Heap is of size 393216 bytes
Buffer Pool Heap is of size 262144 bytes
Buffer Pool Heap is of size 196608 bytes
Lock Manager Heap is of size 491520 bytes
Database Heap is of size 3637248 bytes
Other Memory is of size 16384 bytes
Application Control Heap is of size 327680 bytes
Application Group Shared Heap is of size 57344000 bytes
Total: 67829760 bytes
应用程序组共享内存
这种共享内存集仅适用于以下环境。(对于其他环境,这种内存集不存在。)
多分区(multi-partitioned)数据库。
启用了内部并行(intra-parallel)处理的未分区(non-partitioned)数据库。
支持连接集中器的数据库。
注意:当 max_connections的值大于 max_coordagents的值时,连接集中器便被启用。这两个参数可以在数据库管理器配置中找到。(使用 GET DBM CFG 显示数据库管理器配置。)
在以上环境中,应用程序通常需要不止一个的代理来执行其任务。允许这些代理之间能够彼此通信(相互发送/接收数据)很有必要。为了实现这一点,我们将这些代理放入到一个称作 应用程序组的组中。属于相同应用程序组的所有 DB2 代理都使用 应用程序组共享内存进行通信。
应用程序组内存集是从数据库共享内存集中分配的。其大小由 appgroup_mem_sz 数据库配置参数决定。
多个应用程序可以指派给同一个应用程序组。一个应用程序组内可以容纳的应用程序数可以这样计算: appgroup_mem_sz / app_ctl_heap_sz
在应用程序组内,每个应用程序都有其自己的 应用程序控制堆。此外,应用程序组共享内存中有一部分要预留给应用程序组共享堆。如下图所示:
图 3 - DB2 应用程序组共享内存
例 1 考虑以下数据库配置:
最大应用程序内存集大小 (4KB) (APPGROUP_MEM_SZ) = 40000
最大应用程序控制堆大小 (4KB) (APP_CTL_HEAP_SZ) = 512
用于应用程序组堆的内存所占百分比 (GROUPHEAP_RATIO) = 70
可以计算出下面的值:
应用程序组共享内存集是: 40000 页 * 4K/页 = 160 MB
应用程序组共享堆的大小是: 40000 * 70% = 28000 4K 页 = 114MB
该应用程序组内可容纳的应用程序数为: 40000/512 = 78
用于每个应用程序的应用程序控制堆为: (100-70)% * 512 = 153 4K 页 = 0.6MB
不要被 app_ctrl_heap_sz 参数迷惑。这个参数不是一个应用程序组内用于每个应用程序的各应用程序控制堆的大小。它只是在计算这个应用程序组内可容纳多少应用程序时用到的一个值。每个应用程序的实际应用程序控制堆大小都是通过 图 3中给出的公式计算的,这个公式就是 ((100 - groupheap_ratio)% * app_ctrl_heap_sz)。
因此,groupheap_ratio 越高,应用程序组共享堆就越大,从而用于每个应用程序的应用程序控制堆就越小。
例 2 假设在一天中最忙的时间里,有 200 个应用程序连接到例 1 中所描述的数据库上。由于每个应用程序组可以容纳 78 个应用程序,因此我们需要 200/78 = 3 个应用程序组来容纳总共 200 个应用程序。这里应确保系统有足够多的 RAM 来支持这一配置。否则就会发生 SQL10003N 错误。
代理私有内存
每个 DB2 代理进程都需要获得内存,以执行其任务。代理进程将代表应用程序使用内存来优化、构建和执行访问计划,执行排序,记录游标信息(例如位置和状态),收集统计信息,等等。为响应并行环境中的一个连接请求或一个新的 SQL 请求,要为一个 DB2 代理分配代理私有内存。
代理的数量受下面两者中的较低者限制:
所有活动数据库的数据库配置参数 maxappls 的总和,这指定了允许的活动应用程序的最大数量。
数据库管理器配置参数 maxagents 的值,这指定了允许的最大代理数。
代理私有内存集由以下内存池组成。这些内存池的大小由括号中的数据库配置参数指定:
Application Heap ( applheapsz)
Sort Heap ( sortheap)
Statement Heap ( stmtheap)
Statistics Heap ( stat_heap_sz)
Query Heap ( query_heap_sz)
Java Interpreter Heap ( java_heap_sz)
Agent Stack Size ( agent_stack_sz) (仅适用于 Windows)
我们曾提到,私有内存是在一个 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 中,因为对它们的访问更频繁。如果代理闲置了较长的一段时间,则其代理私有内存将被调出。
图 4 - 并发运行的两个 DB2 实例
共享内存与私有内存
至此,我们已经讨论了实例共享内存、数据库共享内存和应用程序组共享内存以及代理私有内存。但是共享内存和私有内存的意义是什么呢?
为了理解共享内存与私有内存之间的不同之处,首先让我们通过快速阅读 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 内存集。假设:
数据库 A 属于实例 db2inst1。
数据库 A 为应用程序组 1 启用了 intra-parallel。
代理 x 和 代理 y 都属于应用程序组 1。
图 5 - DB2 代理进程内存地址空间
图 5 展示了在 RAM 中分配的以下内存集:
用于实例 db2inst1 的实例共享内存集。
用于 数据库 A 的数据库共享内存集。
用于 应用程序组 1 的应用程序组共享内存。
用于代理 x 的代理私有内存集。
用于代理 y 的代理私有内存集。
为内核和库之类的东西预留的内存。
代理 x 和代理 y 共享相同的实例内存、数据库内存和应用程序组内存,因为它们属于相同的实例、相同的数据库和相同的应用程序组。此外,它们有其自己的代理私有内存。
每个 DB2 代理进程都有其自己的内存地址空间。在内存空间中的内存地址允许代理访问物理 RAM 中的内存。我们可以把这些地址看作指向 RAM 的指针,如 图 5所示。对于任何 DB2 进程,这个地址空间必须能够容纳上述所有 4 种内存集。
前面已提到,ESTORE 是用于扩展缓冲池的大小。那么您可能要问,为什么不创建一个更大的缓冲池呢。答案是:因为地址空间受到限制,地址空间可能容不下一个更大的缓冲池!在此情况下,需要定义一个较小的地址空间能够容纳的缓冲池。如果有过量的物理内存,那么可以用该内存来配置 ESTORE。
那么,我们怎么知道一个 DB2 代理的地址空间是多大呢?地址空间的大小取决于当前的实例是 32 位的实例还是 64 位的实例。我们将在下一节对此进行解释。
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 的可寻址空间?
应该如何配置 图 1中列出的每个参数,以最有效地利用可用的内存?
虽然 4GB 的地址空间限制适用于所有平台,但是对于上述问题的回答却与平台有关。例如,在 AIX 系统上可以分配给 DB2 数据库的最大数据库内存数就与 Solaris 系统上的数据库不同。接下来的几节将讨论不同的平台对 DB2 中的内存配置有何影响。注意:本文的后续部分只针对 32 位内存体系结构。我们即将讨论的问题不适用于 64 位的体系结构。
32-位 Sun Solaris Version 2.6 及及其更高版本中的 DB2 内存配置
在 AIX 中,每个段的大小都是 256MB,而在 Solaris 中则有所不同,其内存段的大小不是固定的。32 位 Solaris 可寻址内存结构如 图 9所示。
图 9 - 32 位 Sun SolarisDB2 中的 32 位内存地址空间
从 0x0 到 0x00010000 之间的地址不能使用。第一个段始于 0x00010000,这个段被预留给 db2sysc 可执行程序和代理私有内存。在缺省情况下,这个段结束于 0x10000000,因而这个段总共是 256MB。(我们可以通过设置 DB2DBMSADDR DB2 注册表变量使这个段更大一些,见后面)。在这个段内,db2sysc 可执行程序只占用很小的一片内存,剩下的用于代理私有内存(200MB+)。
在默认情况下,实例共享内存的起始地址固定于 0x10000000。不过,也可以将其改为一个更高的地址,以便有更多的空间留给代理私有内存。例如,如果实例共享内存始于 0x12000000,而不是 0x10000000,那么就有 0x12000000 减去 0x10000000 即 32MB 的额外内存被用于代理私有内存。要做到这一点,可以将 DB2DBMSADDR DB2 注册表变量设为如下值:
db2set DB2DBMSADDR=0x12000000
注意: DB2DBMSADDR 值的范围是从 0x10000000 到 0x10FFFFFF,每次递增 0x10000。
实例共享内存的结束地址是不固定的。这意味着实例共享内存可能会很大。紧接着实例共享内存的是数据库共享内存。因为实例共享内存的结束地址不固定,所以数据库共享内存的起始地址也是不固定的。
在数据库共享内存之后的是用于 DB2 跟踪、共享库和堆栈(即将执行的指令)的内存。在 AIX 中,堆栈和代理私有内存共享相同的内存段,而在 Solaris 中则有所不同,其中代理私有内存和堆栈分别使用不同的内存段。因此,这里不存在两者之间发生冲突的风险。
由于这些内存段的大小不是固定的,我们只能估计实例共享内存和数据库共享内存的大小为:
4GB - 代理私有内存 - DB2 跟踪 - DB2 共享库 - 堆栈
注意这个数量同时还包括实例共享内存和数据库共享内存,并且它们的内存段必须是邻接的。为了看一看实际中如何使用内存,可以对 db2sysc 或 db2agent 进程的 ID 运行 /usr/proc/bin/pmap 命令(以 root 的身份)。
注意:在 Solaris 中,当使用 "pe -ef | grep instance_name" 命令显示 DB2 进程时,所有进程都作为 db2sysc 进程显示。可以使用 db2ptree命令来以“真实”名称显示 DB2 进程。 图 9展示了 pmap 命令的一个示例输出。
图 10 - pmap 命令对于 db2sysc 进程的示例输出(/usr/proc/bin/pmap -x 15444)
从 图 10 中可以观察到下面一些情况:
从 0x00010000 到 0x023F8000 这一部分被预留给 db2sysc 可执行程序(~36MB)。
橙色的那部分(堆),即从 0x023F8000 到 0x10000000,被用于代理私有内存(~220MB)。
实例共享内存,即绿色那部分,始于默认地址 0x10000000。这意味着没有设置 DB2DBMSADDR。
所有 3 个绿色的段被用于共享内存,包括实例共享内存和数据库共享内存。pmap 输出并没有说出哪一个段是实例共享内存,哪一个段是数据库共享内存。我们只知道,数据库共享内存在 0xFE002000 结束,因为从这个地址开始的是一个用于匿名内存的段。因此,实例共享内存和数据库共享内存总共的大小是 0xFE002000 - 0x10000000 = 3,992,985,600 字节 = ~3.7 GB。
从 0xFFBC0000 到 0xFFFFFFFF 是用于堆栈的内存段(~4MB)。
注意:在 32 位 Solaris 中,我们通常将 DB2 数据库共享内存限制在大约 3.5 GB。
如果设置了 DB2DBMSADDR 注册表变量,那么实例共享内存将从该变量指定的地址开始。下面的例子展示了这一点是如何实现的。
例子 设置 DB2DBMSADDR 注册表变量:
db2set DB2DBMSADDR = 0x12000000
db2stop
db2start (需要重新启动实例,以使更改生效)。
获得 db2sys 进程的进程 ID
ps -ef | grep sylviaq ('sylviaq' 是实例名)。
-ef | grep sylviaq
sylviaq 13166 1 0 13:09:12 pts/2 0:00 /export/home/sylviaq/sqllib/bin/db2bp 13049C11221 5
sylviaq 13263 13256 0 13:11:02 ? 0:00 db2sysc
sylviaq 13265 13256 0 13:11:03 ? 0:00 db2sysc
sylviaq 13257 13254 0 13:10:59 pts/3 0:00 -ksh
sylviaq 13256 13253 0 13:10:59 ? 0:00 db2sysc
sylviaq 13262 13256 0 13:11:00 ? 0:00 db2sysc
sylviaq 13360 13049 0 13:11:41 pts/2 0:00 grep sylviaq
sylviaq 13264 13256 0 13:11:02 ? 0:00 db2sysc
sylviaq 13266 13261 0 13:11:03 ? 0:00 db2sysc
以 root 的身份 cd 到 /usr/proc/pmap,并对任何 db2sysc 进程运行 pmap:
./pmap -x 13263
pmap 输出:
13263: db2sysc
Address Kbytes Resident Shared Private Permissions Mapped File
00010000 35808 4064 1608 2456 read/exec db2sysc
02316000 896 168 48 120 read/write/exec db2sysc
023F6000 744 264 8 256 read/write/exec [ heap ]
12000000 243472 243472 - 243472 read/write/exec/shared [shmid=0xbc3]
21000000 22512 22512 - 22512 read/write/exec/shared [shmid=0xbc4]
FCC00000 8328 8328 - 8328 read/write/exec/shared [shmid=0xa96]
FE002000 8 - - - read/write/exec [ anon ]
注意,实例共享内存现在从 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。
需要从这种结构中了解到的最重要的事情是:
与 AIX 不同,Solaris 的内存段其大小不是固定的。我们可以通过设置 DB2DBMSADDR DB2 注册表变量,将实例共享内存移到更高的地址,从而增加代理私有内存。
数据库共享内存的限制大约是 3.5GB。
功能内存与 RAM 固定,因而不能交换出去。
32 位 Sun Solaris 中与分配数据库共享内存有关的常见问题
没有充分配置内核参数以及初始化数据库共享内存失败可能导致如下失败:
在数据库启动时(激活数据库或第一次连接到数据库) - SQL1478W, SQL1084C, hang condition
在运行时 - SQL2043N, 挂起条件
在 Solaris 系统中,DB2 提供了一个叫做 db2osconf的工具。该工具根据系统的大小对内核参数的值给出建议。对于一个给定的系统,建议的值要足够高,以便能够容纳最合理的工作负载。
最常见的未能正确设置的内核参数是 shmmax。该参数按字节指定系统中可以分配的共享内存段的最大大小。如果把 DB2 配置成创建大于这个值的数据库共享内存,那么请求就会失败。其他要知道的内核参数是 shmseg和 shmmni。
Solaris 中另一个与分配数据库共享内存有关的常见问题是由于这样的一个事实导致的,即共享内存段的所有页都是固定在物理 RAM 中的。如果在 RAM 中没有足够的空闲页可用,或者没有足够的可以被 OS 为满足数据库段而调出的其他页,那么启动数据库的请求就会遭到失败。
下面的例子展示了会导致问题的不恰当配置。
例 1 考虑如下配置: (所有页的大小都为 4K)
服务器:
服务器上的物理 RAM 16GB
shmsys:shminfo_shmmax = 2097152 (2GB)
数据库:
IBMDEFAULTBP 400,000 页
UTILHEAP 17,500 页
DBHEAP 30,000 页
LOCKLIST 1000 页
PCKCACHE 5000 页
CATALOGCACHE 2500 页
限制: DB2 发出的任何创建大于 shmmax 值(这里是 2GB)的数据库共享内存集的请求都将失败,并返回一个 out of memory type 错误。
计算:
数据库共享内存 = (456,000 页 x 4KB/页) x 1.1 = ~2.0GB
问题: 可能仍然可以激活数据库或者连接到数据库。但是,尝试运行一个应用程序时可能返回如下错误消息:
SQL1224N A database agent could not be started to service request, or was terminated as a result of a database system shutdown or a force command. SQLSTATE=55032
这是 DB2 经常返回的一个错误。不过,如果不是在 AIX 服务器上,而是在 UNIX 服务器上,那么这就很可能与内存资源问题有关。特别地,可能是内核参数没有进行适当的调优。
为解决这个问题,可以适当地配置内核参数。设置:
shmsys:shminfo_shmmax = 15099494 (~90% of 16GB)
例 2 考虑如下配置: (所有页的大小都为 4K)
服务器上的物理 RAM 是 1 GB。
数据库 A:
IBMDEFAULTBP 137,500 页
INTRA_PARALLEL ON
UTILHEAP 10,000 页
DBHEAP 10,000 页
SHEAPTHRES_SHR 20,000 页
LOCKLIST 1000 页
PCKCACHE 5000 页
CATALOGCACHE 2500 页
APPGROUP_MEM_SZ 20,000 页
数据库 B:
IBMDEFAULTBP 92,500 页
INTRA_PARALLEL ON
UTILHEAP 5,000 页
DBHEAP 10,000 页
SHEAPTHRES_SHR 15,000 页
LOCKLIST 1000 页
PCKCACHE 5000 页
CATALOGCACHE 2500 页
APPGROUP_MEM_SZ 20,000 页
限制: 因为共享内存是固定到物理 RAM 的,所以这种内存不会被换出。因此,我们只能分配最多 1GB (可用的物理 RAM)的共享内存给数据库使用。
计算:
数据库 A 共享内存 = (186,000 页 x 4KB/页) x 1.1% = ~818MB
数据库 A 应用程序组内存 = 20,000 页 x 4KB/页 = 80MB
数据库 B 共享内存 = (131,000 页 x 4KB/页) x 1.1% = ~576MB
数据库 B 应用程序组内存 = 20,000 页 x 4KB/页 = 80MB
为了启动数据库 A,要求: 818MB + 80MB = ~898MB
为了启动数据库 B,要求: 576MB + 80MB = ~656MB
问题: 假设数据库 A 是激活的。至少有 898MB 的共享内存固定在物理 RAM 中。当尝试激活数据库 B 时,就会碰到如下错误消息:
SQL1084C Shared memory segments cannot be allocated. SQLSTATE=57019
如果同时启动数据库 A 和数据库 B,我们将请求至少 1.55GB (898MB + 656MB) 的可用物理 RAM,以便同样地将共享内存固定在 RAM 中。显然,1GB 的 RAM 不够。为解决这一问题:
尝试减少这两个数据库的缓冲池大小。或者
尝试减少应用程序组内存。或者
更可能的是,增加更多的物理 RAM。在这种情况下,您将需要至少 1.55GB 的物理 RAM,才能同时启动这两个数据库。
在这种情况下,按照上面的数据库配置参数,在任何时刻启动某一个数据库(数据库 A 或数据库 B)是可行的,但是不能同时启动两个数据库。这是必须增加更多的物理 RAM。
这个问题在 AIX 中不会出现,因为在 AIX 中共享内存没有固定在物理 RAM 中。在这种场景中,可以启动数据库 B。但是,这意味着数据库 A 的数据库内存必须调出。当一个应用程序访问数据库 A 时,又得将数据库 B 的数据库内存调出。您可以想象未来要发生的调页次数有多少。
例 3 考虑如下配置: (所有页的大小都是 4K)
服务器:
服务器上的物理 RAM 16GB
shmsys:shminfo_shmmax = 15099494 (16GB 的 ~90%)
数据库:
IBMDEFAULTBP 350,000 页
UTILHEAP 17,500 页
DBHEAP 10,000 页
LOCKLIST 1000 页
PCKCACHE 5000 页
CATALOGCACHE 2500 页
ESTORE_SEG_SZ = 400000 页
NUM_ESTORE_SEGS = 1
计算:
数据库共享内存 = (386,000 页 x 4KB/页 + 400,000 页的 estore * 100 字节) x 1.1 = ~1.66GB
ESTORE memory = 400000 x 4KB = 1.6GB
问题: 数据库共享内存是 1.66GB。这个数小于 3.35GB 的数据库共享内存限制。shmmax 参数设置得比较恰当。但是在启动数据库时可能返回下面的错误消息!您可以在 db2diag.log 中看到如下错误消息:
2003-12-04-10.10.13.362027 Instance:db2inst1 Node:000
PID:18327(db2agent (SAMPLE) 0) TID:1 Appid:*LOCAL.sample.047844091013
oper system services sqloVLMAttachVLMSegment Probe:20 Database:SAMPLE
sqloVLMAttachVLMSegment - shmat failed
0xFFBE833C : 0x0000000C
shmat 是一个 UNIX 函数,它将与 shmid(另一个 Solaris 函数)所标识的共享内存相关的共享内存段附加到调用进程的数据段上。shmat 失败于 0x000000C,即 ENOMEM 或可用数据空间不足以容纳共享内存段。
换句话说,不能激活数据库或连接到数据库,因为没有足够的共享内存来满足请求。
那么该怎么办呢?答案在于我们分配 ESTORE 的方式。每个 ESTORE 段必须是一个连续的块。在我们的例子中,我们试图为 ESTORE 分配很大的一块(连续的)内存(1.6GB)。即使有 16GB 的 RAM,它也可能会被分段,从而没有一块连续的 1.6GB 的内存。
为解决这个问题,在数据库配置文件中像下面这样设置 ESTORE 的值:
ESTORE_SEG_SZ = 40000 页
NUM_ESTORE_SEGS = 10
通过设置上面的 ESTORE 值,实际上我们仍然试图为 ESTORE 分配总共 1.6 GB 的内存。然而,我们将尝试为 ESTORE 分配 10 块 160MB 的内存。这样分配的连续空间就要小得多,因此最有可能解决问题。
全文完。