聊聊数据库 -- Oracle DRCP 常驻连接池测试

背景交代

在 oracle 10g(包括10g)之前,Oracle 数据库提供两种数据库连接方式:

1. Dedicated方式:针对每个数据库连接,Oracle实例都创建一个服务器进程来处理应用过来的每一个连接;优点,每个进程的内存、数据都不共享,每个连接之间互不影响;缺点,每个连接都需要在操作系统层面启动一个新的进程,由于进程的创建和销毁需要占用较多的系统资源(分配内存、CPU等),在频繁创建和销毁连接的情况下,数据库的资源得不到充分的利用,都浪费在创建和销毁进程上了,同时,由于每个进程之间并不共享数据,所以每个进程都需要单独分配内存,在数据库连接较多的情况下,需要操作系统分配较多的内存。

2. Shared Server(或者叫MTS)方式:针对 Dedicated 方式连接数据的缺点,Oracle 提出了共享服务器方式,通过在服务器上提前启动一定数据量的服务器进程 和 Dispatcher 进程,Dispatcher 进程的作用类似于代理,针对应用到数据库的每个连接,首先连接到 Dispatcher 进程上,然后由 Dispatcher 进程转给空闲的服务器进程处理,服务器进程处理完毕之后再把结果返给 Dispatcher 进程,最终 Dispatcher 进程把结果返给应用服务器。从共享服务器的工作方式能够发现,共享服务器通过共享服务器进程在一定程度上缓解了频繁创建和销毁连接对数据库的冲击,同时由于数据库设置的共享服务器进程的数量是可控的,在一定程度上也降低了数据库服务器针在连接上分配的内存。

Shared Server 模式看起很美好,还有什么问题?

新问题出现啦~~~

1. 有些开发语言,例如JAVA,是支持连接池的,这样也能够通过连接池来降低创建连接的开销;可是有些开发语言,例如PHP,本身是不支持连接池的,而PHP有名的短连接会对Oracle 数据库造成一个非常大冲击;

2. 即使有连接池,仍以JAVA为例,URS(网易通行证系统) 163 线上环境配置为 Shared Server 的情况下,从数据库统计到的某一时刻同时活跃的 session 数,一般在 30~100 之间,极端情况下活跃 session 数也不会超过 200 个:

查询活跃和非活跃session数

但是同时数据库中需要维护 1400 ~ 1500 的不活跃的 session,造成这个问题原因是:应用的每个节点都维护了一个自己的连接池,设置了初始化连接数和最大连接数等,而实际运行环境的大部分情况下,每台 Resin(应用节点) 的连接池中只有少部分连接是处于活跃状态,大部分的连接都是空闲在连接池中,大量空闲的 session 浪费了一些数据库机器上的资源。应用使用连接池是一个好事情,同时,也引入一个小麻烦:每当增加一台新的应用服务器的时候,数据库的空闲 session 就会出现一次增长,同时可能需要微调数据库的初始化参数 sessions 和processes,保证数据库能够处理新增加的 sessions 和 processes。

肿么办?

Oracle 又引入了一个新特性,Database Resident Connection Pool (DRCP)常驻连接池来解决:通过把应用服务器上的连接池搬到了数据库上:应用服务器连接到数据库服务器上的每个连接,直接连接到数据库常驻连接池中的空闲连接,此时,增加或者减少应用服务器个数,对数据库的冲击就减少多了。同时由于减少了不活跃的 session 数,也节约了一部分的内存。

真滴那么美好么,压力测试看看。

压力测试

1. 压力测试环境介绍 10.110.10.154

(1)物理硬件配置

Dell R720, 8G * 24 = 192G内存,Intel(R) Xeon(R) CPU E5-2620 0 @ 2.00GHz

12块SSD做了三组 RAID10

(2)软件配置

设置 /etc/sysctl.conf 配置共享内存段大小、hugepage 配置,和线上服务器一致,并重启生效;同时对于 account, user_profile, urs_mobile 三张表也单独设置 keep 池,和线上配置一致;sga_max_size 等数据参数和线上一致;

-- 设置 KEEP池

alter table urs.account STORAGE (BUFFER_POOL KEEP);

alter table urs.user_profile STORAGE (BUFFER_POOL KEEP);

alter table urs.urs_mobile STORAGE (BUFFER_POOL KEEP);

2. 开启 DRCP 特性,微调测试环境 10.110.10.154 的参数配置

(1)配置连接池的初始化设置,配置初始化连接池连接个数200个,最大连接池连接个数500

个,最大Session数设置为4000个,基本能够满足线上查询的需要:

begin

dbms_connection_pool.configure_pool(

POOL_NAME=>'SYS_DEFAULT_CONNECTION_POOL',

minsize=>200,

maxsize=>500,

INCRSIZE=>10,

SESSION_CACHED_CURSORS=>100,

inactivity_timeout=>3000,

max_think_time=>100,

MAX_USE_SESSION=>4000,

MAX_LIFETIME_SESSION=>36000

);

end;

(2)关闭 shared server 设置,检查 session, process, shared_server 的设置等,避免Shared Server的设置对压测的结果产生影响:

alter system set shared_servers=0;

alter system set dispatchers='(protocol=tcp)';

(3)设置逻辑从库 10.110.10.154 实时应用线上的 redo 日志,保证和线上的写入基本实时(延时最多几秒钟,取决于网络情况),避免非实时的写入造成每个归档传送到从库之后,短期的解析应用日志高峰,wtps会出现突增到 700 左右的情况:

alter database start logical standby apply immediate;

(4)最重要的 DRCP 连接池分配的内存配置,根据 Oracle 给出的公式计算:

初始连接池 200 个活跃连接时:Memory used = 200 *(400KB + 4MB)+(4000 * 35KB)= 1G左右连接池最大 400 个活跃连接时:Memory used = 500 *(400KB + 4MB)+(4000 * 35KB)= 2.5G 左右也就是测试环境 10.110.10.154 的 PGA 设置为 3G(线上的PGA值) + 2G(分配给 DRCP 连接池);

alter system set pga_aggregate_target = 5G scope=both;

(5)配置完毕后,开启 DRCP 连接池:

exec dbms_connection_pool.start_pool;

开启连接池之后,能够从 dba_cpool_info 视图观察到 连接池的状态为 ACTIVE:

从操作系统层面,能够从进程上观察到,首先增加了一个 ora_nXXX_$ORACLE_SID 的进程,该进程是 Connection Broker 管理进程,负责常驻连接池 DRCP 中连接对象的管理:

还能够观察到,增加了很多个的 ora_lXXX 进程,这就是 DRCP 连接池中的连接对象,默认是创建了初始化连接池连接池个数的进程,我们的案例环境中初始化创建了 200个 ora_lXXX 进程:

3. 压力测试方式

(1)使用tcpcopy工具转发部分线上(reg.163.com)的请求到测试机器 10.110.10.154,本测试仅关注数据库的表现,所以不计算复制部分的请求,约有 40% 左右的线上真实访问请求转给数据库;

(2)同时我们在测试机器 10.110.10.154 上通过 JAVA程序并发访问随机抽取的 8k万用户,执行SQL 为 select * from account where ssn = ?

4. 压力测试目的

(1)验证在高并发请求下,DRCP 连接池(JDBC连接下) 确实能够节约部分内存,按照Oracle 给出的计算公式预估:

Shared Server 模式下,线上 URS 163 配置的 Shared Server 初始值为 300, 最大值为 500,同时设置 Session 数为 6048,而实际上线上使用的 Shared Server 为 300个,Session 约1500 左右:

根据公式,大约能够计算出 Shared Server 最多需要耗费 6048 * 400KB + 500 * 4MB = 4.8G 内存 ,正常需要耗费 1500 * 400KB + 300 * 4MB = 2G 内存

DRCP 模式,连接池初始化连接 200,最大连接500(同时并发活跃最大500),同时设置最多需要耗费 500 *(400KB + 4MB)+(6048 * 35KB) = 2.2 G 内存,正常需要耗费 200 *(400KB + 4MB)+(1500 * 35KB)= 950MB 内存

也就是说预估测试结果能够节约 1G ~ 2G 的内存。

(2)验证高并发请求下,DRCP 连接池(JDBC连接下) 能够表现比较稳定,不触发Bug等。

压力测试结果出炉啦:

  1. 高并发请求下,确实能够节约很小部分内存:

线上环境的内存使用情况:

对比测试环境上的内存使用量:

关注 -/+ buffers/cache 的 free 内存情况:在高并发压测的情况下,剩余内存基本差不多在26G左右,没有发现明显内存节约的现象(DBTEST程序大约占了2G内存),仅仅是在 Free 内存部分比线上出现明显降低(约降低了18G),应当是进程缓存的数据降低了。

2. 高并发请求下,DRCP 连接池表现的不是特别稳定:

在并发 LOAD 压到 60 开始,后台 ALERT 日志中已经开始断断续续出现异常:

其中,L159,L230等进程之前介绍过,是 DRCP 连接池中的连接对象进程,在短期内高并发连接数据库的情况下,出现一些不能快速的创建连接池的连接进程,出现了部分报错:

(1)提示 Failed to start LXXX ,无法启动连接对象进程 ora_lXXX

(2)提示 Error occured while spawning process LXXX,现有的 Oracle 进程无法生成连接对象进程 ora_lXXX而 601 错误表示:PMON 进程遇到了锁竞争

应该是短期高频的连接过来的时候(200个JAVA线程并发创建连接查询),DRCP 连接池中的连接不足以应付突增的请求时,会出现一些进程创建错误的异常。

3. 总结

在当前数据库版本(11.2.0.2)和JDBC驱动版本(11g)下:高并发请求下,DRCP 连接池对比JDBC连接池来说,基本可以说没有什么性能提升。

DRCP的缺点

1. 11.2.0.2版本仅支持JDBC OCI连接设置为POOLED;并不支持JDBC THIN 连接;

2. DRCP是一个非常新的功能,在12c中仍然作为新特性来介绍,以此推测,11g中的DRCP功能不一定非常完善;

DRCP will be introduced as a new feature in JDBC 12c

3. 从文档说明来看:DRCP更适合不提供连接池的开发语言,例如PHP,C++等,DRCP降低了Oracle创建连接和销毁连接的开销。即支持连接池的语言中,例如Java,使用DRCP最大的好处就是节约少量Oracle数据库的内存资源,减少服务器上不活跃连接的数量,使得随着应用服务器的增加,数据库连接不至于出现较大增长,但在一定程度上也提高了耦合,当DRCP连接池出现问题的时候,所有应用都无法连接数据库,提高了系统的耦合性;从性价比来看,JAVA并不适合使用DRCP连接池。

4. 在使用 DRCP 连接池中数据库的一些管理操作受限。

FAQ

1. 在配置中最常见的问题,在客户端尝试通过 POOLED 方式连接数据库常驻连接池的时候,返回错误 ORA-12523:TNS:listener could not find instance appropriate for the client一般来说,提示这个错误可能有以下几个原因:

(1)数据库端没有启动常驻连接池,可以通过命令启动连接池解决:

exec dbms_connection_pool.start_pool;

或者调整客户端配置 SERVER=POOLED 为 SERVER=DEDICATED 或者 SERVER=SHARED解决。

(2)数据库端已经启动常驻连接池,此时需要检查 $TNS_ADMIN/tnsnames.ora 文件中的配置,例如:下面的例子的 TNS 配置基本一致,唯一的区别就在配置的是 SERVICE_NAME 还是 SID:

此时,通过 SERVICE_NAME 连接的时候就会返回 ORA-12523 错误,调整为 SID 连接即可:

2. 通过设置 实时应用日志方式避免非实时的写入造成每个归档传送到从库之后,短期的解析应用日志高峰,wtps会出现徒增到 700 左右的情况,保证和线上的写入压力基本一致:

调整为实时应用日志之后: wtps 基本维持在 200~300 之间,和线上服务器基本一致:

说明

以上内容是作者在网易工作期间,对数据库常驻连接池的技术调研结果,测试数据源于真实的流量,是对Oracle DRCP特性的一次探索。希望能够对大家学习这项Oracle特性提供一定的借鉴和参考,水平有限,欢迎留言指教和探讨。

请持续关注我的头条号,后续会有更多干货知识分享。

举报
评论 0