Еще раз повторую. Выбирать надо одну запись. И никакая память не переполнится. Я понимаю, что жертвам ОРМ-ов трудно это понять, но ты уж попробуй осилить
Jodes: Обычно такие задачи стоят ТОЛЬКО перед сервис-скриптами. Ну например, надо базу текстов песен переложить на эластик поиск, для этого хочешь не хочешь придется вытащить все записи, и тащить порциями зачастую единственный вариант.
Но если вся таблица с овер 10к записями тащится через орм, то это уже нездоровая фигня.
Nc_Soft: Это как раз и есть сервис-скрипт, задача которого взять данные из одной базы и унести в другую. Данных много, sql-файлами не выйдет, ибо разные СУБД.
Спасибо за пример, то что нужно.
Вы часом "родом" не с небезызвестного конструктора мобильных сайтов? Сильно уж ник похож :)
Любой вменяемый сервис скрипт трати памяти ровно столько, сколько занимает ОДНА строка в базе. Десятки лет тысячи программистов работают с базами данных, объем которых в миллионы раз превышает разумный объем памяти, необходимый скрипту для работы. И только для пользователей несчастного похапе такая задача становится неодолимым препятствием, которое приходится натужно, скрипя извилинами, преодолевать, изобретая уродливые велосипеды.
FanatPHP: Ну т.е. вы предлагаете нагнуть СУБД миллионом запросов в цикле таща записи по одной и ждать пока код отработает до второго пришествия Христа?
Jodes: весь инернет засран классическим примером while ($row = $res->fetch()) { процессим строку }. Если ты мне объяснишь, где этот код умудрится сожрать памяти больше, чем занимает одна строка, я с удовольсвтием послушаю
Jodes: этот код обрабатывает запрошенные из базы записи. Время выполнения прямо пропорционально размеру базы. Сделать так, чтобы с увеличением размера базы время исполнения уменьшалось можно, но не в нашей вселенной. А вот еще больше увеличить его можно, например выполняя этот запрос в цикле
Ты совершенно не читаешь, что тебе пишут. Про запрос в цикле я тебе уже писал. WHERE кстати в твоем запросе лишнее. Но это все детали. Ты почему-то главного никак понять не можешь. при том что это главное составляет основу основ баз данных. Запрошенные данные любое API читает по одной строке. И чтобы превысить совершенно мизерный объем памяти, надо обладать ну совсем кривыми руками.
FanatPHP: Уважаемый, where там не лишнее от слова "совсем".
Выборка построчно циклом, как в вашем примере, это не выход. Абсолютно не выход.
За сим давайте закончим.
FanatPHP: Вот ты меня озадачил. Я наконец понял что ты имел в виду так называемый datareader, но у меня не получается реализовать на pdo, не подскажешь как?
вот вариант "в лоб" grab.by/D6Ge
вот покумекал с курсором grab.by/D6Hu
ну а вот вариант с батчами по 10к grab.by/D6IQ
в таблице около 230к записей
Jodes: ахахахахах! Вот это-то и есть самое смешное :) Как будто твой способ с запросами в цикле хоть чем-то отличается от "китайской пасхи". Тебе реально надо понять буквально две базовые вещи про базы данных: выборка всегда идет по одной строке - и неавжно, сколько строк ты запросил. Выборка с лимитом в цикле выбирает одни и те же данные столько раз, сколько оборотов в цикле.
FanatPHP: Но как бы вам не смешно было, выборка с лимитом отработала за 3 минуты. А ваш вариант работает вот уже как 20 минут.
И вот это уже действительно смешно.
Jodes: это быстрее выполняется чем с лимитами
<?php
$db = new PDO("mysql:host=localhost;dbname=dwl", 'root', 'root');
$db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
$q = $db->prepare("SELECT * FROM files ORDER BY id_file ASC");
$q->execute();
while ($row = $q->fetch()) {
echo "$row[id_file] \n";
}
Jodes: ты пойми, что ты сейчас споришь не со мной, а со своими неверными представлениями. Твои запросы в цикле будут точно так же выбирать по 1 записи. База данных так работает, она по-другому не умеет. Поэтому повторяя запросы в цикле ты сделаешь что угодно, но только не ускоришь свои выборки. Чтобы сделать лимит 1000000, 10000, база должна сначала выбрать 100000 записей, и только потом начать отдавать тебе данные. Чем больше у тебя значение оффсета, тем дольше у тебя будет работать скрипт. Поэтому я и смеялся над твоими словами про китайскую пасху. Не потому что я такой злобный карлик, а потому что все ровно наоборот. Даже если одним запросом долго, то в цикле будет еще дольше
Jodes: факты надо уметь интерпретировать. И отличать факты от неверных наблюдений. К примеру многие начинающие пользователи похапе искренне считают фактом то, что у них "база данных переводы строк съела".
Nc_Soft: Протестировал. 2 ядра по 92% загрузки, 121 метр памяти. работает уже 6 минут.
М.б. у меня хост такой особенный, что я в упор не вижу ожидаемого прироста производительности?
FanatPHP: с этим способом есть проблемы...
Например, если мне надо процессить таблицу и заносить туда данные что я обработал строчку.
Такой вариант совсем не канает (LIMIT 1000 стоит чтобы долго не ждать)
<?php
$db = new PDO("mysql:host=localhost;dbname=dwl", 'root', 'root');
$db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
$q = $db->prepare("SELECT * FROM files ORDER BY id_file ASC LIMIT 1000");
$q->execute();
while ($row = $q->fetch()) {
echo "$row[id_file] \n";
$db->prepare('update files set `time` = NOW() where id_file = ? ')->execute([$row['id_file']]);
}
?>
данные не обновляются, потому что как оказалось при unbuffered query нельзя выполнять другие запросы в этом же коннекте.
А вариант с отдельным коннектом $db1
<?php
$db = new PDO("mysql:host=localhost;dbname=dwl", 'root', 'root');
$db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
$q = $db->prepare("SELECT * FROM files ORDER BY id_file ASC LIMIT 1000");
$q->execute();
$db1 = new PDO("mysql:host=localhost;dbname=dwl", 'root', 'root');
while ($row = $q->fetch()) {
echo "$row[id_file] \n";
$db->prepare('update files set `time` = NOW() where id_file = ? ')->execute([$row['id_file']]);
}
?>
привносит какой-то коллапс, видимо табла лочится, запросы ставятся в очередь, там обновилось всего около сотки строчек и выдало ошибку
Warning: Empty row packet body in /Users/evgenij/projects/snip/fetch.php on line 8
PHP Warning: Empty row packet body in /Users/evgenij/projects/snip/fetch.php on line 8
там в цикле $db1->prepare('update files set `time` = NOW() where id_file = ? ')->execute([$row['id_file']]);
и оно висит наверное лок таймаут ждет grab.by/D6RU
Jodes: имел печальный опыт, мало памяти - увеличивайте память. В противном случае будете иметь проблемы с изменениями данных между запросами пачек на посещаемом сайте. При добавлении новых строк будете считывать одни и теже в соседних пачках. При удалении - некоторые вообще не увидите.
Jodes: не надо. Я имею опыт работы с базами данных отличающийся от опыта среднего пользователя похапе. И даже имею некоторую теоретическую подготовку, согласно которой работа с БД большого объема не представляет каких-то особенных требований в плане потребляемой оперативаной памяти.
FanatPHP: смотря как работать. Если человек написал, что его беспокоит память, значит так оно и есть. А съесть ее можно всегда ... Будут основания для изменения алгоритма - изменит сам.
Jodes: Так я наставляю уже битый час. Не надо никаких лимитов. Сделай один запрос и обрабатывай свои данные спокойно. Управишься на порядок быстрее, чем заставляя базы выбирать одни и те же данные 100500 раз
Jodes: человек старается объяснить, что для перекладывания записей из одного места в другое не нужно хранить всю пачку единовременно. Вы же о своем алгоритме пишете так, что не ясно зачем ОБЯЗАТЕЛЬНО так делать. Вот и идет "дискуссия" ни о чем. Пачка нужна, если есть какой то алгоритм помимо перекладывания, требующий рассмотрения нескольких записей и это нельзя сделать в один проход (при том самом пресловутом переборе по одной записи). Это что касается PHP.
А на стороне СУБД болячку не обойти - там будет строиться результирующий набор и сожрет он столько памяти, сколько будет выбрано строк.
На стороне PHP можно только не увеличить вдвое требуемый объем памяти.
Чего тогда спор - вот вопрос ...
Jodes: ответ зависит от блокировок.
Если таблицу не блокировать, то разницы для PHP никакой, что все сразу выбрать, что пачки перебирать.
А вот для СУБД разница есть определенно. Сложность запроса одна и таже, СУБД вынужден делать выборку каждый раз полностью и из нее вычленять диапазон.
Все эти нарезания нужны только если если особый алгоритм (какой - трудно представить, нужно знать задачу), либо идет борьба с блокировками (но там могут быть проблемы, я уже написал о них).
FanatPHP: за инфо спасибо (правда, сходу в mysqi не нашел, есть только в mysql). Тогда свой код в PHP может удвоить или утроить потребность в памяти. Если говорить о MySQL, то он в своей памяти все равно хранит результат и он зависит от количества строк. Так что фокусы в PHP решают не все, нужно понимать что и из какой таблицы берешь ...
FanatPHP: это я видел (сейчас узрел, что примеры по mysqli там есть), уговорить сам MySQL не делать буферизацию это не поможет. Так что вопрос о памяти имеет право на жизнь.