服务器 频道

DB2如何使用内存(中篇)

  【IT168 服务器学院】 需要从这种结构中了解到的最重要的事情是:
  • 对于禁用了 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 - 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

  map 输出:

  • 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 配置成创建大于这个值的数据库共享内存,那么请求就会失败。其他要知道的内核参数是 shmsegshmmni

  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

  hmat 是一个 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 的内存。这样分配的连续空间就要小得多,因此最有可能解决问题。

0
相关文章