charleschchen
作者charleschchen·2021-06-15 10:25
系统架构师·浪潮商用机器有限公司

不合适的stack_hard设置可能造成Oracle 11gR2 sqlplus 性能问题

字数 8661阅读 4311评论 0赞 4

问题背景

/etc/security/limits 关于 stack 的限制有两个参数,分别是:

* stack  - soft stack segment size in blocks

* stack_hard - hard stack segment size in blocks

其中 stack 是软限制(当前限制值),而 stack_hard 是硬限制(最高可设置的软限制值)。 stack_hard 取值一般直接采用系统默认值。

本案例中,客户误将 stack_hard 设置为 -1 ,造成在本地启动 sqlplus 进程时,系统 CPU 消耗显著上升,系统整体性能受到了影响。

问题分析

stack的硬限制为-1的情况下 , 如果64 位进程把 stack 的软限制先通过 setrlimit 先改小 , 再改成 -1/RLIM_INFINITY , 就会触发系统分配 大量 stack segments ( 4096 个)以满足 stack size limit 扩充的需求。如果 stack 软限制始终保持为默认的 -1 , 则不会触发(系统默认初始分配 16 个 stack segments ,用完再分配新的 segments )。

从最近的一个客户案例看, oracle 11gR2 sqlplus 会调用 setrlimit 先改小 stack size( 把 soft limit 设置成 32MB) , 再改大 stack size( 取值为当前环境 ulimit -s 的实际值,在案例的环境中为 -1) , 正好符合了上述模式,因此触发了大量 stack segments 分配。作为对比, Oracle 19c 不存在此问题,即使当前系统环境 stack_hard=-1 也不会触发大量分配 stack segments 问题。这样 11gR2 在启动、退出 sqlplus 的过程耗时将会显著增加( fork 时分配 segments , exit 时销毁 segments );如果正好有大量 sqlplus 进程创建、退出,可能造成极高的 sys CPU 消耗,影响系统效率。

如下是一个 11gR2 sqlplus 的 truss 输出片段(第二个参数是指针,使用 dbx 跟踪可以观察到指针指向的具体值,有兴趣可以自行尝试):

$ truss -t appgetrlimit,appsetrlimit sqlplus "/as sysdba"

appgetrlimit(3, 0x0FFFFFFFFFFFE830)  = 0

appgetrlimit(3, 0x0FFFFFFFFFFFE5D0)  = 0

appsetrlimit(3, 0x0FFFFFFFFFFFE5D0)   = 0  // 参考 /usr/include/sys/resource.h, 第一个参数值 3 代表 RLIMIT_STACK ,也即设置 stack size.

appgetrlimit(3, 0x0FFFFFFFFFFFE830)  = 0

appgetrlimit(3, 0x0FFFFFFFFFFFE820)  = 0

appsetrlimit(3, 0x0FFFFFFFFFFFE820)  = 0

SQL*Plus: Release 11.2.0.4.0 Production on Wed Jul 12 11:54:39 2017

Copyright (c) 1982, 2013, Oracle.  All rights reserved.

解决方法

系统配置参数stack_hard不应当设置为-1 通常我们只要求对 oracle 用户设置 stack 为 -1( 软限制 ) ,而 stack_hard 硬限制默认为 8388608 blocks , 也就是 4GB ,即 16 个 stack segments ( 每个 segment 大小为 256MB) 。

Stack segments 主要存储调用栈信息以及临时变量等等。 16 个 segments 对于 stack 来说已经绰绰有余,即使是负载很重的 64 位进程,一般也最多只用到几个段。因为通常主要的数据都集中在 shared memory segments (比如 SGA )、 data segments (比如 PGA )、和 client segments (比如访问的文件)。

配置检查建议

Oracle 安装手册说明了只要求 stack=-1 ( Soft Limit ),并没有要求设置 stack_hard 为 -1 (Hard Limit).

对于安装 Oracle 11gR2 的 AIX 环境, 建议检查 /etc/security/limits 里,是否误添加了 “ stack _hard =-1 ”的设置 ,如果存在,删除即可,后续用户重新登录时即可生效。


说明:

  1. 如果是 32 位程序,按地址分布规则,最多只能有一个 stack segment ,因此不存在上述问题。
  2. Oracle 11gR2 sqlplus 会先把 stack size limit 改小成 32MB ,然后再改回环境实际的 ulimit -s 值。这个行为与 Oracle bug 12547243 的修复方法有关。经验证, Oracle 19c 不存在此问题,即使设置 stack_hard=-1 也没有影响。

PS: 根据某客户反馈, Oracle 10g 也不存在此问题。预计此 bug 仅限 Oracle11gR2 。用户可以根据附录一中的 dbx 方法来确认您的环境下 sqlplus 是否存在此问题。

附录一:相关跟踪数据

【 truss 数据】

如下是一个 truss 输出片段(第二个参数是指针,使用 dbx 跟踪可以观察到指针指向的具体值,有兴趣可以自行尝试):

$ truss -t appgetrlimit,appsetrlimit sqlplus "/as sysdba"

appgetrlimit(3, 0x0FFFFFFFFFFFE830) = 0

appgetrlimit(3, 0x0FFFFFFFFFFFE5D0) = 0

appsetrlimit(3, 0x0FFFFFFFFFFFE5D0) = 0 // 参考 /usr/include/sys/resource.h, 第一个参数值 3 代表 RLIMIT_STACK ,也即设置 stack size.

appgetrlimit(3, 0x0FFFFFFFFFFFE830) = 0

appgetrlimit(3, 0x0FFFFFFFFFFFE820) = 0

appsetrlimit(3, 0x0FFFFFFFFFFFE820) = 0

而在 Oracle 10g 的版本里面是没有调用setrlimit方法的,所以即使在 10g 的环境中设置了 stack_hard 为 -1 也不会触发问题。

正常情况与异常情况的 truss 对比

正常情况:


异常变慢:

【 dbx 数据】

对 19c 的跟踪, 19c 的 sqlplus 不会调用 appsetrlimit 修改 stack 限制(而是直接使用默认值):

对 11g 的跟踪如下, 11g 的 sqlplus 会调用两次 appsetrlimit 修改 stack 限制,第一次设置为 32MB ,第二次设置为 unlimited ,触发段空间分配:

$ dbx /oracle/db/bin/sqlplus

Type 'help' for help.

reading symbolic information ...warning: no source compiled with -g

(dbx) listi appgetrlimit

0x900000000206290 (appgetrlimit)  e9822c68  ld  r12,0x2c68(r2)

0x900000000206294 (appgetrlimit+0x4)  f8410028  std  r2,0x28(r1)

0x900000000206298 (appgetrlimit+0x8)  e80c0000  ld  r0,0x0(r12)

0x90000000020629c (appgetrlimit+0xc)  e84c0008  ld  r2,0x8(r12)

0x9000000002062a0 (appgetrlimit+0x10) 7c0903a6  mtctr  r0

0x9000000002062a4 (appgetrlimit+0x14) 4e800420  bctr

0x9000000002062a8 (appgetrlimit+0x18) 00000000  Invalid opcode.

0x9000000002062ac (appgetrlimit+0x1c) 000ca000  Invalid opcode.

0x9000000002062b0 (appgetrlimit+0x20) 00000000  Invalid opcode.

0x9000000002062b4 (appgetrlimit+0x24) 00000018  Invalid opcode.

(dbx) stop in appsetrlimit

[1] stop in appsetrlimit

(dbx) run "/as sysdba"

[1] stopped in appsetrlimit at 0x900000000206268 ($t1)

0x900000000206268 (appsetrlimit)  e9822c60  ld  r12,0x2c60(r2)

(dbx) where

appsetrlimit() at 0x900000000206268

setrlimit(??, ??) at 0x900000000206050

sskgm_set_stack_limit(??, ??, ??) at 0x100103e2c

sskgminit(??, ??) at 0x100103b5c

skgminit(??, ??, ??, ??) at 0x1000f98a4

kpuiniPG(??) at 0x10009a9f4

kpuinit0(??, ??, ??, ??, ??, ??, ??, ??) at 0x10009bf40

kpuenvcr(0x900000000000980, 0x2700000026, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0) at 0x10009a670

OCIEnvCreate(??, ??, ??, ??, ??, ??, ??, ??) at 0x1000866bc

afioci(??, ??, ??) at 0x100085884

aficntini(??) at 0x101cc83a0

afidrv(??, ??, ??, ??) at 0x10005dce0

main(??, ??) at 0x100000688

(dbx) registers

 $r0:0x0000000100103e30  $stkp:0x0fffffffffffe4e0  $toc:0x09001000a01e94d0

 $r3:0x0000000000000003  $r4:0x0fffffffffffe5e0  $r5:0x7fffffffffffffff

 $r6:0x800000000000f032   $r7:0x0000000000003b60  $r8:0x0000000000000000

 $r9:0xf1000a01508b7400  $r10:0x0000000000000000  $r11:0xf00000002ff47600

 $r12:0x09001000a01e2350  $r13:0x00000001101f00e0  $r14:0x00000001102be2b0

 $r15:0x0000000101f050e8  $r16:0x0000000101f05150  $r17:0x0000000000000000

 $r18:0x00000001102be9d0  $r19:0x00000001102be420  $r20:0x0000000000000000

 $r21:0x0000000000000000  $r22:0x0000000000000000  $r23:0x0000000000000000

 $r24:0x0000000000000000  $r25:0x0000000000000000  $r26:0x0000000000000000

 $r27:0x0000000101d62c58  $r28:0x0000000002000000  $r29:0x0fffffffffffe990

 $r30:0x0fffffffffffe968  $r31:0x0000000000000000

 $iar:0x0900000000206268  $msr:0xa00000000000d032  $cr:0x22200228

$link:0x0900000000206054  $ctr:0x0900000000206040  $xer:0x00000000

 Condition status = 0:e 1:e 2:e 5:e 6:e 7:l

 [unset $noflregs to view floating point registers]

 [unset $novregs to view vector registers]

 [unset $novsregs to view vector scalar registers]

in appsetrlimit at 0x900000000206268 ($t1)

0x900000000206268 (appsetrlimit)  e9822c60  ld  r12,0x2c60(r2)

(dbx) 0x0fffffffffffe5e0/16x

0x0fffffffffffe5e0:  0000 0000 0200 0000 7fff ffff ffff ffff

0x0fffffffffffe5f0:  0fff ffff ffff e670 0000 0000 0000 0000

(dbx) cont

[1] stopped in appsetrlimit at 0x900000000206268 ($t1)

0x900000000206268 (appsetrlimit)  e9822c60  ld  r12,0x2c60(r2)

(dbx) registers

 $r0:0x0000000100103e30  $stkp:0x0fffffffffffe730  $toc:0x09001000a01e94d0

 $r3:0x0000000000000003  $r4:0x0fffffffffffe830  $r5:0x7fffffffffffffff

 $r6:0x800000000000f032  $r7:0x0000000000003b60  $r8:0x0000000000000000

 $r9:0xf1000a01508b7400  $r10:0x0000000000000000  $r11:0xf00000002ff47600

 $r12:0x09001000a01e2350  $r13:0x00000001101f00e0  $r14:0x00000001102be2b0

 $r15:0x0000000101f050e8  $r16:0x0000000101f05150  $r17:0x0000000000000000

 $r18:0x00000001102be9d0  $r19:0x00000001102be420  $r20:0x0000000000000000

 $r21:0x0000000000000000  $r22:0x0000000000000000  $r23:0x0000000000000000

 $r24:0x0000000000000000  $r25:0x0000000000000000  $r26:0x0000000000000000

 $r27:0x0000000101d62c58  $r28:0x7fffffffffffffff  $r29:0x0000000000000000

 $r30:0x0000000000000000  $r31:0x0000000000000000

 $iar:0x0900000000206268  $msr:0xa00000000000d032  $cr:0x22200228

$link:0x0900000000206054  $ctr:0x0900000000206040  $xer:0x00000000

 Condition status = 0:e 1:e 2:e 5:e 6:e 7:l

 [unset $noflregs to view floating point registers]

  [unset $novregs to view vector registers]

 [unset $novsregs to view vector scalar registers]

in appsetrlimit at 0x900000000206268 ($t1)

0x900000000206268 (appsetrlimit)  e9822c60  ld  r12,0x2c60(r2)

(dbx) where

appsetrlimit() at 0x900000000206268

setrlimit(??, ??) at 0x900000000206050

sskgm_set_stack_limit(??, ??, ??) at 0x100103e2c

skgm_set_stack_limit(??, ??, ??) at 0x1000f9f4c

kpuiniPG(??) at 0x10009aa4c

kpuinit0(??, ??, ??, ??, ??, ??, ??, ??) at 0x10009bf40

kpuenvcr(0x900000000000980, 0x2700000026, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0) at 0x10009a670

OCIEnvCreate(??, ??, ??, ??, ??, ??, ??, ??) at 0x1000866bc

afioci(??, ??, ??) at 0x100085884

aficntini(??) at 0x101cc83a0

afidrv(??, ??, ??, ??) at 0x10005dce0

main(??, ??) at 0x100000688

(dbx) 0x0fffffffffffe830/16x

0x0fffffffffffe830:  7fff ffff ffff ffff 7fff ffff ffff ffff

0x0fffffffffffe840:  0000 0000 0200 0000 0000 0000 0000 0000

(dbx) cont

SQL*Plus: Release 11.2.0.4.0 Production on Wed Jul 12 12:23:15 2017

Copyright (c) 1982, 2013, Oracle.  All rights reserved.

Connected to:

Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production

With the Partitioning and Automatic Storage Management options

SQL> quit

Disconnected from Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production

With the Partitioning and Automatic Storage Management options

execution completed

如果觉得我的文章对您有用,请点赞。您的支持将鼓励我继续创作!

4

添加新评论0 条评论

Ctrl+Enter 发表

作者其他文章

相关文章

相关问题

相关资料

X社区推广