【问题标题】:Error in SSIS Package - unable to allocate an environment handleSSIS 包中的错误 - 无法分配环境句柄
【发布时间】:2015-01-28 12:00:37
【问题描述】:

我有一个每天运行的 SSIS 包,它处理给定目录中的 CSV 文件。每个 CSV 文件包含约 2000 行,每行只有一列包含 10 或 11 个字符的字符串。该包将数据插入到记录集中,遍历行并为每行调用最多两个存储过程。

通常情况下,每天只有一个 CSV 文件在相关目录中,因此将处理大约 2000 行。我又添加了 10 行,总共有 20,000 到 25,000 行。当包运行时,它在最后约 2000 行期间抛出了以下错误:

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.InvalidOperationException: ERROR - unable to allocate an environment handle.
   at System.Data.Odbc.OdbcHandle..ctor(SQL_HANDLE handleType, OdbcHandle parentHandle)
   at System.Data.Odbc.OdbcConnection.CreateStatementHandle()
   at System.Data.Odbc.OdbcCommand.GetStatementHandle()
   at System.Data.Odbc.OdbcCommand.ExecuteReaderObject(CommandBehavior behavior, String method, Boolean needReader, Object[] methodArguments, SQL_API odbcApiMethod)
   at System.Data.Odbc.OdbcCommand.ExecuteReaderObject(CommandBehavior behavior, String method, Boolean needReader)
   at System.Data.Odbc.OdbcCommand.ExecuteNonQuery()
   at ST_5d19344133644426ade9d7b3f8a994c2.csproj.ScriptMain.Main()
   --- End of inner exception stack trace ---
   at System.RuntimeMethodHandle._InvokeMethodFast(Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.RuntimeType.InvokeMember(String name, BindingFlags bindingFlags, Binder binder, Object target, Object[] providedArgs, ParameterModifier[] modifiers, CultureInfo culture, String[] namedParams)
   at Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTATaskScriptingEngine.ExecuteScript()

一些快速谷歌搜索表明此错误与可用内存问题有关,但我无法真正理解它。

所以,我有一些问题:

1) 在尝试调用存储过程的上下文中,此错误的真正含义是什么?

2) 我将包的数据流缓冲区 DefaultBufferSize 设置保留为默认值(如果我记得的话,为 10 MB),但我被告知 SSIS 会根据需要更改大小和最大行数?这有关系吗?

【问题讨论】:

    标签: sql-server stored-procedures ssis sql-server-2008-r2 odbc


    【解决方案1】:

    我不确定该错误是什么意思,但您是否有任何迹象表明可能存在内存压力? odbc驱动是32位还是64位?

    至于如何在 SSIS 数据流中管理内存,SSIS 将使用 DefaultBufferMaxSize 和 DefaultBufferMaxRows 作为为每个缓冲区分配多少行的边界。使用默认值,它将达到 10 MB 或 10,000 行,以较大者为准,但不超过这些边界。 SSIS 不会更改这些值,但您可以将内存大小增加到 100 MB。

    编辑: 该公式将像这样工作:

    RowsPerBuffer = If (DefaultBufferMaxRows * RowSize) > DefaultBufferMaxSize
                        Then (DefaultBufferMaxSize / RowSize)
                        Else DefaultBufferMaxRows
    

    鉴于您上面的陈述,您应该看到每个缓冲区接近 10k 行。 增加默认值会为数据流分配更多内存,因此如果您的服务器内存分配严重不足,这可能会对您的 ODBC 连接造成压力。

    每行调用一个或两个 proc 是一个密集的过程。您可以考虑将这些值导入临时表并使用数据库引擎以基于集合的方式完成繁重的工作。

    编辑: 为了澄清基于集合的方法,我来自 SQL ETL 开发背景,所以我通常会考虑如何快速处理大量数据。同样,我想到了如何以最少的开发量做到这一点,同时仍然满足完整的要求。所以基本的经验法则是:

    • 尽可能使用 SSIS 开箱即用的组件
    • 将包制作成小型、可测试的单元
    • 尽可能避免逐行操作
    • 暂存数据,以便将数据库服务器用于它更擅长的任务,例如合并数据集和排序数据

    因此,如果您为每一行调用一个 proc,则意味着 SQL 中存在对该行集很重要的数据。您可以执行以下操作:

    • 添加一个 foreach 循环来处理每个文件,并将您的数据流添加到该循环中
    • 将自定义脚本源替换为标准平面文件源组件
    • 将数据按原样暂存到 SQL 表中
    • 将 proc 逻辑替换为 SQL 查询,该查询将数据集连接在一起以获得您需要的最终结果集。这个新任务可以进入它自己的存储过程。
    • 在 foreach 循环完成后,在 SSIS 中的 t-SQL 任务中调用上述查询

    最终结果是您将拥有一个性能更好且更易于维护的包。

    【讨论】:

    • 这是一个 64 位驱动程序。那么,如果 SSIS 没有达到大小限制,它会超过最大行设置吗?您能否详细说明您的上一条建议?
    • 不,SSIS 不会超过这些限制中的任何一个。我将更新我的回复,以更清楚地说明我作为替代方案的含义。
    • 我想我应该提一下,总是被调用的存储过程,将行中的字符串作为参数,并返回另一个字符串(如果有的话,然后这个字符串被传递给第二个 proc 调用被退回)。我想我明白了你的意思,而不是获取每行的数据,我会在一次调用中获得我需要的所有数据。我从中获取数据的数据库也不在 SQL Server 中,而是在远程服务器上。我可以对远程表进行联接吗?
    • @ScottOliver 开始变得有点难看,具体取决于您要检索的数据量、更改频率以及 proc 中的逻辑有多复杂。但是有几个选项 - 在本地暂存远程数据并使用我已经概述的过程,在 SSIS 中使用 MERGE JOIN 和条件拆分将数据放在一起,或者使用链接服务器。
    • 是的,我觉得我需要克服很多限制。感谢您的建议,我将来可能会这样做。
    猜你喜欢
    • 2019-08-11
    • 1970-01-01
    • 2017-02-08
    • 1970-01-01
    • 2017-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-08
    相关资源
    最近更新 更多