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

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

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

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

Спасибо за ответ.Вот некоторые фрагменты кода о том, как записи 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']);

Ответы [ 5 ]

3 голосов
/ 09 сентября 2011

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

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

  1. кратковременное

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

    • представляют API уровня сервиса, который скрывает детали реализации базового объектасписки и первоначально просто использует существующие списки объектов, затем постепенно заменяет реализацию уровня обслуживания на запросы SQL или вызовы ORM
    • wiПри проектировании сервисного уровня приложение также можно разделить на клиентское приложение с графическим интерфейсом и приложение на стороне сервера (службы)
2 голосов
/ 09 сентября 2011

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

2 голосов
/ 08 сентября 2011

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

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

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

1 голос
/ 08 сентября 2011

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

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

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

  2. Пользователь может указать критерии фильтра.

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

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

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

0 голосов
/ 08 сентября 2011

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...