Задача: реализовать загрузку на сайт в каталог данных из csv файла + прикрепление картинок в публичной части, то есть для клиентов, которым не дан доступ в админку.
На данный момент сама по себе задача решена: 1) Файлы csv + картинки загружаются в 1 zip архиве 2) Архив распаковывается 3) Сначала обрабатываются построчно данные из csv файлов 4) Наконец подгружаются кратинки к элементам То есть все работает.
Проблема: в том, что при всем при этом уж больно много запросов возникает даже для небольших файлов с данными.
Собственно вопрос: кто какие способы оптимизации предложит?
На данный момент используется: 1) Для добавления нового элемента ($Element содержит сразу и PROPERTY_VALUES):
Код
$el = new CIBlockElement;
$ID = $el->Add($Element);
2) Для обновления элемента: Сначала для обновления свойств
Код
foreach($ElementProps as $PropCode=>$Value){
CIBlockElement::SetPropertyValues($ElementID, $IBlock["ID"], $Value, $PropCode);
}
этот код вызванный для 9 элементов (по 4-5 свойств у каждого) генерирует ~136 запросов. Затем для обновления самого элемента (и поискового индекса)
Код
$el = new CIBlockElement;
$el->Update($ElementID, $Element);
этот код вызванный для 9 элементов ($Element содержит только NAME, ACTIVE, DATE_ACTIVE_TO) генерирует ~535 запросов (! вообще ужас). На сколько я понял, в основном запросы тратятся на обновление поискового индекса... Так как, если его не обновлять (bUpdateSearch=true), количество запросов падает сразу да нескольких штук. Но ведь обновить его нужно, если элемент изменен. 3) Для подгрузки картинок (отдельно для каждого элемента):
этот код вызванный 5 раз (5 элементов) для загрузки 9 картинок (по 2-3 картинки у некоторых) генерирует ~99 запросов. Итого около 800 запросов для обновления каких-то 9 элементов! А что будет, если подгрузить понадобится 200, 500 позиций?
Важно: используются инфоблоки+, т.е. хранение в отдельных таблицах.
Это ясно - само собой разумеется. Но вопрос я ставил не "как сделать, чтобы работало", а именно "как оптимизировать" - это самое главное, как сократить количество запросов.
У меня такой же вопрос как оптимизировать? Я добавляю 100000 записей из csv файла с помощью функции добавления элемента. За секунду у меня проходит лишь 100 записей. Для проверки сделал то же самое, но через mysql_query() - 10000 записей в секунду. Получается, чтобы добавить в базу, мне нужно ждать 16 минут. Я их буду добавлять каждый день, в дальнейшем у меня будет и 1 000 000 записей и больше.
Помимо этой темы обратился в поддержку продукта, и ответа не последовало.
Поэтому отвечу сам.
1. Добавление ускоряется в 3-4 раза если удалить модуль поиска. Затем добавляю элементы $el->Add($Element) с bUpdateSearch = false. Минусы: - теряем модуль поиска, скорость добавления все равно не устраивает. Сойдет, если добавлять по 50-100 элементов за цикл.
2. Если элементов больше 50000 лучше вообще не использовать добавление битрикса. Выход из ситуации - свои таблицы + mysql_query или чтение из файлов. Это будет работать в десятки и сотни раз быстрее. Лично поверил на 1 000 000 записей.
Александр, я в общем к такому же выводу пришел. А не отвечают судя по всему из-за того, что ответа попросту нет... Так что из решений и остается только: 1) Обязательное использование пошаговости 2) Добавление с отключенным поиском (bUpdateSearch = false) и затем уже переиндексация 3) Всякие самопальные оптимизации, вроде проверок на измененность, чтобы не обновлять то, что не изменилось Так пока и делаю... В итоге на загрузку уходит до нескольких минут. А что делать?
Как насчет использования стандартного скрипта импорта. Вроде у него со скоростью проблем нет. Не реализовывал, но, думаю, из публички его можно дернуть.
Иван, да надо будет попробовать. Только он не совсем удовлетворяет. Либо придется переписывать, либо еще что. Функционал-то у него другой. Разве что подсмотреть, как в нем импорт реализован, и использовать оттуда функции в своем скрипте. Хотя я сомневаюсь, что там будет что-то относительно новое. Ну в общем глянем, когда время будет.
о. сейчас стоит похожая задача, практически идентичная =) пока тоже отталкивался от $el->Add($Element). Элементов пока порядка 2000, даже на таких объемах оптимизация требуется. Была мысль вставлять записи в базу напрямую подготавливая обширные вставки, пока только мысль.
Иван подал хорошую идею посмотреть в сторону стандартного импорта, тоже сейчас буду его изучать.
Иван, в файле импорта (bitrix\modules\iblock\admin\data_import.php) ничего нет, что могло бы ускорить процесс. Быстрее он не будет работать. Тут та же процедура Add(). У меня грузил в базу в 2,5 раза дольше моей обработки с той же процедурой (моя 4 минуты, скрипт - 9 минут)
Дмитрий Олареско, если будешь использовать стандартный импорт, перепроверь! соответствует ли количество элементов в базе. Случается, что скрипт может не добавить элементы, и при этом в отчете ошибки = 0.
NightCat, если найдешь метод - обязательно пиши, это поможет многим
Александр, обидно, что в скрипте ничего нового, но я и раньше писал, что вряд ли в нем что-то есть особенное - как в воду глядел. Тем не менее - молодец, что проверил. Если решение какое-то будет - обязательно поделюсь. Пока просто этим некогда заниматься - оставил так как есть - медленно, но работает. Я имею ввиду загрузка через Add и Update + пошаговый режим, чтобы избежать проблем с большими прайсами.
PS: это я - NightCat... Ник просто сменил... Думал, он у всех сообщений поменяется, а нифига...
Добрый день, столкнулся с той же проблемой, в инфоблоке около 400 тысяч элементов, заливаю csv в ИБ стандартным битриксовским пошаговым импортом (не импортом из Торгового каталога). За шаг (60 секунд) у меня загружается около 80 элементов (строк), причем элементы просто добавляются, те которые были залиты ранее не удаляются и не деактивируются, заливаются только текст, без всяких файлов и картинок. Сервер выделенный с 4х ядерным процессором и 4 гигами оперативы.
Тех.поддержка Битрикса сказала, что в низкой скорости импорта виноват сервер, что якобы не правильно настроен, т.к. показатель "База данных MySQL (запись)" - 1725 очень низкий. Тех.поддержка хостера утверждает, что скорость падает из-за "кривого" скрипта импорта и они тут не причем.
Самое что интересное, если импортировать тот же csv в новый ИБ, то скорость импорта за шаг (60 секунд) 5029 элементов и потом по мере заполнения ИБ элементами скорость снижается.
ИБ хранятся в отдельных таблицах, тип таблиц InnoDB. Тестил импорт в таблицы в MyISAM скорость импорта увеличивается на 10%, примерно.
Подскажите может это уже ограничения какие-то MySQL или еще чего то? Т.е., при ИБ забитым более 400 тыс. элементов ни железо, ни оптимизация скрипта импорта уже не повлияет значительно на скорость импорта?
Предполагаю, что вы не загружаете поле "Уникальный идентификатор B_IBLOCK_ELEMENT.XML_ID). В этом случае добавьте индекс по имени элемента: create index my_iblock_element_name on b_iblock_element(IBLOCK_ID, NAME)
Maxim Smirnov пишет: Предполагаю, что вы не загружаете поле "Уникальный идентификатор B_IBLOCK_ELEMENT.XML_ID).
В этом случае добавьте индекс по имени элемента:
create index my_iblock_element_name on b_iblock_element(IBLOCK_ID, NAME)
Не совсем Вас понял, в мастере импорта выбираю напротив строки "Поле 1 (IE_NAME):" - "Название (B_IBLOCK_ELEMENT.NAME)". А в csv файле в столбце IE_NAME идут значения (прописанные мною) 67000001, 67000002, 67000003 и т.д.
Так я вроде таким образом как раз и импортирую, вот фрагмент csv файла
IE_NAME;IP_PROP322;IP_PROP330;IP_PROP323;IP_PROP324;IP_PROP338;IP_PROP339;IP_PROP390;IP_PROP387;IP_PROP329 67800001;CEK3014;Pegas;Челябинск;Египет;авиаперелет;виза;USD;27748;HARMONY MAKADI BAY HOTEL AND RESORT 5*
А IE_NAME указываю в мастере импорта как Название (B_IBLOCK_ELEMENT.NAME)
Или надо IE_NAME указывать в мастере как Уникальный идентификатор B_IBLOCK_ELEMENT.XML_ID) ?
Индексы сильная вещь, только делали импорт в базу иблоков из файа csv, обновляем по XML_ID. Добавленный индекс только по этому полю, ускорил работу более чем в 100 раз.
Максим такой индекс не повлияет на скорость работы штатных процедур?
Добавил столбец с индесками, в мастере импорта указал этот столбец как Уникальный идентификатор B_IBLOCK_ELEMENT.XML_ID) и скорость импорта действительно возросла в 100 раз, т.е. была 80 элементов за шаг (60 сек), то сейчас около 8500!!!
Дмитрий Якинцев пишет: Скрипт из трех строчек: $strings=file('file.csv'), потом foreach($strings), в нем explode и CIBlockElement add.
1. $strings=file('file.csv') - читаем файл в массив, очень удобно каждая строка - элемент массива 2. foreach($strings) - перебираем эти строки в массиве и на каждой итерации разбиваем строку при помощи explode() 3. и ниже каждое звено строки заносим в метод CIBlockElement add
Сколько строк в csv-файле - столько и элементов в инфоблоке, так же возникнет вопрос, про создание каталогов, не будут же они рядком без каталогов болтаться.
И собственно вопрос:
Конечно же если загрузка из csv, значит - это много элементов, значит проблеммы с нагрузкой на сервер и вот в двух словах - как вы делаете пошаговость?
Артём Курочка пишет: вот в двух словах - как вы делаете пошаговость?
Если в двух словах, делаю так: Пусть имеем файл "3r5g6hty.csv" в какой-то папочке, и его надо импортировать... 1) засекаю в начале выполнения скрипта время старта 2) после каждого CIBlockElement add смотрю, сколько со старта прошло времени, если больше порога (скажем больше 15 сек.), прерываю цикл импорта, запоминаю куда-нибудь, например, в файл "3r5g6hty.csv_" номер строки, на которой остановился 3) перезапускаю скрипт сначала ну и пункт, который на самом деле идет перед пунктом 1 (то есть в начале скрипта импорта): 0) смотрю, нет ли рядом с файлом "3r5g6hty.csv" файла "3r5g6hty.csv_", если нет, импорт идет с 1 строки, если есть, то импорт идет со строки, номер которой указан в файле "3r5g6hty.csv_"
Вот как-то так... На самом деле там еще всякие разные мелочи, можно сделать это все с аяксом, без перезагрузки основной страницы, с прогрессбаром и т.п., но в общем и целом все так Так же можно почаще записывать номер строки, которую уже импортировали, тогда и в случае сбоя скрипта можно будет начать с места сбоя...
Тут ясно, читать каждый раз файл 3r5g6hty.csv_ и допустим там номер строки можно передавать в допустим for(). А вот с аякс- загрузкой как раз интерессно. У меня есть сейчас функционал такой: ссылка, которая отсылает аякс-get-запрос на эту же страницу, получаю параметр и по этому параметру начинается парсинг csv-файла, появляеться div с сообщением "идёт загрузка...", вот и тут есть как раз эти 2 момента
1. Хотелось бы заменить "идёт загрузка..." на прогресс-бар 2. Если файл сильно большой, то делать пошаговую парсингу.
В двух словах можно? как вы перед этим отписали, сразу концепция ясна стала