SQL Server 2008 R2链接Oracle 12c集成时内存耗尽及系统卡顿问题求助
解决SQL Server链接Oracle EBS时PREEMPTIVE_COM_GETDATA等待与内存耗尽问题
碰到这种通过SQL Server链接服务器(ODBC驱动)往Oracle EBS R12(12c)插发票数据,用SQL Server代理启动就爆内存、系统卡顿的问题,我之前帮不少同行排查过,大概率是驱动、批量逻辑或者服务配置的坑,给你整理几个靠谱的排查和解决方向:
1. 先把ODBC驱动和链接配置捋顺
- 首先确认你用的Oracle ODBC驱动是对应Oracle 12c的最新稳定版,老驱动尤其是几年前的版本,在批量数据交互时很容易出现内存泄漏或者COM组件资源释放不彻底的问题。注意区分32/64位:如果你的SQL Server代理是32位的,就得装32位驱动,并且在
C:\Windows\SysWOW64\odbcad32.exe里配置数据源;64位的话就用系统默认的ODBC管理器。 - 检查链接服务器的设置:在SSMS里找到你的链接服务器,右键「属性」→「服务器选项」,确保**「启用RPC」「启用RPC Out」「数据访问」**这几个选项都勾上。另外把「连接超时」和「查询超时」设成合理值(比如300秒),避免无限等待导致内存堆积。
2. 优化批量插入的逻辑,别让SQL Server扛太多
- 如果是一次性插大量发票数据,千万别直接用
INSERT INTO [ORACLE_LINK]..AP_INVOICES_INTERFACE SELECT * FROM LOCAL_INVOICES这种写法,这种方式会让SQL Server在内存里缓存所有要插入的数据,再通过ODBC驱动传给Oracle,内存不爆才怪。- 改成分批插入:比如每次插1000条,每插完一批就提交事务,还可以加个短暂等待让SQL Server释放资源,示例代码大概是这样:
DECLARE @BatchSize INT = 1000; DECLARE @RowCount INT = 1; WHILE @RowCount > 0 BEGIN BEGIN TRANSACTION; INSERT INTO [ORACLE_LINK]..AP_INVOICES_INTERFACE (invoice_id, amount, vendor_id) SELECT TOP (@BatchSize) invoice_id, amount, vendor_id FROM LOCAL_INVOICES WHERE is_processed = 0; SET @RowCount = @@ROWCOUNT; UPDATE LOCAL_INVOICES SET is_processed = 1 WHERE invoice_id IN (SELECT TOP (@BatchSize) invoice_id FROM LOCAL_INVOICES WHERE is_processed = 0); COMMIT TRANSACTION; WAITFOR DELAY '00:00:01'; -- 给系统一点喘息时间 END - 改用OPENQUERY把查询推到Oracle端执行:OPENQUERY会让Oracle来处理插入逻辑,SQL Server只负责传数据,内存占用会小很多,示例:
INSERT INTO OPENQUERY(ORACLE_EBS, 'SELECT invoice_id, amount, vendor_id FROM AP_INVOICES_INTERFACE') SELECT invoice_id, amount, vendor_id FROM LOCAL_INVOICES WHERE batch_id = @CurrentBatch;
- 改成分批插入:比如每次插1000条,每插完一批就提交事务,还可以加个短暂等待让SQL Server释放资源,示例代码大概是这样:
3. 检查SQL Server代理的服务配置
- 代理服务的运行账号权限不够,也可能导致COM组件(ODBC驱动)无法正常释放资源。确保代理服务的账号有访问Oracle客户端目录、ODBC驱动安装目录的权限,最好是本地管理员权限(如果服务器是专门跑SQL Server的话)。另外要确认账号能读取Oracle的
tnsnames.ora配置文件,不然链接Oracle时会出各种奇怪的问题。 - 给SQL Server设个内存上限:在SSMS里右键实例→「属性」→「内存」,把「最大服务器内存(MB)」设成服务器总内存的70%-80%(比如服务器有32G内存,就设成24576),留足够内存给操作系统和其他进程,别让SQL Server把内存吃光。
4. 深挖等待类型和内存泄漏的根源
- 用
DBCC MEMORYSTATUS看看内存分配情况:执行这个命令后,重点看「Connection Memory」和「COM Object Memory」部分,如果这两个数值一直在涨不下降,那基本就是COM组件内存泄漏了,得换驱动或者调整逻辑。 - 用扩展事件抓
PREEMPTIVE_COM_GETDATA的详细信息:创建个扩展事件会话,抓这个等待类型对应的SQL语句和会话,就能定位到具体是哪一步出问题了,示例代码:
启动会话后重现问题,然后用SSMS打开那个xel文件,就能看到具体是哪个会话、哪个SQL语句导致的等待,针对性优化。CREATE EVENT SESSION [Preemptive_COM_Wait_Trace] ON SERVER ADD EVENT sqlserver.wait_info( ACTION(sqlserver.sql_text, sqlserver.session_id, sqlserver.login_name) WHERE ([wait_type] = N'PREEMPTIVE_COM_GETDATA')) ADD TARGET package0.event_file(SET filename = N'C:\SQLTraces\Preemptive_COM_Waits.xel') WITH (STARTUP_STATE = OFF); - 顺便查下Oracle端的性能:如果Oracle处理插入很慢,SQL Server会一直等COM组件返回结果,内存就堆起来了。登录Oracle查
v$session_wait看看有没有锁或者等待事件,查v$sql看看插入语句的执行计划是不是有问题(比如缺少索引、全表扫描)。
5. 实在不行换个集成方式
如果ODBC链接服务器这条路走不通,可以试试更稳定的方案:
- 用SSIS做数据迁移:SSIS有专门的Oracle数据源组件,对批量数据处理的内存控制更好,还能监控每一步的执行情况,比链接服务器靠谱多了。
- 用Oracle的工具导入:先把SQL Server的发票数据导出成CSV,然后用Oracle的
SQL*Loader或者外部表导入,绕过COM组件的交互,稳定性拉满。
内容的提问来源于stack exchange,提问作者JZDBA
相关产品推荐
相关产品推荐

