Oracle 数据库的网络服务默认采用专用服务器模式,每个客户端会话对应一个独立的服务器进程。但并发连接数较高时,专用模式会占用大量内存,共享服务器模式则通过调度器进程将众多连接复用到较少的共享服务器进程上。这里的调度器(Dispatcher)负责接收客户端请求并把结果返回给客户端,是共享服务器模式能否稳定运行的关键组件。调度器的数量、协议句柄以及监听注册方式都会直接影响连接分配效率和请求队列等待情况。

一、调度器在共享服务器模式中的协作机制
共享服务器模式由监听器、调度器进程、请求队列、共享服务器进程、响应队列和客户端连接构成。客户端发起连接时,监听器会根据服务注册信息选择一个可用的调度器,并把该调度器的地址返回给客户端。如果监听器没有找到可用的调度器,或者服务没有以共享模式注册,客户端连接会回退到专用服务器模式,或者直接失败。随后客户端与调度器建立持久连接,后续的所有 SQL 和数据传输都经过这个调度器。
调度器收到客户端请求后,会将请求放入 SGA 中的请求队列。共享服务器进程从请求队列中取出请求并执行,结果放入响应队列,调度器再从响应队列取出结果返回给对应客户端。与专用服务器模式相比,共享服务器进程不需要保持与客户端的直接连接,因此进程数量可以大幅降低。但队列本身需要消耗 large pool 内存,如果 large pool 尺寸不足,请求和响应队列无法分配,可能出现 ORA-04031 错误。所以配置调度器之前,通常需要把 large pool 设置得足够大。
调度器进程在操作系统中的名称类似 D000、D001,而共享服务器进程名称为 S000、S001。可以通过 ps -ef | grep ora_d 或 ps -ef | grep ora_s 查看。数据库参数 MAX_DISPATCHERS 虽然可以限制最大调度器数量,但多数情况下直接通过 DISPATCHERS 参数控制实际启动的数量。一个调度器可以服务的会话数没有固定上限,但每个调度器只能监听一个网络协议地址,因此如果需要同时支持 TCP 和 IPC,就要配置多个调度器项。
二、DISPATCHERS 与 SHARED_SERVERS 参数配置
DISPATCHERS 是共享服务器模式最核心的初始化参数。它的值由一组或多组括号表示,每组指定一个网络协议和该协议下的调度器数量。常见写法如下:
ALTER SYSTEM SET dispatchers='(PROTOCOL=TCP)(DISPATCHERS=3)' SCOPE=BOTH;
上面语句表示配置 3 个 TCP 协议调度器,SCOPE=BOTH 会同时修改内存和 spfile,这样数据库重启后仍然有效。如果只需要临时调整,可以使用 SCOPE=MEMORY。若使用 pfile 而不是 spfile,则需要修改初始化参数文件后重启数据库才能生效。
如果环境同时存在 TCP 和 IPC 协议,可以在一行中配置多项,用逗号分隔:
ALTER SYSTEM SET dispatchers='(PROTOCOL=TCP)(DISPATCHERS=3)','(PROTOCOL=IPC)(DISPATCHERS=1)' SCOPE=BOTH;
调度器数量并不是越多越好。每个调度器进程都会占用一定的内存和 CPU,过多的调度器会分散连接,导致某些调度器空闲而另一些排队。一般建议根据并发会话数和每个调度器的活跃程度逐步增加。初始配置可以从 2 到 3 个 TCP 调度器开始,监控一段时间后再决定是否扩容。共享服务器进程的数量由 SHARED_SERVERS 参数控制,它决定数据库启动时启动的最小共享服务器进程数。如果请求队列持续积压,Oracle 会自动启动额外的共享服务器进程,最多不超过 MAX_SHARED_SERVERS。一个常用的配置组合如下:
ALTER SYSTEM SET shared_servers=10 SCOPE=BOTH; ALTER SYSTEM SET max_shared_servers=30 SCOPE=BOTH; ALTER SYSTEM SET max_dispatchers=10 SCOPE=BOTH;
需要注意的是,SHARED_SERVERS 和 DISPATCHERS 都是动态参数,可以通过 ALTER SYSTEM 在线调整。但已有客户端连接不会因为新增调度器而自动切换,只有新建连接才会被分配到新启动的调度器。因此如果业务需要立即分散压力,除了增加调度器,还需要等待新连接进入,或者手动断开部分空闲会话。
客户端连接字符串也需要明确指出使用共享服务器模式,例如在 tnsnames.ora 中加上 SERVER=SHARED。如果没有这个参数,默认会使用专用服务器连接,调度器根本不会被使用。监听器服务注册方面,可以使用 lsnrctl services 查看服务是否包含 D000 之类的调度器地址,确保监听器能够把新连接转发给调度器。
三、监控调度器状态与优化调整
调度器配置完成后,需要通过动态性能视图检查其工作状态。最常用的视图是 V$DISPATCHER,它记录了每个调度器的名称、网络协议、状态、已接受连接数、消息数、忙时间和空闲时间等字段。以下查询可以看到调度器是否处于正常接收状态:
SELECT name, network, status, accept, messages, busy, idle, owned, queued FROM v$dispatcher ORDER BY name;
其中 STATUS 为 WAIT 或 RECEIVE 通常表示调度器空闲等待客户端请求;ACCEPT 为 YES 表示仍然可以接受新连接。BUSY 字段表示调度器处理请求的总时间,IDLE 表示空闲时间,两者比例可以反映调度器忙碌程度。如果 BUSY / (BUSY + IDLE) 长期接近 100%,说明该调度器已经非常繁忙,需要增加调度器数量。如果 QUEUED 持续大于 0,说明请求出现排队,同样需要考虑扩容。
另一个需要关注的是 V$SHARED_SERVER 视图,它展示共享服务器进程的状态和请求处理情况。共享服务器进程不足时,Oracle 会自动启动更多进程,但自动扩展也有上限。如果请求队列平均等待时间持续上升,可以结合 V$QUEUE 视图查看请求队列和响应队列的平均等待:
SELECT q.name, q.waiting, q.average_wait
FROM v$queue q
WHERE q.name IN ('COMMON REQUEST', 'COMMON RESPONSE');
这里的 COMMON REQUEST 是共享服务器处理常规请求的队列,COMMON RESPONSE 是响应队列。如果请求队列等待时间明显高于响应队列,说明共享服务器进程处理速度跟不上,需要适当提高 SHARED_SERVERS,同时确认共享服务器进程没有被某些长事务阻塞。共享服务器环境不适合执行长时间占用数据库进程的操作,例如大批量排序、并行 DML 或者某些 PL/SQL 长时间循环。这类操作会让共享服务器进程被长期占用,影响其他通过同一队列处理的会话。
最后还要检查 large pool 的剩余空间。可以通过查询 V$SGASTAT 中的 large pool 使用情况判断是否存在内存不足风险:
SELECT pool, name, bytes FROM v$sgastat WHERE pool = 'large pool' ORDER BY bytes DESC;
如果 free memory 长期很小,可能出现 ORA-04031。可以将 LARGE_POOL_SIZE 调大,或者减少不必要的其他组件占用 large pool。在共享服务器模式下,Oracle 会把 UGA 的一部分放入 large pool,因此调度器会话越多,large pool 需求越大。通常建议至少设置数百 MB,具体以 V$SGASTAT 和 V$SESSTAT 的实际消耗为准。
通过以上监控和调整,可以保证调度器数量与业务并发匹配,避免连接排队和共享服务器进程饥饿。若需要回退到专用服务器模式,只需将 DISPATCHERS 清空或设置为 0,并将客户端连接字符串中的 SERVER=SHARED 去掉即可。