SQL Server 2017 SSIS维护作业报错:execution_cursor不存在
我之前也碰到过一模一样的情况,咱们一步步来捋清楚问题和解决办法:
首先先明确你遇到的核心问题:SQL Server 2017中的SSIS Server维护作业(负责清理旧执行日志)执行失败,报错游标execution_cursor不存在,重试3次都没成功;后续随着日志量超出正常范围,所有代理作业都因为查询超时失败了。
先把你提供的错误日志贴出来方便对照:
Date 5/23/2018 12:18:45 AM
Log Job History (SSIS Server Maintenance Job)
Step ID 1
Server KARABL3
Job Name SSIS Server Maintenance Job
Step Name SSIS Server Operation Records Maintenance
Duration 00:02:34
Sql Severity 16
Sql Message ID 16916
Operator Emailed
Operator Net sent
Operator Paged
Retries Attempted 3
Message Executed as user: ##MS_SSISServerCleanupJobLogin##. A cursor with the name 'execution_cursor' does not exist. [SQLSTATE 34000] (Error 16916). NOTE: The step was retried the requested number of times (3) without succeeding. The step failed.
问题根源分析
这个报错通常是因为SSIS维护作业依赖的内置存储过程[SSISDB].[internal].[cleanup_server_retention_window]执行时,游标execution_cursor的声明/释放逻辑出了异常——比如某次执行中途中断导致游标没正常释放,或者大日志量下游标逻辑冲突,导致后续执行时要么找不到游标,要么无法重复声明同名游标。
而后续所有作业超时,是因为SSISDB里的操作日志表(比如[SSISDB].[internal].[executions]、[SSISDB].[internal].[operation_messages])数据量爆炸,作业执行时查询这些表的时间过长,触发了超时阈值。
亲测有效的解决步骤
按顺序尝试下面的方法,基本能解决问题:
1. 手动执行清理脚本,修复游标异常
先手动运行核心清理逻辑,强制完成一次清理同时修复游标残留问题:
USE SSISDB; GO -- 先检查并清理残留的execution_cursor DECLARE @cursorExists INT; SELECT @cursorExists = 1 FROM sys.dm_exec_cursors(0) WHERE name = 'execution_cursor'; IF @cursorExists = 1 BEGIN DEALLOCATE execution_cursor; END -- 手动执行清理,这里保留7天日志,批量清理1000条,可根据你的需求调整参数 EXEC [internal].[cleanup_server_retention_window] @retention_window = 7, @cleanup_batch_size = 1000; GO
执行完后重新运行SSIS Server Maintenance Job,看看是否还会报错。
2. 调整维护作业的执行参数
如果手动执行有效,但自动作业还是失败,大概率是默认参数不适合你的日志量:
- 打开SQL Server代理,找到
SSIS Server Maintenance Job,进入SSIS Server Operation Records Maintenance步骤 - 修改步骤的T-SQL命令,把
@cleanup_batch_size调小(比如从默认10000改成1000),避免一次性处理太多数据导致游标逻辑崩溃 - 同时可以把作业执行频率从每天一次改成每12小时一次,减少单次需要清理的数据量
3. 紧急清理超大日志表(已出现全作业超时的情况)
如果日志已经堆到所有作业都超时,先紧急分批清理旧日志缓解压力:
USE SSISDB; GO -- 重要!先备份SSISDB再操作 BACKUP DATABASE SSISDB TO DISK = 'D:\Backups\SSISDB_Before_Emergency_Cleanup.bak' WITH INIT; -- 分批清理30天前的日志,重复执行直到数据量降到正常范围 DELETE TOP (10000) FROM [internal].[operation_messages] WHERE operation_id IN ( SELECT operation_id FROM [internal].[executions] WHERE start_time < DATEADD(day, -30, GETDATE()) ); DELETE TOP (10000) FROM [internal].[executions] WHERE start_time < DATEADD(day, -30, GETDATE()); GO
用TOP分批删除是为了避免长时间锁表,导致其他作业彻底无法运行。
4. 修复SSISDB系统存储过程(极端情况)
如果以上方法都无效,可能是SSISDB的内置对象损坏了,重新生成一下:
-- 先停止SQL Server代理服务 USE master; GO EXEC sp_configure 'show advanced options', 1; RECONFIGURE; GO EXEC sp_configure 'clr enabled', 1; RECONFIGURE; GO USE SSISDB; GO EXEC [internal].[refresh_internal_objects]; GO -- 启动SQL Server代理服务
这个操作会重新生成SSISDB的内置存储过程和函数,修复可能的逻辑损坏。
后续预防措施
- 定期监控SSISDB日志表的大小,避免数据量过度增长
- 根据你的业务日志生成量,调整维护作业的保留窗口和批量清理大小
- 开启SQL Server代理作业的失败通知,第一时间发现清理作业的异常,避免日志堆积
内容的提问来源于stack exchange,提问作者VahidSH

