SSIS脚本任务锁机制引发变量死锁,无法发送SQL查询结果邮件
SSIS脚本任务变量死锁问题修复方案
错误原因分析
出现变量死锁超时的核心问题在于变量锁使用不规范,加上资源未正确释放:
- 已通过
VariableDispenser锁定变量,却同时使用Dts.Variables直接访问,引发锁冲突 - 变量解锁操作
vars.Unlock()位置不合理,未覆盖所有执行路径(如异常、提前返回) - 错误使用
SqlConnection连接DB2数据库,可能导致连接泄漏,间接影响锁资源释放 - 未在
finally块中统一处理资源释放,异常场景下锁和连接无法正常释放
修复后的完整代码
public void Main() { Variables vars = null; // 使用DB2专属连接类 IBM.Data.DB2.DB2Connection conn = null; int insertedRows1 = 0; int insertedRows2 = 0; int updatedRows11 = 0; int updatedRows12 = 0; int updatedRows21 = 0; int updatedRows22 = 0; int deletedRows = 0; string ServerName = string.Empty; try { // 一次性锁定所有需要的变量 Dts.VariableDispenser.LockForRead("User::isDataFlowSuccessful"); Dts.VariableDispenser.LockForRead("$Package::Environment"); Dts.VariableDispenser.LockForRead("$Package::SqlDatabaseServer"); Dts.VariableDispenser.LockForRead("User::InsertedRowCount1"); Dts.VariableDispenser.LockForRead("User::UpdatedRowCount11"); Dts.VariableDispenser.LockForRead("User::UpdatedRowCount12"); Dts.VariableDispenser.LockForRead("User::InsertedRowCount2"); Dts.VariableDispenser.LockForRead("User::UpdatedRowCount21"); Dts.VariableDispenser.LockForRead("User::UpdatedRowCount22"); Dts.VariableDispenser.LockForRead("User::DeletedRowCount"); Dts.VariableDispenser.LockForWrite("User::emailsubject"); Dts.VariableDispenser.LockForWrite("User::emailbody"); Dts.VariableDispenser.GetVariables(ref vars); // 统一通过vars对象读写变量 insertedRows1 = (int)vars["User::InsertedRowCount1"].Value; insertedRows2 = (int)vars["User::InsertedRowCount2"].Value; updatedRows11 = (int)vars["User::UpdatedRowCount11"].Value; updatedRows12 = (int)vars["User::UpdatedRowCount12"].Value; updatedRows21 = (int)vars["User::UpdatedRowCount21"].Value; updatedRows22 = (int)vars["User::UpdatedRowCount22"].Value; deletedRows = (int)vars["User::DeletedRowCount"].Value; ServerName = (string)vars["$Package::SqlDatabaseServer"].Value; bool isDataFlowSuccessful = (bool)vars["User::isDataFlowSuccessful"].Value; string Environment = (string)vars["$Package::Environment"].Value; int totalInsertedRows = insertedRows1 + insertedRows2; int totalUpdatedRows = updatedRows11 + updatedRows12 + updatedRows21 + updatedRows22; string message = string.Empty; string sql = "SELECT FISCAL_YR, LOC_CODE, COUNT(*) FROM LC1U1.Location_Supertbl1 GROUP BY FISCAL_YR, LOC_CODE;"; string connString = Dts.Connections["TDB1 Connection Manager"].ConnectionString; conn = new IBM.Data.DB2.DB2Connection(connString); StringBuilder resultBuilder = new StringBuilder(); conn.Open(); using (IBM.Data.DB2.DB2Command cmd = new IBM.Data.DB2.DB2Command(sql, conn)) { using (IBM.Data.DB2.DB2DataReader reader = cmd.ExecuteReader()) { // 正确读取多列查询结果 while (reader.Read()) { resultBuilder.AppendLine($"财政年度: {reader["FISCAL_YR"]}, 位置代码: {reader["LOC_CODE"]}, 数量: {reader[2]}"); } } } message = resultBuilder.ToString(); if (isDataFlowSuccessful) { vars["User::emailsubject"].Value = $"LCGMS_Supertable_Update_Process_LC1P1_{Environment}:{ServerName} Success on {DateTime.Now:yyyy-MM-dd HH:mm:ss}"; vars["User::emailbody"].Value = $"总计插入 {totalInsertedRows} 行, 更新 {totalUpdatedRows} 行, 删除 {deletedRows} 行\n\n{message}\nEND"; Dts.TaskResult = (int)ScriptResults.Success; } else { vars["User::emailsubject"].Value = $"LCGMS_Supertable_Update_Process_LC1P1_{Environment}:{ServerName} Failure on {DateTime.Now:yyyy-MM-dd HH:mm:ss}"; vars["User::emailbody"].Value = $"总计插入 {totalInsertedRows} 行, 更新 {totalUpdatedRows} 行, 删除 {deletedRows} 行\n\n{message}\nEND"; Dts.TaskResult = (int)ScriptResults.Failure; } } catch (Exception ex) { Dts.Events.FireError(0, "Script Task Error", $"{ex.Message}\n{ex.StackTrace}", string.Empty, 0); Dts.TaskResult = (int)ScriptResults.Failure; } finally { // 确保所有场景下释放变量锁 if (vars != null) { vars.Unlock(); } // 确保DB连接被关闭释放 if (conn != null && conn.State != System.Data.ConnectionState.Closed) { conn.Close(); conn.Dispose(); } // 释放连接管理器资源 Dts.Connections["TDB1 Connection Manager"].ReleaseConnection(null); } } enum ScriptResults { Success = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Success, Failure = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Failure };
关键修复点
- 统一变量访问:通过
VariableDispenser获取vars对象后,所有变量操作均通过该对象完成,避免锁冲突 - 强制资源释放:将变量解锁、DB连接关闭逻辑放在
finally块,覆盖所有执行路径 - 适配DB2连接:替换为IBM官方DB2驱动类,避免连接类型不匹配问题
- 优化数据读取:修正原代码只读取第一列的错误,正确拼接多列查询结果
- 简化代码逻辑:移除冗余判断,使用字符串插值提升可读性
内容的提问来源于stack exchange,提问作者Steven
相关产品推荐
相关产品推荐

