Ускорить QoQ или альтернативный подход?
Я строю приложение, которое выполняет основной запрос со многими объединениями. Эти данные запроса затем доступны всему приложению для воспроизведения в глобальной переменной. Запрос обновляет или получает последний набор результатов при каждом обновлении страницы; так что это только в том же состоянии для жизни запроса.
В других частях этого приложения я иногда запускаю сотню QoQ для этих данных - обычно результат рекурсивных вызовов функций. Тем не менее, хотя QoQ - отличная функция, она не слишком быстрая, и иногда загрузка страниц в плохой день может составлять от 3000 до 5000 мс. Это просто недостаточно быстро.
Есть ли какие-либо методы оптимизации, которые я могу сделать, чтобы QoQ работал быстрее или, возможно, альтернативный метод? Я прочитал интересную статью Бена Наделя о функции Duplicate() - есть ли возможность использовать это, и если да, то как?
Я хотел бы услышать ваши мысли.
Не беспокойтесь о сумасшедших предложениях, это личный проект, поэтому я готов рисковать. Я запускаю это на Railo, совместимом с CF8.
Большое спасибо, Майкл.
3 ответа
Без понимания кода и сложности QoQ трудно сказать наверняка лучший подход, однако одну вещь, которую вы можете сделать, это использовать структуру для индексации записей вне QoQ. Большая часть накладных расходов на использование QoQ заключается в создании новых объектов запросов, а использование подхода только для структурной записи намного эффективнее, чем, например, циклическое выполнение исходного запроса и сравнение.
Например:
<!--- build up index --->
<cfset structindex = {} />
<cfset fields = "first,last,company" />
<cfloop list="#fields#" index="field">
<cfset key = "field:#field#,value:#q[field][currentrow]#" />
<!--- initialize each key (instead of using stuctkeyexists) --->
<cfloop query="q">
<cfset structindex[key] = "" />
</cfloop>
<cfloop query="q">
<!--- update each key with list of matching row indexes --->
<cfset structindex[key] = listappend(structindex[key], currentrow) />
</cfloop>
</cfloop>
<!--- save structindex to global variable --->
<!--- output rows matching index --->
<cfset key = "field:company,value:stackexchange" />
<cfoutput>
<cfloop list="#structindex[key]#" index="row">
#q.last[row]#, #q.first[row]# (#q.company[row]#)<br />
</cfloop>
</cfoutput>
Если это не соответствует вашим потребностям, приведите несколько примеров операторов QoQ и количества записей в основном запросе.
Сначала я посмотрю на время, затраченное на основной запрос. Если он может быть кэширован в течение некоторого периода времени и занимает большую часть времени загрузки страницы, я бы его кешировал.
Далее я бы посмотрел на рекурсивные вызовы. Если их можно сделать итеративными, это, вероятно, ускорит процесс. Я понимаю, что это не всегда возможно. Я был бы удивлен, если бы это не было вашей самой большой потерей времени. однако, не зная больше о том, что вы делаете, вам трудно помочь оптимизировать это.
Я мог бы также подумать о написании некоторых из хранимых процедур рекурсивного QoQ на сервере БД, который предназначен для быстрой обработки данных, а также для эффективного нарезания и вырезания. CF - нет - QoQ очень полезны, но не демоны скорости (как вы заметили).
Наконец, я бы искал прямые фильтры, а не использовал QoQ. Скорее, я бы просто запустил цикл над главным запросом в стандартном теге cfoutput и отфильтровал на лету. Это означает, что вы зацикливаетесь на главном запросе один раз, а не на основном запросе один раз и на результате запроса один раз.
Здесь есть два основных решения. Сначала вы могли бы что-то сделать в CF с записями вне QoQ. Я уже опубликовал свое предложение по этому вопросу. Другой - все делать в БД. Один из способов сделать это - использовать подзапрос в качестве временной таблицы. Вы даже можете сохранить оператор sql в глобальной переменной, а затем ссылаться на него в тех же местах, где вы в настоящее время используете QoQ, но делаете реальный запрос к базе данных. Это может звучать медленнее, чем одна поездка в БД, а затем много QoQ, но на самом деле это, вероятно, не будет, если индексироваться эффективно.
select *
from (
#sqlstring#
) as tmp
where company = 'stackexchange'
Я фактически сделал это для системы со сложными критериями как к тем записям, к которым у пользователя должен быть доступ, так и к тому, что они могут фильтровать в этих записях. Использование такого подхода означает, что вы всегда знаете источник внутренних записей, а не пытаетесь гарантировать, что каждый запрос выполняется правильно.
Изменить: На самом деле безопаснее (и, как правило, более эффективно) использовать параметры запросов, когда это возможно. Я обнаружил, что это можно сделать, включив файл оператора SQL...
select *
from (
<cfinclude template="master_subquery.cfm" />
) as tmp
where company = 'stackexchange'