Ускорить 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'
Другие вопросы по тегам