Windows Server 2008 R2 Enterprise,SQL Server 2008 X64,SP3,开发人员版
我构建并动态执行(通过sp_executesql)BULK INSERT命令。一般形式是:
BULK INSERT #HeaderRowCheck
from "\\Server\Share\Develop\PKelley\StressTesting\101\DataSet.csv"
with
(
lastrow = 1
,rowterminator = '\n'
,tablock
,maxerrors = 0
,errorfile = 'C:\SQL_Packages\TempFiles\#HeaderRowCheck_257626FB-A5CD-41B8-B862-FAF8C591C7A9.log'
)
(错误文件名基于已配置的本地文件夹,正在加载的表,以及为每次批量插入运行新生成的guid - 它是一个包含在自己的存储过程中的子例程。)
外部进程(是SQL代理,现在是WCF服务)启动DTEXEC,它启动一个SSIS包,该包调用循环遍历集合的数据库中的存储过程,构建查询并为每个执行查询。最多可以有四个负载同时从/进入给定数据库,并且SQL实例上的多个数据库可以同时运行 - 尽管历史上,数量一直很低,而且我们通常只有一个实例一次运行它。我们做了很多,并且它已经完成了两年多的完美工作 - 安全性已正确配置,必要的文件和文件夹存在,通常都是这样。 (运气?我不想。)
我们现在预计会有一些严重的工作负载,所以我们正在做一些压力测试,我在其中启动8次运行,每次运行有4个进程,其中一组四个将分开并逐个处理要加载的文件(即执行多达32个同时批量插入。就像我说的,压力测试。)低,看,启动时,一个或多个将在执行过程中失败,并出现如下错误信息:
Error #4861 encountered while loading header information from file "DataSet.csv": Cannot bulk load because the file "C:\SQL_Packages\TempFiles\#HeaderRowCheck_D0070742-76A5-4175-A1A7-16494103EF25.log" could not be opened. Operating system error code 80(The file exists.).
从运行到运行,不会对同一文件,数据集或整体处理点发生错误。
从表面上看,听起来有两个进程正在尝试访问同一个错误文件,这意味着它们会独立生成相同的guid(!)。我的理解是,这应该是不可能的。另一种理论是,同时发生了很多事情(可能最多同时运行32个BULK INSERT命令),SQL和/或OS在某种程度上变得混乱(我是DBA,而不是网络管理员)。我可以做一个解决方法,构建我的try-catch块以检查错误4861并重试最多三次,但我宁愿避免这样的kludgery。
我已经抛出一个例程,在使用它之前将错误文件的名称(使用guid)记录到表中。经过多次运行并且多次运行失败后,我看到(a)我的表中记录了失败的文件+ guid,以及(b)没有记录重复的guid。
任何人都知道可能会发生什么?
菲利普
答案 0 :(得分:6)
我在微软技术支持部门开了一个案例,经过不少的反复,Pradeep M.M. (SQL Server支持技术主管)全力以赴。
一般过程:读入文件夹中的文件列表,并逐个对这些文件执行一系列批量插入(首先读取第一行,我们解析列,然后从中读取数据)第二行+。所有批量插入都使用“ErrorFile”选项,以便在用户数据格式错误时为用户提供哪些信息。过程已经工作了3年多,但在最近的压力测试条件下(单个SQL Server实例最多同时运行8次,所有文件格式正确),我们得到了上面列出的错误。
我们最初虽然生成GUID时出现错误,因为“已经打开”错误,但这个想法最终被丢弃 - 如果newid()运行不正常,会有更多人拥有更多严重的问题。
根据Pradeep,以下是批量插入工作原理的分步流程:
在失败的运行期间运行ProcMon(进程监视器)显示ErrorFile已成功创建并在步骤3中打开,但未在步骤4中关闭,导致步骤5生成我们看到的错误。 (对于成功运行,文件已按预期创建和关闭。)
对ProcMon的进一步分析表明,在批量插入尝试之后,运行CMD.EXE的另一个进程正在对文件发出“关闭句柄”操作。我们使用涉及xp_cmdshell的例程来检索要处理的文件列表,这将是CMD.EXE过程的原因。这是踢球者:
...有一些业务逻辑在SQL Server中启动CMD.EXE,并且由于CMD.EXE是子进程,它继承了父进程打开的所有句柄(所以这可能是CMD中的某种时序问题。 EXE保存文件的句柄,这些文件在启动时打开,并且CMD.EXE继承的所有处理文件都无法删除,只能在CMD.EXE被销毁后才能释放。
就是这样。单个运行永远不会遇到此问题,因为在发出批量插入之前,xp_cmdshell调用已完成。但是对于并行运行,特别是对于许多并行运行(我只遇到5个或更多的问题),出现了一个时间问题:
我的短期解决方法是(1)实现重试循环,生成新的ErrorFile名称并在放弃之前尝试新的批量插入最多三次,以及(2)在我们的夜间进程上构建另一个例程以删除在我们的“ErrorFile文件夹”中找到的所有文件。
长期修复是将我们的代码修改为不通过xp_cmdshell列出文件。这似乎是可行的,因为整个ETL过程都包含在SSIS包中并由其管理;或者,CLR例程可以构建和工作。目前,考虑到我们预期的工作负载,解决方案就足够了(特别是考虑到我们刚才正在处理的其他工作),所以可能在我们实现最终结果之前固定。
张贴后人,万一发生在你身上!