Как использовать Delphi для обработки очень большого списка с ограниченной памятью

44
3

Я только что обнаружил, что мой программист фактически загрузил всю базу данных SQLite в списки (выберите * из tablename) (для операций поиска и фильтрации) вместо выполнения SQL-запросов отдельно. Это работало хорошо, когда насчитывается менее 20 000 - 50 000 записей. Но как только память работает на низком уровне или там будет определенное количество записей, приложение Delph 7 замерзает.

Слишком поздно менять этот подход, поскольку мы глубоко входим в проект и его развертываем. У меня нет времени для повторного решения, поэтому мне нужно найти "быстрое исправление", чтобы уменьшить использование памяти и увеличить количество записей в базе данных. Мне интересно, можно ли разбить список на файлы и затем обрабатывать один за другим?

Обновлено, чтобы добавить ответы OPs на вопрос

Спасибо за ответ. Здесь некоторые фрагменты кода о том, как записи sqlite загружаются в запись, которая затем использовалась во всем приложении. Может быть до 200 000 записей, которые потребляют много памяти. Есть ли способ записать запись в файл?

type
TMyDatabase = class(TThread)
private
Owner: TComponent;
FSqldb: TSQLiteDatabase;
FSltb: TSQLIteTable;
.....

type
PMyMessageRec = ^TMyMessageRec;
TMyMessageRec = record
Id: integer;
RcptId: integer;
PhoneNumber: ShortString;
Text: string;
......
end;

procedure TMyDatabase.Execute;
........
begin
...........
FSltb := FSqldb.GetTable('SELECT * FROM Messages ORDER BY ID LIMIT ' + IntToStr(MaximumMessages));
try
Synchronize(SyncLoadAllMessages);
Synchronize(SyncLoadMessages);
finally
FSltb.Free;
end;

procedure TMyDatabase.SyncLoadAllMessages;
var MessRec: PMyMessageRec;
.......
begin
....
while not FSltb.EOF do
Begin
if TerminateAll then exit;
New(MessRec);
MessRec.Id := FSltb.FieldAsInteger(FSltb.FieldIndex['ID']);
MessRec.RcptId := FSltb.FieldAsInteger(FSltb.FieldIndex['RecipientId']);
MessRec.PhoneNumber := FSltb.FieldAsString(FSltb.FieldIndex['RecipientPhone']);
MessRec.Text := FSltb.FieldAsString(FSltb.FieldIndex['Text']);
MessRec.Charset := FSltb.FieldAsString(FSltb.FieldIndex['Charset']);

спросил(а) 2011-09-08T19:18:00+04:00 8 лет, 2 месяца назад
5
Решение
63

"Базы данных в памяти" - это не плохой дизайн как таковой (на рынке есть много продуктов). Объектно-реляционные картотеки и кэши объектов используют эту стратегию для небольших или больших частей базы данных для повышения производительности.

Попытайтесь разделить проблему на две части, чтобы иметь краткосрочное и долгосрочное решение:

в ближайщем будущем

    увеличить доступную память (см. другие ответы) применять шаблоны проектирования, которые уменьшают использование памяти, например, мухи использовать алгоритм кеша объектов (ленивая загрузка/удаление объектов) найти и устранить утечки памяти используйте приложение отслеживания использования FastMM для поиска объектов приложения, которые используют много памяти

долгосрочный

    ввести API уровня сервиса, который скрывает детали реализации списков базовых объектов и изначально просто использует существующие списки объектов, а затем заменяет реализацию уровня сервиса поэтапно SQL-запросами или вызывает ORM с дизайном сервисного уровня приложение также можно разделить на клиент GUI и приложение на стороне сервера (службы)

ответил(а) 2011-09-09T10:33:00+04:00 8 лет, 2 месяца назад
Еще 4 ответа
57

Я бы добавил предложение WHERE, основанное на некоторых критериях, таких как диапазон дат, на исходный запрос, чтобы вы или ваше приложение могли контролировать некоторый размер исходного набора результатов. Я использую DevExpress QuantumGrids, который загружает весь результат запроса в память за большую гибкость и скорость. (Опыт DevExpress потрясающий....) Я помещаю пару элементов управления датами в приложение, с которыми мои пользователи могут взаимодействовать, и устанавливают диапазон StartDate и EndDate для набора результатов. Это позволяет контролировать производительность.

ответил(а) 2011-09-09T02:42:00+04:00 8 лет, 2 месяца назад
56

Это может помочь, но это очень прикладывает прилипающую штукатурку к отрубленной голове!

Переключитесь на 64-битную машину. Сделайте свою программу /LARGEADDRESSAWARE. Переключитесь на FastMM (необходимо сделать шаг 2 из-за ошибок в старой Borland MM).

Затем скрестите пальцы и надейтесь, что 4 ГБ адресного пространства, в отличие от 2 ГБ, достаточно!

ответил(а) 2011-09-08T19:22:00+04:00 8 лет, 2 месяца назад
47

Даже решение, которое вы предлагаете, является "изменением этого подхода". Вы как-нибудь завершите рефакторинг, и лучше использовать механизм SQL, чтобы сделать как можно больше фильтрации.

Вы не описываете используемый алгоритм, но я собираюсь предположить, что вы сейчас это делаете:

Загрузите весь набор данных в какой-то список при запуске.

Попросите пользователя указать критерии фильтра.

Вызовите функцию, которая проходит по критериям фильтра и работает в полном списке, возвращая другой список, который фильтруется и сортируется.

Если это так, вы должны иметь возможность рефакторировать на шаге 3. На шаге 1 просто создайте пустой список (чтобы вы не бросали ошибки в ссылках на этот список). Затем на шаге 3 либо используйте SQL для выполнения всей вашей фильтрации, либо, если это слишком много рефакторинга, выясните, как получить частично отфильтрованный список из SQLite в промежуточный список в памяти, а затем применить существующую сортировку и фильтрацию к этому списку.

Это должно ограничивать ваш рефакторинг одной функцией (предполагая, что я правильно понял общую структуру программы).

ответил(а) 2011-09-08T19:34:00+04:00 8 лет, 2 месяца назад
32

Как другие отметили, что его трудно давать предложения без каких-либо подробностей. Это сказано здесь мои два цента.

Мое предложение было бы использовать профилировщик, чтобы определить, где "горячие точки" и сосредоточиться на них. Вы можете получить бесплатную версию SmartBear.

Вы не указали, с каким списком вы работаете. Если список укоренился в дизайне, вы можете написать оболочку, которая работает как список с точки зрения вызывающего абонента, но внутренне она зависит от параметризованных операторов выбора.

ответил(а) 2011-09-08T21:29:00+04:00 8 лет, 2 месяца назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема