Как ускорить получение данных из .NET AD в ColdFusion?
-
01-07-2019 - |
Вопрос
Как я могу оптимизировать следующий код, который в настоящее время занимает более 2 минут для извлечения и перебора более 800 записей из пула, состоящего из более чем 100 тысяч записей, возвращая 6 полей на запись (добавляет примерно 20 секунд на каждое дополнительное поле):
<cfset dllPath="C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\System.DirectoryServices.dll" />
<cfset LDAPPath="LDAP://" & arguments.searchPath />
<cfset theLookUp=CreateObject(".NET","System.DirectoryServices.DirectoryEntry", dllPath).init(LDAPPath) />
<cfset theSearch=CreateObject(".NET","System.DirectoryServices.DirectorySearcher", dllPath).init(theLookUp) />
<cfset theSearch.Set_Filter(arguments.theFilter) />
<cfset theObject = theSearch.FindAll() />
<cfloop index="row" from="#startRow#" to="#endRow#">
<cfset QueryAddRow(theQuery) />
<cfloop list="#columnList#" index="col">
<cfloop from="0" to="#theObject.Get_Item(row).Get_Properties().Get_Item(col).Get_Count()-1#" index="item">
<cftry>
<cfset theQuery[col][theQuery.recordCount]=ListAppend(theQuery[col][theQuery.recordCount],theObject.Get_Item(row).Get_Properties().Get_Item(col).Get_Item(item),"|") />
<cfcatch type="any">
</cfcatch>
</cftry>
</cfloop>
</cfloop>
</cfloop>
Решение
Насколько велик список элементов для внутреннего цикла?
Переключение на массив мощь быть быстрее, если имеется значительно большое количество элементов.
Я реализовал это вместе с предложениями x0n...
<cfset dllPath="C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\System.DirectoryServices.dll" />
<cfset LDAPPath="LDAP://" & arguments.searchPath />
<cfset theLookUp=CreateObject(".NET","System.DirectoryServices.DirectoryEntry", dllPath).init(LDAPPath) />
<cfset theSearch=CreateObject(".NET","System.DirectoryServices.DirectorySearcher", dllPath).init(theLookUp) />
<cfset theSearch.Set_Filter(arguments.theFilter) />
<cfset theObject = theSearch.FindAll() />
<cfloop index="row" from="#startRow#" to="#endRow#">
<cfset Props = theObject.get_item(row).get_properties() />
<cfset QueryAddRow(theQuery) />
<cfloop list="#columnList#" index="col">
<cfset CurrentCol = Props.getItem(col) />
<cfset ItemArray = ArrayNew(1)/>
<cfloop from="0" to="#CurrentCol.getcount() - 1#" index="item">
<cftry>
<cfset ArrayAppend( ItemArray , CurrentCol.Get_Item(item) )/>
<cfcatch type="any">
</cfcatch>
</cftry>
</cfloop>
<cfset theQuery[col][theQuery.recordCount] = ArrayToList( ItemArray , '|' )/>
</cfloop>
</cfloop>
Другие советы
Давно я не касался CF, но могу дать некоторые подсказки в псевдокоде.Во-первых, это выражение крайне неэффективно:
#theObject.Get_Item(row).Get_Properties().Get_Item(col).Get_Count()-1#
Возьмем, к примеру, первую часть: Get_Item(row) — ваш код заставляет CF извлекать строку и ее свойства для каждой итерации цикла #columnList#;и в довершение всего, вы делаете это ДВАЖДЫ за итерацию списка столбцов (один раз для цикла и еще раз для внутреннего cfset).Если подумать, ему нужно только получить строку для каждой итерации внешнего цикла (от #sfstart# до #cfend).Итак, в псевдокоде сделайте следующее:
для каждой строки между началом и концом
cfset props = #theobject.get_item(row).get_properties()#
для каждого столбца в #columnlist#
cfset currentcol = #props.getitem(col)#
cfset count = #currentcol.getcount() - 1#
по каждому элементу от 0 до #count#
cfset #currentcol.getItem(item)# и т.д...
Имеет смысл?Каждый раз, когда вы входите в цикл, кэшируйте объекты, которые будут повторно использоваться в этой области (или дочерних областях), в переменной.Это означает, что вы захватываете объект столбца только один раз за итерацию цикла столбца.Все переменные, определенные во внешних областях, доступны во внутренних, как вы можете видеть из того, что я сделал выше.Я знаю, что заманчиво вырезать и вставлять предыдущие строки, но не делайте этого.В конце концов это только навредит тебе.
надеюсь это поможет,
Ойсин
Кроме того, использование блока cftry в каждом цикле, вероятно, немного замедлит этот процесс.Если вы не ожидаете, что отдельные строки потерпят неудачу (и вам нужно продолжить с этого момента), я бы предложил один блок try/catch для всего процесса.Try/catch — дорогостоящая операция.
Я думаю, вам стоит прекратить выполнять так много вычислений внутри ваших циклов и вместо этого использовать переменные для хранения счетчиков, указателей на объект col и для хранения строки разделения канала до тех пор, пока вы не будете готовы передать объект запроса. .Если я выполнил рефакторинг правильно, вы заметите улучшение, если воспользуетесь приведенным ниже кодом:
<cfloop index="row" from="#startRow#" to="#endRow#">
<cfset QueryAddRow(theQuery) />
<cfloop list="#columnList#" index="col">
<cfset PipedVals = "">
<cfset theItem = theObject.Get_Item(row).Get_Properties().Get_Item(col)>
<cfset ColCount = theItem.Get_Count()-1>
<cfloop from="0" to="#ColCount#" index="item">
<cftry>
<cfset PipedVals = ListAppend(PipedVals,theItem.Get_Item(item),"|")>
<cfcatch type="any"></cfcatch>
</cftry>
</cfloop>
<cfset QuerySetCell(theQuery,col) = PipedVals>
</cfloop>