Недостаточно памяти при вставке записей в SQLite, FireDac, Delphi
У меня есть приложение Delphi, которое вставляет около 200000 записей (около 1 ГБ) в базу данных SQLite через компонент FireDac TFDTable. Когда он вставляется, я вижу, как увеличивается объем памяти приложения, пока не появится сообщение об ошибке "Недостаточно памяти". Я предполагаю, что это связано с кешем и подкачкой страниц, но я не могу найти ничего, что могло бы исправить закрытие и повторное открытие базы данных каждые 1000 записей или около того. Мысли?
Отредактировано... Извините за тонко сформулированный вопрос... Код был прост, поэтому я не включил его, но выглядит в основном так:
procedure DoIt;
begin
myDB.Insert;
myDBField1.AsString := StringOfChar('-',1000);
myDB.Post;
end;
Теперь я ожидаю, что объем памяти может увеличиться, поскольку строка, вероятно, копируется в кэш-память БД. Если я посмотрю на распределение с помощью GetMemoryManagerState(), то смогу увидеть это. Я ожидаю, что в какой-то момент память в кеше будет сброшена при записи данных на диск. Однако, похоже, что нет. Это продолжается до тех пор, пока я не получу ошибку "Недостаточно памяти".
Как правило, для большинства свойств объекта установлены состояния по умолчанию, за исключением выбора sqlite в соединении и добавления полей в таблицу.
Я знаю, что здесь не так много. Но я не думал, что это тоже не получится, и я надеялся, что у кого-то может быть похожая проблема.
1 ответ
TFDTable - это тонкая оболочка вокруг объекта запроса, которая может создавать команды SQL для работы с базовой таблицей СУБД. У него есть собственное хранилище (объект Table), в котором хранятся данные, извлеченные клиенту, а также вставленные вами кортежи. Но все, что находится в памяти, не имеет основного файлового кэша.
Хотя это внутреннее хранилище может быть очищено во время вставки, TFDTable не является хорошим объектом для вставки данных в таком количестве. Лучше использовать объект запроса, такой как TFDQuery, который в сочетании с техникой выполнения пакетных команд, называемой Array DML, может принести вам реальное повышение производительности даже для локального механизма СУБД. И TFDQuery не будет кэшировать вставленные кортежи.
FireDAC изначально поддерживает эту технику для SQLite при использовании привязки индексированных параметров, например, этот код должен вставлять 200-кратный пакет из 1000 уникальных кортежей:
const
BatchSize = 1000;
TotalSize = 200000;
var
Batch: Integer;
Index: Integer;
begin
FDQuery.SQL.Text := 'INSERT INTO MyTable VALUES (:p1, :p2)';
FDQuery.Params.BindMode := pbByNumber;
FDQuery.Params.ArraySize := BatchSize;
for Batch := 0 to TotalSize div BatchSize - 1 do
begin
for Index := 0 to BatchSize - 1 do
begin
FDQuery.Params[0].AsIntegers[Index] := (Batch * BatchSize) + Index;
FDQuery.Params[1].AsWideStrings[Index] := 'Some Unicode string value';
end;
FDQuery.Execute(BatchSize, 0);
end;
end;