Производительность SQL-запросов на больших сайтах
Продолжение к этой статье: https://modxclub.ru/topics/tegi-dlya-krupnogo-sajta-2595.html (вынес и расширил комментарий). Информация по поводу производительности SQL-запросов на больших сайтах.
На самом деле чаще всего основная нагрузка происходит в мало кому заметный момент - подсчет количества записей. Вот пример (это не документы, а заявки в CRM-системе, но для примера все равно сгодится, так как на документах и всем остальном все сказанное так же будет справедливо):
Так вот, даже после оптимизации страницы, настройке вторичных ключей и т.п., всего при одном джоине со связью один-к-одному, выборка с подсчетом строк занимает несколько секунд.
Total time: 5.6790 s
Даже оптимизировав getdata-процессор и исключив один запрос из трех, я все равно получаю довольно нерадостный результат.
Total time: 0.6432 s
А вот если я исключу и запрос подсчета количества записей, то результат уже вот такой:
Total time: 0.0036 s
Заметьте, что это еще с учетом выполнения консоли, обработкой данных на уровне PHP и т.п. То есть реально запрос выполняется где-то в районе 0.0003 сек.
Такое поведение связано с тем, что при подсчете строк, если результат базе данных заведомо не известен (к примеру, при джоинах и условиях, когда базе приходится именно читать данные, а не просто "подсмотреть" в индексы), базе приходится читать все запрошенные таблицы целиком, а это самая медленная операция (ибо работа с диском).
В таких случаях приходится думать об оптимизации всего приложения, чтобы запрос количества строк выполнялся только тогда, когда это реально нужно, а не каждый раз.
Кстати, в MODX-процессорах (которые и getdata-процессор наследует), изначально было два полезных метода: prepareQueryBeforeCount() и prepareQueryAfterCount(). В getdata-процессорах изначально был добавлен еще один метод - setSelection(). Хотя он, по сути, дополняет метод prepareQueryAfterCount() и вполне можно обойтись без него, все-таки он по названию как-то боле логичный и традиционно использовался для добавления select-конструкций в основной запрос. Сейчас объясню логику.
Вот возьмем, к примеру, основную таблицу документов. Когда вы просто формируете запрос из одной только его таблицы, даже если вы добавили какие-нибудь условия выборки, если на эти колонки созданы корректные индексы и БД использовала их для запроса, при подсчете количества записей скорее всего реального обращения к диску не будет, то есть кол-во строк БД подсчитает из индексов. В таком случае запрос на подсчет пройдет мгновенно. (Это область действия в рамках методе prepareQueryBeforeCount() и выполняемого за ним запроса на подсчет строк). А вот если вам надо получить данные из дополнительных таблиц (с использованием join-ов), то лучше их добавить уже в методе prepareQueryAfterCount() (или в случае использования getdata-процессоров в метод setSelection(), просто для наглядности). Вот тогда даже при использовании джоинов, запросы пройдут быстрее, так как не будет в этот момент выполняться подсчета строк.