gpt4 book ai didi

sql-server - 将 600MB 文件作为文件流数据插入 SQL Server Express 时出现内存不足错误

转载 作者:行者123 更新时间:2023-12-03 15:04:03 24 4
gpt4 key购买 nike

(请阅读下面的更新部分,为了清楚起见,我也保留了原始问题)

我正在将许多文件插入到为文件流配置的 SQL Server 数据库中。

我正在循环中将文件从文件夹插入到数据库表中。

一切都很顺利,直到我尝试插入 600 MB 文件。

当它插入时,任务管理器中的内存使用量超过了 600MB,我遇到了错误。

数据库大小 < 1 GB,文档总大小为 8 GB,我使用的是 SQL Server Express R2,根据文档,只有在尝试插入大于 10 GB 的文档时,我才会遇到问题(明确限制)- 当前数据库大小。

谁能告诉我为什么会出现这个错误?这对我来说非常重要。

赏金更新:

我提供了150,因为这对我来说非常重要!

这似乎是Delphi内存管理器的限制,试图插入大于500MB的文档,我没有检查确切的阈值,反正它在500到600MB之间)。我使用 SDAC 组件,特别是 TMSQuery (但我认为可以使用 TDataset 后代完成同样的操作),将文档插入到具有 PK (ID_DOC_FILE) 和 varbinary(max) 字段 (DOCUMENT) 的表中:

procedure UploadBigFile;
var
sFilePath: String;
begin
sFilePath := 'D:\Test\VeryBigFile.dat';
sqlInsertDoc.ParamByName('ID_DOC_FILE').AsInteger := 1;
sqlInsertDoc.ParamByName('DOCUMENT').LoadFromFile(sFilePath, ftblob);
sqlInsertDoc.Execute;
sqlInsertDoc.Close;
end;

SDAC 团队告诉我这是 Delphi 内存管理器的限制。现在,由于 SDAC 不支持文件流,我无法执行第一个答案中 c# 中建议的操作。唯一的解决方案是向 Embarcadero 报告并要求修复错误吗?

最终更新:

真的谢谢所有回答我的人。当然,对于 Express 版来说,插入大 blob 可能是个问题(因为 1 GB 内存的限制),无论如何,我在企业版上遇到了错误,而且这是一个“delphi”错误,而不是 sql server 错误。所以我认为我接受的答案确实解决了问题,即使我现在没有时间验证它。

最佳答案

SDAC team told me this is a limitation of Delphi memory manager

对我来说,这看起来像是一个简单的答案,我进行了调查。我没有 SDAC 组件,也不使用 SQL Server,我最喜欢的是 Firebird SQL 和 IBX 组件集。我尝试使用 IBX 将 600Mb blob 插入表中,然后使用 ADO 尝试相同的操作(涵盖两种连接技术,都是 TDataSet 的后代)。我发现事实是在中间的某个地方,它并不是真正的内存管理器,这不是 SDAC 的错(嗯...他们有能力对此做点什么,如果更多的人尝试将 600 Mb blob 插入到数据库,但这与本讨论无关)。 “问题”出在Delphi 中的DB 代码上。事实证明,Delphi 坚持使用单个 Variant 来保存可能加载到参数中的任何类型的数据。这是有道理的,毕竟我们可以将很多不同的东西加载到 INSERT 的参数中。第二个问题是,Delphi 希望将该 Variant 视为 VALUE 类型:它将它在列表中复制两次,也许三次!从文件加载参数时会立即生成第一个副本。当参数准备发送到数据库引擎时,会创建第二个副本。

写这个很容易:

var V1, V2:Variant;
V1 := V2;

并且适用于整数、日期和小字符串,但是当 V2 是 600 Mb 变体数组时,该赋值显然会生成完整副本!现在考虑一下未在“3G”模式下运行的 32 位应用程序的可用内存空间。只有 2 Gb 的寻址空间可用。其中一些空间是保留的,一些空间用于可执行文件本身,然后是库,然后是为内存管理器保留的一些空间。在进行第一个 600 Mb 分配后,可能没有足够的可用寻址空间来分配其他 600 Mb 缓冲区!因此,可以肯定地将其归咎于内存管理器,但话又说回来,为什么数据库内容到底需要 600 Mb 怪物的另一个副本?

一个可能的修复

尝试将文件分割成更小、更易于管理的 block 。将数据库表设置为具有 3 个字段:ID_DOCUMENT、SEQUENCE、DOCUMENT。还将表上的主键设置为(ID_DOCUMENT,SEQUENCE)。接下来试试这个:

procedure UploadBigFile(id_doc:Integer; sFilePath: String);
var FS:TFileStream;
MS:TMemoryStream;
AvailableSize, ReadNow:Int64;
Sequence:Integer;
const MaxPerSequence = 10 * 1024 * 1024; // 10 Mb
begin

FS := TFileStream.Create(sFilePath, fmOpenRead);
try
AvailableSize := FS.Size;
Sequence := 0;
while AvailableSize > 0 do
begin
if AvailableSize > MaxPerSequence then
begin
ReadNow := MaxPerSequence;
Dec(AvailableSize, MaxPerSequence);
end
else
begin
ReadNow := AvailableSize;
AvailableSize := 0;
end;
Inc(Sequence); // Prep sequence; First sequence into DB will be "1"
MS := TMemoryStream.Create;
try
MS.CopyFrom(FS, ReadNow);

sqlInsertDoc.ParamByName('ID_DOC_FILE').AsInteger := id_doc;
sqlInsertDoc.ParamByName('SEQUENCE').AsInteger := sequence;
sqlInsertDoc.ParamByName('DOCUMENT').LoadFromStream(MS, ftblob);
sqlInsertDoc.Execute;

finally MS.Free;
end;
end;
finally FS.Free;
end;

sqlInsertDoc.Close;

end;

关于sql-server - 将 600MB 文件作为文件流数据插入 SQL Server Express 时出现内存不足错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3247324/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com