db2 内存研究
是不是数据库管理器的共享内存就是DB2能够使用的最大内容呢,然后数据库全局内存从管理器内存那里获得分配的内存,然后应用程序全局内存又从数据库全局内存那里获得分配内存的,也就是说他们是一层一层的包含关系.还是我理解有问题,是并列关系的呢?
PS 现在DB2好的教材比较少,找了几本书上写的都不是很清楚,谁要要好的资料共享下,先谢了
【导读】本文将向您讲解 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) 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))。这时,应该连接到数据库,并减少主缓冲池的大小。
如果没有索引满足所取的行的要求顺序,或者优化器断定排序的代价低于索引扫描,那么就需要进行排序。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 应用程序组共享内存
最大应用程序内存集大小 (4KB) (APPGROUP_MEM_SZ) = 40000
最大应用程序控制堆大小 (4KB) (APP_CTL_HEAP_SZ) = 512
用于应用程序组堆的内存所占百分比 (GROUPHEAP_RATIO) = 70
可以计算出下面的值:
因此,groupheap_ratio 越高,应用程序组共享堆就越大,从而用于每个应用程序的应用程序控制堆就越小。
代理私有内存
代理的数量受下面两者中的较低者限制:
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指定的最大值。当传入一个新的请求时,就要调用这些闲置代理来服务该新请求。这样就减少了创建和销毁代理的开销。
图
1展示了一个 DB2 实例的 DB2 内存结构。 图 4将展示在同一个系统上有两个实例并发运行的情况。虚拟内存包括物理 RAM
和调页空间(paging space)。共享内存“倾向于”留在 RAM
中,因为对它们的访问更频繁。如果代理闲置了较长的一段时间,则其代理私有内存将被调出。
共享内存与私有内存
为了理解共享内存与私有内存之间的不同之处,首先让我们通过快速阅读
DB2 进程 model来了解一下 DB2 代理进程。在 DB2 中,所有数据库请求都是由 DB2
代理或子代理来服务的。例如,当一个应用程序连接到一个数据库时,就有一个 DB2 代理指派给它。当该应用程序发出任何数据库请求(例如一个
SQL 查询)时,该代理就会出来执行完成这个查询所需的所有任务 —— 它代表该应用程序工作。(如果数据库是分区的,或者启用了
intra-parallel,那么可以分配不止一个的代理来代表应用程序工作。这些代理叫做 子代理。)
除了私有内存(代理在其中使用
排序堆执行“私有”任务,例如私有排序)外,代理还需要数据库级的资源,例如缓冲池、
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 位 AIX 中的 DB2 内存配置
在
32 位的 AIX 上,4GB 的可寻址内存空间被拆分为 16 个段,每段 256MB。 图 6展示了用于一个 DB2 代理进程的 32位
内存地址空间。(假设 DB2_MMAP_READ 和 DB2_MMA_WRITE 这两个 DB2 注册表变量都被设为
NO。如果这两个变量没有设为 NO,则表示方法会有点不同。我们将在后面解释。)
图 6 - AIX 中的 DB2 32 位内存地址空间
段
0 - 预留给 AIX 内核。
段 1 - 预留给 db2sysc 进程。
段 2 - 预留给代理私有内存。
段 3 - 预留给实例共享内存。
段 4 到段 B - 数据库共享内存始于段 4,这些段必须紧挨在一起。所有这 8 个段(2GB)可能都被用于数据库共享内存。但是,下面的每种配置都会从数据库共享内存中拿出一个段(256MB)。
注意: 对于下面的每种配置,DB2 将从数据库共享内存中拿出一个段,这个段始于段 B。
如果数据库是分区的,或者启用了 intra-parallel 或连接集中器,那么数据库共享内存中有一个段被预留给应用程序组共享内存。
Fast
Communication Manager (FCM):FCM 用于系统物理节点上不同分区之间的通信。默认情况下,这种通信是通过 UNIX
socket 进行的。如果 DB2_FORCE_FCM_BP 被设为 YES,那么 FCM
通信发生在共享内存内。这意味着数据库共享内存中有一个段被预留给 FCM 通信。虽然 FCM
通信变得更快,但是它也令数据库共享内存减少了一个段。
fenced UDF 和存储过程:如果数据库上运行着一个 fenced 函数或过程,那么数据库共享内存中有一个段要预留给 fenced 模式的通信。
如果数据库允许任何本地连接,那么数据库共享内存中有一个段要预留给代理/本地应用程序通信。如果将所有本地连接配置为
loopback 连接,那么就可以为这些连接使用
TCP/IP,而不需要共享内存(即使数据库就在服务器本地)。这样就有效地为数据库共享内存空出一个段来。 然而,如果您不想使用 loopback
解决方案,还有一种方法可以迫使
DB2 选择段 E 来用于代理/本地应用程序通信,这样数据库共享内存就不受影响(即不会减少)。请参阅后面的解释。
如果启用了
ESTORE,那么还要从数据库共享内存中拿出另一个段。因此,如果启用 ESTORE,则应确保它至少是
256MB,否则就不起作用,因为要从数据库共享内存中拿出一个 256 MB 的段来仅用于管理这个 ESTORE。建议将 estore 段的大小(
estore_seg_sz)设为 256MB,然后根据可用的内存更改段的数目(
num_estore_segs)。
段 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。
需要从这种结构中了解到的最重要的事情是:
对于禁用了 intra-parallel 的单分区系统,我们可以得到至多 2GB 的空间用于数据库共享内存(段 4 到段 B)。
下面每种配置都将数据库共享内存减少了一个段(256MB):带
fenced UDF 或存储过程的数据库、带本地连接的数据库、DB2_FORCE_FCM_BP=YES 情况下的数据库、支持
intra_parallel、或支持集中器以及分区的数据库,以及启用了 ESTORE 的数据库。
如果允许与数据库进行本地连接,那么应该将 DB2_MMAP_READ 和 DB2_MMAP_WRITE 都设置成 NO,以便使用段 E。否则,任何本地连接都要从数据库共享内存中拿走一个段。
这些限制规定了我们该如何配置数据库共享内存集中的每个内存池。可以使用前面给出的公式来计算数据库共享内存。得到的总和不能超过这个限制。
注意:内存可以分配,释放,也可以当数据库正在运行时在不同区域之间交换。例如,您可以减少 locklist 而增加相同数量的任何一个给定的缓冲池。
使用 svmon 监控 AIX 上的内存使用情况
在
AIX 上,除了 db2mtrk 工具外,还可以使用 svmon 工具来监控 DB2 代理进程的内存消耗情况(需要 root
权限)。这个命令是: "svmon -P PID",其中 PID是 DB2 代理(db2agent 或 db2agentp)的进程 ID。
图 7 展示了 svmon 对于一个名为 db2agent 的进程的示例输出。与这个 db2agent 进程相关的数据库有如下特征:
数据库名是
TEST。
INTRAP_PARALLEL = YES (get dbm cfg)
DB2_FORCE_FCM_BP = YES (db2set -all)
DB2_MMAP_READ=NO, DB2_MMAP_WRITE=NO (db2set -all)
图 7 - svmon 对于一个 DB2 代理进程 (svmon -P 11649046) 的输出
从 图 7中观察到的一些情况:
进程 ID 是 11649046,进程名为 db2agent(左上角)。这个代理被连接到数据库 TEST(数据库的名称在 svmon 输出中已经被截断,只显示了 ‘TES‘)。
Esid 列显示已经分配的内存段:
段 4 (绿色)被分配给数据库共享内存。
段 2 (橙色)被分配给代理私有内存。
段 3 (蓝色)被分配给实例共享内存。
段 B (紫色)被分配给应用程序组共享内存,因为数据库支持 intra_parallel。
段 A (粉红色)被分配给 FCM,因为 DB2_FORCE_FCM_BP=YES。
本地连接段被移到段 E(红色)。这是因为 DB2_MMAP_READ 和 DB2_MMAP_WRITE 都被设为 NO。否则,就必须将段 8 用于代理和本地连接通信,因为段 A 和段 B 都已经被占用,而段 9 被预留给 fenced 模式的通信。
观察到的这些情况与 图 6中说明的相匹配。
设置 32 位 AIX 系统上数据和堆栈的 ulimit
我们早先说过, 图 6中的段 #2 是用于代理私有内存的。实际上,并非完全如此。
确切地说,段
#2 被预留给数据和堆栈。数据包含用户数据(即代理私有内存)。而堆栈则包含要执行的指令。在这个 256M 的段中,数据是从地址
0x20000000 向下增长的。而堆栈是从地址 0x2FFFFFFF
向上增长的。为了不让数据和堆栈有冲突,设置它们的限制就十分重要。如果数据和堆栈真的有冲突,那么实例就会崩溃,并产生信号 4
或信号 11。
图 8 - 数据段和堆栈段
数据和堆栈的限制是在
ulimits(/etc/security/limits)中设置的。使用 SMIT 将它们设置成如下值:
Data = 491519, Stack = 32767 (512 字节)
上述值以 512 字节为单位,其中对于数据是 240MB,对于堆栈是 16MB。当使用 "ulimit -a" 显示这两个限制时,显示的值以 1K 字节为单位,而不是以 512 字节为单位。因此这两个值变为:
Data = 245760, Stack = 16384
注意: /etc/security/limits 包含了以 512 字节为单位的限制,而不是以 1 KB 为单位的限制。
32 位 AIX 上与分配数据库共享内存有关的常见问题
初始化数据库共享内存失败可能产生如下问题:
在数据库启动时(激活数据库或第一次连接数据库) - SQL1478W, SQL0987C, SQL1084C。
在运行时 - SQL10003N, SQL1042C。
下面的例子展示了会导致问题的不恰当配置。
例 1 考虑以下配置:(所有页的大小为 4KB)
单分区,禁用集中器,INTRA_PARALLEL OFF,DB2_MMAP_READ=NO,DB2_MMAP_WRITE=NO,无 fenced 函数或过程
IBMDEFAULTBP 450,000 页
UTILHEAP 17,500 页
DBHEAP 10,000 页
LOCKLIST 1000 页
PCKCACHE 5000 页
CATALOGCACHE 2500 页
限制:在此配置中,对于数据库共享内存的限制是 2GB 或 8 个段。
计算:使用公式计算数据库共享内存,我们得到:
数据库共享内存 = (486,000 页 x 4KB/页的总数) x 1.1 (考虑到 10% 的开销) = ~2.1GB = 9 个段
注意:我们会将 4 个隐藏缓冲池排除在计算之外,因为它们太小了,不会产生明显的不同。
问题:这超出了 2GB 的限制。当激活数据库或第一次连接到数据库时,您将收到如下警告:
SQL1478W
The defined buffer pools could not be started. Instead, one small
buffer pool for each page size supported by DB2 has been started.
SQLSTATE=01626
在 db2diag.log 中,您将看到有消息说 DB2 将利用隐藏缓冲池启动。要解决这个问题,可以减少主缓冲池的大小。
例 2 考虑如下配置: (所有页的大小都是 4K)
单分区,无 fenced 函数或过程,INTRA_PARALLEL=ON
IBMDEFAULTBP 300,000 页
UTILHEAP 17,500 页
DBHEAP 10,000 页
SHEAPTHRES_SHR 50,000 页
LOCKLIST 1000 页
PCKCACHE 5000 页
CATALOGCACHE 2500 页
限制: 1.5GB (6 个段)。一个段用于应用程序组内存,因为 INTRA_PARALLEL 是 ON。另一个段用于本地连接,因为 DB2_MMAP_READ 和 DB2_MMAP_WRITE 都被设为 YES (该值是默认值)。
计算:数据库共享内存 = (386,000 页 x 4KB/页的总数) x 1.1 = ~1.67GB = 7 个段
问题:这超出了 1.5GB 的限制。当尝试激活数据库或第一次连接到数据库时,您将得到以下错误消息:
SQL1042C An unexpected system error occurred. SQLSTATE=58004
要解决这一问题:
使用 db2set 将 DB2_MMAP_READ 和 DB2_MMAP_WRITE 设为 NO。这样便迫使 DB2 将段 E 用于本地连接,从而为数据库共享内存空出一个段。
例 3 考虑如下配置: (所有页的大小都为 4K)
IBMDEFAULTBP 250,000 页
INTRA_PARALLEL=ON
UTILHEAP 17,500 页
DBHEAP 10,000 页
SHEAPTHRES_SHR 20,000 页
LOCKLIST 1000 页
PCKCACHE 5000 页
CATALOGCACHE 2500 页
存在 Fenced UDF
限制: 以下各占一个段:Intra-parallel ON,本地连接,以及 fenced UDFB。这样还剩下 5 个段,或者 1.25G 给数据库共享内存。
计算:
数据库共享内存 = (306,000 页 x 4KB/页的总数) x 1.1% = ~1.35GB = 6 个段
问题: 引用了 fenced UDF 的查询就会失败,并返回错误 sql10003C。要解决这个问题,可以在 unfenced 模式下运行 UDF,或者将数据库共享内存减少至不多于 5 个段。
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 中可以观察到下面一些情况:
如果设置了 DB2DBMSADDR 注册表变量,那么实例共享内存将从该变量指定的地址开始。下面的例子展示了这一点是如何实现的。
db2set DB2DBMSADDR = 0x12000000
db2stop
db2start (需要重新启动实例,以使更改生效)。
获得 db2sys 进程的进程 ID
./pmap -x 13263
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)
虽然与
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的工具。该工具根据系统的大小对内核参数的值给出建议。对于一个给定的系统,建议的值要足够高,以便能够容纳最合理的工作负载。
Solaris
中另一个与分配数据库共享内存有关的常见问题是由于这样的一个事实导致的,即共享内存段的所有页都是固定在物理 RAM 中的。如果在 RAM
中没有足够的空闲页可用,或者没有足够的可以被 OS 为满足数据库段而调出的其他页,那么启动数据库的请求就会遭到失败。
例 1 考虑如下配置: (所有页的大小都为 4K)
服务器上的物理 RAM 16GB
shmsys:shminfo_shmmax = 2097152 (2GB)
数据库:
计算:
问题: 可能仍然可以激活数据库或者连接到数据库。但是,尝试运行一个应用程序时可能返回如下错误消息:
这是 DB2 经常返回的一个错误。不过,如果不是在 AIX 服务器上,而是在 UNIX 服务器上,那么这就很可能与内存资源问题有关。特别地,可能是内核参数没有进行适当的调优。
shmsys:shminfo_shmmax = 15099494 (~90% of 16GB)
服务器上的物理 RAM 是 1 GB。
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:
计算:
SQL1084C Shared memory segments cannot be allocated. SQLSTATE=57019
尝试减少这两个数据库的缓冲池大小。或者
尝试减少应用程序组内存。或者
更可能的是,增加更多的物理 RAM。在这种情况下,您将需要至少 1.55GB 的物理 RAM,才能同时启动这两个数据库。
在这种情况下,按照上面的数据库配置参数,在任何时刻启动某一个数据库(数据库 A 或数据库 B)是可行的,但是不能同时启动两个数据库。这是必须增加更多的物理 RAM。
例 3 考虑如下配置: (所有页的大小都是 4K)
服务器:
IBMDEFAULTBP 350,000 页
UTILHEAP 17,500 页
DBHEAP 10,000 页
LOCKLIST 1000 页
PCKCACHE 5000 页
CATALOGCACHE 2500 页
ESTORE_SEG_SZ = 400000 页
NUM_ESTORE_SEGS = 1
计算:
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_SEG_SZ = 40000 页
NUM_ESTORE_SEGS = 10
通过设置上面的 ESTORE 值,实际上我们仍然试图为 ESTORE 分配总共 1.6 GB 的内存。然而,我们将尝试为 ESTORE 分配 10 块 160MB 的内存。这样分配的连续空间就要小得多,因此最有可能解决问题。
转自:http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=879309