Всегда ли выполнение оператора занимает память для результирующего набора? - PullRequest
9 голосов
/ 12 ноября 2010

Коллега сказал мне, что выполнение оператора SQL всегда помещает данные в RAM / swap на сервере базы данных. Таким образом, нецелесообразно выбирать большие наборы результатов.

Я думал, что такой код

my $sth = $dbh->prepare('SELECT million_rows FROM table');
while (my @data = $sth->fetchrow) {
    # process the row
}

извлекает результирующий набор построчно, без его загрузки в ОЗУ. Но я не могу найти ссылку на это в DBI или MySQL документах. Как набор результатов действительно создается и извлекается? Работает ли это так же для простого выбора и объединения?

Ответы [ 4 ]

6 голосов
/ 12 ноября 2010

Ваш коллега прав.

По умолчанию модуль perl DBD :: mysql использует mysql_store_result, который действительно читает все данные SELECT и кэширует их в оперативной памяти. Если вы не измените это значение по умолчанию, когда вы выбираете строку за строкой в ​​DBI, он просто читает их из этого буфера памяти.

Обычно это то, что вам нужно, если только у вас нет очень больших наборов результатов. В противном случае, пока вы не получите последние данные из mysqld, он должен держать эти данные готовыми, и я понимаю, что он вызывает блокировку при записи в те же строки (блоки? Таблицы?).

Имейте в виду, современные машины имеют много оперативной памяти. Набор результатов из миллиона строк обычно не имеет большого значения. Даже если каждая строка достаточно велика по 1 КБ, это только 1 ГБ ОЗУ плюс накладные расходы.

Если вы собираетесь обрабатывать миллионы строк больших двоичных объектов, возможно, вам нужен mysql_use_result - или вы хотите ВЫБРАТЬ эти строки по частям с прогрессивным использованием LIMIT x,y.

Подробнее см. Mysql_use_result и mysql_store_result в perldoc DBD::mysql.

6 голосов
/ 12 ноября 2010

Это не так (если речь идет о самом сервере баз данных, а не о клиентских слоях).

MySQL может буферизовать весь набор результатов, но это не обязательно сделано, и если сделано, не обязательно в RAM.

Набор результатов буферизируется, если вы используете встроенные представления (SELECT FROM (SELECT …)), запрос должен быть отсортирован (который отображается как using filesort), или план требует создания временной таблицы (которая отображается как using temporary в плане запроса).

Даже если using temporary, MySQL сохраняет таблицу в памяти, только если ее размер не превышает ограничение, установленное в tmp_table. Когда таблица превышает этот предел, она преобразуется из memory в MyISAM и сохраняется на диске.

Однако вы можете явно указать MySQL для буферизации набора результатов, добавив инструкцию SQL_BUFFER_RESULT к самому внешнему SELECT.

Подробнее см. документы .

3 голосов
/ 12 ноября 2010

Нет, это не так.

База данных не будет содержать строки в оперативной памяти / подкачке.

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

Проверьте это
Итог - это должно быть очень легко протестировать это с использованием только клиента (я не знаю dbi в Perl, возможно, но я сомневаюсь в этом, делать что-то, что заставляет mysql загружать все при подготовке). Во всяком случае ... проверить это:

Если вы действительно выполните подготовку к SELECT SQL_NO_CACHE million_rows FROM table, а затем извлекаете только несколько строк из миллионов. Затем вы должны сравнить производительность с SELECT SQL_NO_CACHE only_fetched_rows FROM table и посмотреть, как это работает. Если результаты сопоставимы (и быстры), то я считаю, что вы можете назвать блефом вашего коллеги.

Также, если вы включите журнал операторов, фактически выданных для mysql, и дадите нам стенограмму этого, то мы (не люди Perl) можем дать более точный ответ о том, что будет делать mysql.

1 голос
/ 12 ноября 2010

Я не очень знаком с этим, но мне кажется, что DBD :: mysql может извлекать все заранее или только по мере необходимости, основываясь на атрибуте mysql_use_result.Обратитесь к документации DBD :: mysql и MySQL.

...