с большим количеством каталогов (порядка тысячи и более),
большим количеством товаров (десятки тысяч и более)
и большим количеством зарегистрированных клиентов (порядка нескольких тысяч и более).
Количество категорий и товаров можно посмотреть в "Админка->Отчеты->Системные данные".
Количество зарегистрированных клиентов в "Админка->Списки->Покупатели",
там умножить число строк (обычно это 20) на число страниц.
Программная часть (скорость выполнения PHP-кода)
================================================
При тысяче (и более) категорий основное время PHP-вычислений занимает построение иерархического списка категорий, который мы видим в блоке "Развернутый каталог" и на странице "Расширенный поиск".
При каждой перезагрузке страницы таблица категорий заново получается SQL-запросом из базы и на нее напускается рекурсивная функция построения списка, которая (при тысяче категорий/подкатегорий) работает долго.
Но зачем каждый раз заново строить список категорий в клиентской части, если изменяется этот список только в админке? Куда правильнее сохранить этот список готовым в файл и брать его каждый раз оттуда, меняя этот файл только из админки.
Примерно так и сделано.
В клиентской части: если файлов с таблицей категорий и построенным списком нет, то все происходит штатным путем (медленно), а таблица категорий и построенный список сохраняются в файлы. Т.е. на следующей странице они будут взяты уже из файлов (быстро).
В админской части: если произошли изменения в таблице категорий, то файлы удаляются и - см.выше.
Можно прямо в админке их и создать заново, но особого смысла я не вижу. Просто у первого клиента после изменений в категориях медленнее загрузится первая страница.
Второй список категорий (для "не развернутого" каталога) не трогался, т.к. строится линейно и быстро.
1. в файле category_functions.php
1.1. в функции catGetCategoryCListMin
вместо
return _recursiveGetCategoryCList( 1, 0, null, 'NUM', false, false);
вставляем
# BEGIN кэшируем в файл клиентский список категорий
#return _recursiveGetCategoryCList( 1, 0, null, 'NUM', false, false);
if (file_exists("core/temp/catcache.txt") && ($cache = file_get_contents("core/temp/catcache.txt")) && $cats = unserialize($cache)) return $cats;
else
{
$cats = _recursiveGetCategoryCList( 1, 0, null, 'NUM', false, false);
file_put_contents("core/temp/catcache.txt",serialize($cats));
return $cats;
}
# END кэшируем в файл клиентский список категорий
1.2. в функции update_psCount в начало вставляем
# BEGIN кэшируем в файл клиентский список категорий
if (file_exists("core/temp/catcache.txt")) unlink("core/temp/catcache.txt");
if (file_exists("core/temp/categories.txt")) unlink("core/temp/categories.txt");
# END кэшируем в файл клиентский список категорий
2. в файле index.php
2.1. перед
$q = db_query("select categoryID, name, products_count, products_count_admin, parent, picture, subcount FROM ".CATEGORIES_TABLE." ORDER BY sort_order, name");
вставляем
# BEGIN кэшируем в файл клиентский список категорий
if (file_exists("core/temp/categories.txt") && ($cache = file_get_contents("core/temp/categories.txt")) && $categories = unserialize($cache))
{
foreach ($categories as $row)
{
$fc[(int)$row["categoryID"]] = $row;
$mc[(int)$row["categoryID"]] = (int)$row["parent"];
}
}
else
{
$categories = array();
# END кэшируем в файл клиентский список категорий
2.2. после
$mc[(int)$row["categoryID"]] = (int)$row["parent"];
вставляем
# BEGIN кэшируем в файл клиентский список категорий
$categories[] = $row;
}
file_put_contents("core/temp/categories.txt",serialize($categories));
# END кэшируем в файл клиентский список категорий
SQL-часть (скорость выполнения запросов)
========================================
У таблиц категорий, товаров и клиентов довольно небольшой список используемых вариантов сортировки и полей для поиска.
Достаточно добавить несколько индексов, чтобы на удивление сильно увеличить скорость выполнения некоторых тормозных запросов.
создаем файл addon.php с содержимым
<?php
db_query("ALTER TABLE ".PRODUCTS_TABLE." ADD INDEX date_added(date_added)"); // home.php - новинки
db_query("ALTER TABLE ".PRODUCTS_TABLE." ADD INDEX items_sold(items_sold)"); // home.php - популярные
db_query("ALTER TABLE ".PRODUCTS_TABLE." ADD INDEX sort_order_name(sort_order,name)"); // стандартная сортировка товара
db_query("ALTER TABLE ".PRODUCTS_TABLE." ADD INDEX price(Price)"); // сортировка по цене товара
db_query("ALTER TABLE ".PRODUCTS_TABLE." ADD INDEX enabled(enabled)"); // включенные-отключенные товары
db_query("ALTER TABLE ".PRODUCTS_TABLE." ADD INDEX name(name)"); // сортировка по названию товара
db_query("ALTER TABLE ".CATEGORIES_TABLE." ADD INDEX sort_order_name(sort_order,name)"); // стандартная сортировка категорий
db_query("ALTER TABLE ".CUSTOMERS_TABLE." ADD INDEX login(Login)"); // для WHERE Login=
unlink("core/includes/admin/addon.php");
?>
Кладем его в core/includes/admin/ и заходим в админку либо перезагружаем страницу, если уже в админке.
Файл добавит индексы и должен удалиться сам, но лучше проверить (мне попадались сайты, где удаление файлов в этом каталоге было запрещено).
В общем случае я искал тормозные запросы следующим образом:
В файле mysql.php в функции db_query
строку
$res["resource"] = mysql_query($s);
заменил на
if (isset($_GET['speedtest'])) global $relaccess;list($usec, $sec) = explode(" ", microtime());$start = (float)$usec + (float)$sec;
$res["resource"] = mysql_query($s);
if (isset($_GET['speedtest'])) if (in_array(100, $relaccess)) {list($usec1, $sec1) = explode(" ", microtime());$f=fopen('sql.txt','a');fwrite($f,round((float)$usec1+(float)$sec1-$start,5)."/".$s."\n");fclose($f);}
При этом если находишься под админом и добавляешь в url строку с GET-переменной speedtest=1 (например, http://badisoft.ru/?speedtest=1), то в корне сайта создастся (либо будет дополняться, если уже есть) файл sql.txt примерно с таким содержимым:
0.00021/select code, currency_value, where2show, currency_iso_3, Name, roundval from emph_currency_types where CID=3 0.0002/select CID, Name, code, currency_value, where2show, roundval, currency_iso_3 from emph_currency_types order by sort_order 0.01132/select categoryID, name, products_count, products_count_admin, parent, picture, subcount FROM emph_categories ORDER BY sort_order, name 0.00056/select bid, title, content, bposition, which, sort, html, url, admin, pages, dpages, categories, products FROM emph_blocks WHERE active=1 ORDER BY sort ASC 0.00021/select count(*) from emph_categories where categoryID=1 0.0002/select categoryID, parent, name FROM emph_categories WHERE categoryID=1 0.00051/select categoryID, parent, name, products_count from emph_categories where parent=1 order by sort_order, name 0.00023/select last_update from emph_dump where type=1 0.00022/select today from emph_counter WHERE tbid=1 0.05832/replace into emph_online values ('97ca250a247dba98302e15d5f127644a', '1453217907') 0.0584/UPDATE emph_counter SET todayp=todayp+0, todayv=todayv+1, allp=allp+0, allv=allv+1, allieb=allieb+0, allmozb=allmozb+1, allopb=allopb+0, allozb=allozb+0, allrusl=allrusl+1, allenl=allenl+0, allozl=allozl+0, allwins=allwins+1, alllins=alllins+0, allmacs=allmacs+0, allozs=allozs+0 WHERE tbid=1 0.00096/select count(*) from emph_online WHERE time > 1453217307 0.00023/select todayp, todayv, allp, allv from emph_counter WHERE tbid=1 0.00019/select poll_id, poll_title, poll_ans, all_poll FROM emph_survey WHERE active=1 [тут кусок лога вырезан для краткости] 0.00024/select settings_value from emph_settings where settings_constant_name='CONF_DATE_FORMAT' 0.00025/select settings_value from emph_settings where settings_constant_name='CONF_DATE_FORMAT' 0.00701/select customerID from emph_customers where Login='admin' 0.00021/select itemID, Quantity FROM emph_shopping_carts WHERE customerID=1 0.0003/select orderID, customerID, order_time, customer_ip, shipping_type, payment_type, customers_comment, statusID, shipping_cost, order_discount, order_amount, currency_code, currency_value, customer_firstname, customer_lastname, customer_email, shipping_firstname, shipping_lastname, shipping_country, shipping_state, shipping_city, shipping_address, billing_firstname, billing_lastname, billing_country, billing_state, billing_city, billing_address, cc_number, cc_holdername, cc_expires, cc_cvv, affiliateID, shippingServiceInfo, currency_round from emph_orders where orderID=0 0.00018/select AID, uri, update_date, title, textToPrePublication, bid from emph_articles order by ordering,update_date DESC,AID DESC 0.00025/select settings_value from emph_settings where settings_constant_name='CONF_DATE_FORMAT'
Что мы видим? Видим кучу запросов по 0.000xx секунд и четыре явно медленных запроса 0.01132, 0.05832, 0.0584, 0.00701.
Первый решается индексом этой таблицы (категорий) по sort_order+name.
Второй и третий - это запросы малюсеньких таблиц (в третьм таблица вообще в одну запись), но с изменением содержимого таблиц, тут был не лучшим образом настроен SQL-сервер.
Четвертый решается индексом этой таблицы (клиентов) по Login.
Вот таким не особенно сложным способом скорость работы сайта была улучшена с
Обработка данных: 0.350 сек Работа с БД: 0.260 сек Общее время работы: 0.665 сек
до
Обработка данных: 0.041 сек Работа с БД: 0.011 сек Общее время работы: 0.056 сек
PS. В процессе улучшений случился также и переезд на другое железо, но, как ни странно, особого прироста производительности это не дало. Хотя "стало в два раза быстрее" - это тоже хорошо.
Наибольший прирост общей скорости работы дали:
1. кэширование на диск списка категорий.
2. настройка MySQL-сервера. Исходно ооочень медленно, на порядок-два хуже SELECT-ов выполнялись запросы, меняющие содержимое таблиц - UPDATE, REPLACE, INSERТ. Что-то связанное с кэшированием изменений и сбросом их на диск. Делал не я. Я в настройках MySQL-сервера ничего не понимаю. Видимо, какая-то специфика развернутого сервера, т.к. у меня все MySQL-сервера стоят с настройками практически по дефолту, но такой разницы между SELECT-ом и UPDATE-ом нет.
3. создание индексов по часто используемым полям у объемных таблиц (категории, товары, клиенты).
PPS. Обязательно надо мониторить в админке раздел "Статистика ошибок" и следить, чтобы там не было записей.
Особенно если в лог валятся не только ошибки, но и warning по несоответствию типа данных, чего даже в штатном shopCMS "из коробки" довольно много.
При появлении error или warning происходит:
1. вставка сообщения в таблицу (INSERT)
2. подсчет записей в таблице (SELECT)
3. усечение таблицы, если записей больше пятидесяти (DELETE)
т.е. при периодическом warning, который никак не влияет на работоспособность сайта мы получим три лишних SQL-запроса по каждому warning, причем два из них могут оказаться не быстрыми.
Некоторый (довольно небольшой) прирост даст убивание сбора статистики по клиентам (файл counter.php, но осторожно, там еще и ежесуточная чистка таблицы сессий).
Просто станет на пять SQL-запросов меньше.
Статистика там все равно довольно бессмысленная (браузеры - IE/FF/Opera/Other, ОС - Win/Lin/Mac/Other, Языки - Ru/En/Other).
Новых браузеров нет, новых ОС нет.
Статистика Яндекса или Гугля (а обычно у сайта есть обе) покажут то же самое намного лучше и развесистей.