Форум: Форум PHPФорум ApacheФорум Регулярные ВыраженияФорум MySQLHTML+CSS+JavaScriptФорум FlashРазное
Новые темы: 0000000
Социальная инженерия и социальные хакеры. Авторы: Кузнецов М.В., Симдянов И.В. MySQL на примерах. Авторы: Кузнецов М.В., Симдянов И.В. PHP. Практика создания Web-сайтов (второе издание). Авторы: Кузнецов М.В., Симдянов И.В. MySQL 5. В подлиннике. Авторы: Кузнецов М.В., Симдянов И.В. Объектно-ориентированное программирование на PHP. Авторы: Кузнецов М.В., Симдянов И.В.
ВСЕ НАШИ КНИГИ
Консультационный центр SoftTime

Форум MySQL

Выбрать другой форум

 

Здравствуйте, Посетитель!

вид форума:
Линейный форум Структурный форум

тема: Запись большого массива в БД
 
 автор: *m*   (26.07.2010 в 15:45)   письмо автору
 
 

Всем привет.

Скажите нужно ли как-то предварительно настраивать БД при записи в нее большого массива данных, к примеру 10000 тыс строк?

у меня стоит только:

<?
ini_set
('max_execution_time',0);
?>


Пробовал записать 300, 1700 строк, все записывается, а вот большие файлы, не дают никакого результата. (не записывает ни одной строчки). Начиная где-то с 4000 тыс. строк. Хотя я распечатываю массив и просматриваю его, все ок. Ранее никогда такие большие файлы не приходилось писать..

  Ответить  
 
 автор: Trianon   (26.07.2010 в 16:49)   письмо автору
 
   для: *m*   (26.07.2010 в 15:45)
 

зависит от того, как именно пишете.
В общем случае существует как минимум ограничение на размер пакета обмена клиент-сервер.

  Ответить  
 
 автор: *m*   (26.07.2010 в 16:53)   письмо автору
 
   для: Trianon   (26.07.2010 в 16:49)
 

пишу так:

<?
//$array - массив с данными

array_walk($arraycreate_function('&$s','$s = "(\'" . implode("\',\'", array_map(mysql_real_escape_string, $s)) . "\')";'));
$array implode(',',$array); 
mysql_query("INSERT INTO `sometable` (`name1`, `name2`) VALUES " $array); 
?>



>>>В общем случае существует как минимум ограничение на размер пакета обмена клиент-сервер.
текс.. и где сие ограничение можно расширить и можно ли?

  Ответить  
 
 автор: Trianon   (26.07.2010 в 17:01)   письмо автору
 
   для: *m*   (26.07.2010 в 16:53)
 

то есть всё одним запросом.
Так может стоит попробовать записать отдельными?

  Ответить  
 
 автор: *m*   (26.07.2010 в 17:13)   письмо автору
 
   для: Trianon   (26.07.2010 в 17:01)
 

отдельными вы имеете ввиду для каждого столбца отдельный запрос?

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

  Ответить  
 
 автор: Trianon   (26.07.2010 в 17:21)   письмо автору
 
   для: *m*   (26.07.2010 в 17:13)
 

>отдельными вы имеете ввиду для каждого столбца отдельный запрос?

для каждой строки таблицы.

  Ответить  
 
 автор: *m*   (26.07.2010 в 17:34)   письмо автору
 
   для: Trianon   (26.07.2010 в 17:21)
 

то есть нужно как-то зацикливать, я правильно понимаю?
но тогда нужно менять арх БД. а именно (id) auto_increment убирать?

  Ответить  
 
 автор: Trianon   (26.07.2010 в 17:40)   письмо автору
 
   для: *m*   (26.07.2010 в 17:34)
 

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

  Ответить  
 
 автор: Valick   (26.07.2010 в 17:46)   письмо автору
 
   для: *m*   (26.07.2010 в 17:34)
 

нет ненадо ничего убирать
просто разбейте свой массив 10000 элементов на 10 частей, в итоге получится 10 массивов по 1000 элементов.
и будет у Вас 10 запросов по одному запросу на каждый массив.
___
можно и по-другому, но мне кажется для Вас так будет легче

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

  Ответить  
 
 автор: Valick   (26.07.2010 в 17:22)   письмо автору
 
   для: *m*   (26.07.2010 в 17:13)
 

отдельными вы имеете ввиду для каждого столбца отдельный запрос?
конечно же нет (где валидол)
например по 1000 элементов массива (строк), или сколько там у Вас проходят без ругани со стороны сервера

  Ответить  
 
 автор: *m*   (26.07.2010 в 17:32)   письмо автору
 
   для: Valick   (26.07.2010 в 17:22)
 

Вас не затруднит пример привести? спасибо.

  Ответить  
 
 автор: sim5   (27.07.2010 в 00:07)   письмо автору
 
   для: *m*   (26.07.2010 в 17:32)
 

Вы уже тут, бедолага. ) Какой пример, ведь опять же приходиться говорить о уже сказанном. Ну допустим, после 4000 строк ругается. Но вам то не на 4000 делить данные нужно, а на число кратное числу полей записываемых, которое бы не выходило за пределы 4000.
То есть, если у вас к примеру, строк для записи 39970, вы не можете более 4000 за один запрос записать, а записываете вы 7 полей, то нужно узнать сколько групп/строк для семи полей не будет превышать 4000:
<?
$grp 
floor(4000/7); //571 группа
//всего данных для одного запроса:
$date $grp*7//3997
//на сколько частей разбить массив данных для записи в цикле:
$n ceil(39970/3997); //10

Не удивляйтесь, что так сошлось все, оно и должно быть так - кратно числу полей записываемых, и если будет остаток при делении для $n, то и он должен быть кратен 7 (на одну, две.... строки записи), иначе с входными данными непорядок. Далее уже известное - входной массив делим на 10:
<?
$arr 
array_chunk($arr$n);
//и циклом производим запись
foreach($arr as $val) function getStringForMySQL($val7); 

  Ответить  
 
 автор: Trianon   (27.07.2010 в 00:22)   письмо автору
 
   для: sim5   (27.07.2010 в 00:07)
 

>. Ну допустим, после 4000 строк ругается.

Ну допустим, оно ругается не на 4000 строк, а на определенное в MAX_ALLOWED_PACKET количество даже не символов, а байт.
И всё это можно посчитать, и неприблизительно а предельно точно.
Да только кой смысл это делать, если человек одну строку добавить не могет?


sypex...
сказал бы я...

  Ответить  
 
 автор: sim5   (27.07.2010 в 00:25)   письмо автору
 
   для: Trianon   (27.07.2010 в 00:22)
 

Ну если одну не может, тогда я не знаю. )

PS. Я так полагаю, что пишутся фиксированные данные, почему так четко за неким пределом N отваливается. Хотя посчитать объем конечно можно.

  Ответить  
 
 автор: *m*   (27.07.2010 в 15:31)   письмо автору
 
   для: Trianon   (27.07.2010 в 00:22)
 

почему вы думаете, что я не могу записать строку?
я просто пытался понять, на что вы мне указываете, куда посмотреть.

А если поменять, параметр max_allowed_packet, на более большой, это будет являться нормальным решением?

  Ответить  
 
 автор: Trianon   (28.07.2010 в 02:12)   письмо автору
 
   для: *m*   (27.07.2010 в 15:31)
 

потому что если бы Вы записывали данные по одной строке на запрос, эта ошибка не возникла бы.

  Ответить  
 
 автор: *m*   (27.07.2010 в 15:25)   письмо автору
 
   для: sim5   (27.07.2010 в 00:07)
 

sim5 вы не подумайте, что я ваши слова сквозь уши пропустил и все, наоборот)
я просто тестирую, теперь сей способ. ну а всего знать не возможно, и как вы можете видеть из названия темы, и первого моего поста я пришел сюда с вопросом. И я думал что тут дело в возможно каких-то надстройках БД..

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

  Ответить  
 
 автор: sim5   (29.07.2010 в 06:57)   письмо автору
 
   для: *m*   (27.07.2010 в 15:25)
 

http://dev.mysql.com/doc/refman/5.1/en/packet-too-large.html

В какой момент разделить? А что вы записываете, те данные которые вы в РНР разделе приводили?

  Ответить  
 
 автор: *m*   (29.07.2010 в 12:15)   письмо автору
 
   для: sim5   (29.07.2010 в 06:57)
 

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

то есть наибольшим пакетом, который может быть передан к или от MySQL сервера и клиента, является - 1 GB.
Правильно ли расширять до такого объема max_allowed_packet?
Или правильно все таки делить большой массив в PHP?

  Ответить  
 
 автор: sim5   (29.07.2010 в 12:50)   письмо автору
 
   для: *m*   (29.07.2010 в 12:15)
 

The server's default max_allowed_packet value is 1MB. Вы не то прочли.

Можно все что угодно объеденить, был бы только смысл. В данном случае его нет, ибо нужно проходить циклом разбитый массив на N строк для записи, отдавая его в функцию. Если ваши данные это фактически фиксированные данные, то есть, например, вы записываете, к примеру, 4 поля в таблицу, и все эти четыре поля VARCHAR, при этом вы их ограничили по длине, например 100 символов, то не сложно посчитать число сток записи, которые не будут выходить за пределы установки по умолчанию. Другими словами вы знаете некую постоянную величину, и расчетов практически не потребуется.
Другое дело, если вы, например, записываете эти же четыре поля, но одно из них TEXT, и в это поле поступают данные пусть от textarea, но в одной строке это будет 30 слов, а в другой уже на несколько килобайт, и т.п.. В данном случае объем данных от строки к строке может быть различным и очень, значит нужно будет производить некий расчет и группировать число строк записи в цикле исходя из объема данных.
Коли вы знаете что за данные вам поступают, и это не произвольные по длине данные, а имеют некий максимум, то в этом случае тоже не сложно просто в уме посчитать на какое число строк нужно разбить массив, по максимальному значению. Главное чтобы это число было кратно числу записываемых полей. Далее в цикле отдавайте по частям этот массив для преобразования в строку для многострочной записи, и записывайте эту строку.

  Ответить  
 
 автор: *m*   (29.07.2010 в 13:46)   письмо автору
 
   для: sim5   (29.07.2010 в 12:50)
 

а у меня данные не фиксированные, то есть у меня есть одно поле текстовое, там содержится описание и оно бывает самым разным... перечитал тему вы приводили пример действительно исходя из предположения что данные фиксированные... А как же быть если данные не фиксированные?

  Ответить  
 
 автор: sms-send   (29.07.2010 в 14:14)   письмо автору
 
   для: *m*   (29.07.2010 в 13:46)
 

В цикле, в котором собираете запрос, ставьте проверку, если текущая длина запроса + длина добавляемого куска больше чем max_allowed_packet, то отправляйте собранный запрос в СУБД и продолжайте собирать следующий запрос.
Можно отправлять запрос на вставку каждого ряда отдельно или цель именно в том, чтобы уложить максимум данных в каждый запрос?

  Ответить  
 
 автор: *m*   (29.07.2010 в 16:50)   письмо автору
 
   для: sms-send   (29.07.2010 в 14:14)
 

да нее такой цели нет, я просто хотел использовать пример sim5.
ну вобщем сделал так:


<?
foreach ($data as $index=>$val)
mysql_query("INSERT INTO `data` (`name1`, `name2`) VALUES ('$val[0]','$val[1]')");
?>


видимо об этом мне говорил Трианон, а я его не понимал и продолжал смотреть на пример sim5.
И думал как из примера sim5 можно сделать построчную запись)

  Ответить  
 
 автор: sim5   (29.07.2010 в 18:44)   письмо автору
 
   для: *m*   (29.07.2010 в 16:50)
 

Конечно можно, вы только не поймете одной вещи - на сколько разбить ваш массив, чтобы его N элементов для будующих M строк записи не выходили за пределы установленного в настройках MySQL по умолчанию. И сделать это можно даже на глаз. Ваши данные, это скорее всего результат парсинга html-страниц, а даже в сотни КБ текста трудно найти страницу, а тем более много элементов с одной страницы по сотни КБ. Из этого следует, что вы даже без РНР расчетов можете узнать "безопасное" число строк для записи, а следовательно и разбиение массива на N элементов.
Записывать же массив из многих тысяч строк отдельным запросом для каждой, это ужас.

  Ответить  
 
 автор: *m*   (30.07.2010 в 09:31)   письмо автору
 
   для: sim5   (29.07.2010 в 18:44)
 

понять то я понимаю) ,а вот сделать, к сожалению, пока не могу сам..
а почему ужас? вот например 20 тыс. строк записалось за 30 сек.. или дело в другом?

  Ответить  
 
 автор: sim5   (30.07.2010 в 09:48)   письмо автору
 
   для: *m*   (30.07.2010 в 09:31)
 

Последняя попытка. ) Это просто пример. Он основан на том, что просто по масимальной длине некоего значения для записи получаем количество одновременно записываемых значений (строк значений). Конечно, если одно значение, это 512 байт, например, а остальные по 2 байта, то это получается "расточительный" расчет, но и в этом случае будет выгода. Можно конечно подсчитать все как в бухгалтерии, но это несколько сложнее будет выглядеть. Тут думайте сами. Запись производится исходя из установок сервера. И так:
<?
//формирование строки для MySQL запроса
function getStringForMySQL($a$n$r false) {
  
$a array_chunk($a$n);
  
array_walk($acreate_function('&$s','$s = "(\'" . 
                 implode("\',\'", array_map("mysql_real_escape_string", $s)) . 
                 "\')";'
));
  
$a implode(',',$a);
  if(
$r$a str_replace("'""`"$a);
}

//получаем установки сервера
$q mysql_query("SHOW VARIABLES LIKE 'max_allowed_packet'");
$max mysql_result($q,0,1);

echo 
"Максимальный пакет для записи (установки сервера): " $max " байт<br>";

//не нужно выбирать по максимуму, mysql ругнется, 
//так как не только буковки для записи ему свои отдаем
//поэтому устанавливаем размер пакета для записи
//округляя его кратным 1 МБ
$max -= $max 1000000;

echo 
"Выбираем максимальный пакет для записи: " $max " байт<br>";

//имена полей для записи
$fields = array('name1''name2');
//число полей
$n count($fields);

echo 
"Записываемых полей: " $n "<br>";

//формируем тест-массив
//можете сформировать любого размера для проверки
//но по числу элементов кратных числу $n
//проверте, что будет, если число элементов будет некратным
$a array_fill(064382'Эти функции дают возможность работать с массивами различными способами. 
Массивы очень удобны для хранения, обслуживания и работы с наборами переменных. Поддерживаются одно- 
и многомерные массивы, могут быть даже массивы, создаваемые пользователем.'
);

echo 
"Всего строк для записи: " count($a)/$n '<br>';

//получаем максимальную длину записываемого значения 
$len max(array_map('strlen',$a));
//получаем число элементов массива для одновременной записи
$str floor($max/$len);
//устанавливаем это число элементов кратным числу записываемых полей
if($str $n$str -= $str $n;

//здесь "по N байт каждая....", справедливо только для этого примера
echo "За один запрос будет записано строк: " $str/$n ", по " $len*$n" байт каждая, и общим объемом " .$str*$len" байт<br>";

//разбиваем массив на число одновременно записываемых значений
$a array_chunk($a$str);

echo 
"Всего запросов на запись: " count($a) . '<br><br>';

//можете просмотреть массив в результате
//echo '<pre>';
//print_r($a);

$error false;

//формируем строку имен полей для запроса
getStringForMySQL(&$fields$ntrue);

//производим запись
foreach($a as $val) {
  
//формируем строки записываемых значений для запроса
  
getStringForMySQL(&$val$n);
  
//многострочная запись одним запросом
  
$q "INSERT INTO `test` $fields VALUES $val";
  if(!
mysql_query($q)) {
    
$error mysql_error();
    break;
  }
}

echo !
$error 'Данные записаны успешно!' 'Что-то не срослось! ' $error;

Посмотрите сколько всего строк записывается, и сколько всего запросов к базе выполняется. Вот вам и ответ на ваш вопрос.

PS. Обратите внимание на вызов функции mysql_real_escape_string в функции getStringForMySQL, она взята в кавычки. Именно так и должно быть прописано, ранее я просто упустил из виду это, хотя работать будет, и вы не увидете предупреждения, если они подавляются.

  Ответить  
 
 автор: mihdan   (27.07.2010 в 00:12)   письмо автору
 
   для: *m*   (26.07.2010 в 15:45)
 

Уже несколько лет пользую это чудо - sypex.net, так как phpmyadmin не справляется с большими дампами.

  Ответить  
 
 автор: sim5   (27.07.2010 в 00:21)   письмо автору
 
   для: mihdan   (27.07.2010 в 00:12)
 

А речь разве о РМА идет?

  Ответить  
Rambler's Top100
вверх

Rambler's Top100 Яндекс.Метрика Яндекс цитирования