Экономия памяти при выборке больших наборов результатов с помощью PDO

Я написал инструмент для репликации базы данных на PHP. Работает нормально, но есть одна проблема:

Я использую PDO для подключения к различным базам данных, чтобы он не зависел от какой-либо конкретной СУБД, что крайне важно для этого приложения.

Инструмент выполняет некоторый анализ таблиц, чтобы решить, как преобразовать определенные типы и некоторые другие вещи. Затем он в значительной степени выполняет «SELECT * FROM <tablename>», чтобы получить строки, которые необходимо скопировать. Наборы результатов довольно велики (около 50 тыс. строк в некоторых таблицах).

После этого он перебирает набор результатов в цикле while с PDOStatement::fetch();, выполняет некоторое преобразование типов и экранирование, создает оператор INSERT и передает его в целевую базу данных.

Все это прекрасно работает за одним исключением. Извлекая строки из результирующего набора по очереди, процесс PHP продолжает потреблять все больше и больше памяти. Я предполагаю, что PDO хранит уже обработанные строки в памяти до тех пор, пока не будет обработан весь набор результатов.

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

Я НЕ храню данные в переменных PHP! Я держу только одну строку в любой момент для обработки, так что это не проблема.

Теперь к вопросу: есть ли способ заставить PDO не хранить все данные в памяти? Я обрабатываю только одну строку за раз, поэтому нет необходимости хранить весь этот мусор. Я действительно хотел бы использовать меньше памяти на этой штуке.


person selfawaresoup    schedule 02.07.2010    source источник


Ответы (1)


Я полагаю, что проблема возникает из-за сборщика мусора php, так как он не выполняет сборку мусора достаточно быстро.
Я бы попытался получить результаты фрагментами размером row_count, например "SELCT ... LIMIT offset, row_count" в MySQL или "SELECT * FROM (SELECT ...) WHERE ROW_NUM BETWEEN offset AND (offset + row_count)" в ORACLE.
Используя Zend_Db_Select можно генерировать независимые от БД запросы:

$select = $db->select()
    ->from(array('t' => 'table_name'),
        array('column_1', 'column_2'))
    ->limit($row_count, $offset);
$select->__toString(); 
# on MySQL renders: SELECT column_1, column_2 FROM table_name AS t LIMIT 10, 20
person clyfe    schedule 02.07.2010
comment
Возможно, это могло бы помочь, но это слишком специфично для СУБД. Инструмент должен работать с несколькими системами баз данных, и любое неуниверсальное решение слишком подвержено ошибкам. Я также думаю, что разделение запросов на несколько запросов снизит общую производительность. - person selfawaresoup; 02.07.2010
comment
возможно, используйте Zend_Db_Select и создайте независимый от базы данных запрос - person clyfe; 02.07.2010