如何在ColdFusion中加快从.NET AD的数据检索?
我如何优化以下代码,当前从超过10万条记录的池中检索和循环遍历800多个记录需要花费2分钟的时间,每条记录返回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>
解决方案
回答
自接触CF以来已经有很长时间了,但是我可以用伪代码给出一些提示。一方面,此表达式非常无效:
#theObject.Get_Item(row).Get_Properties()。Get_Item(col).Get_Count()-1#
以第一部分为例,代码使Get_Item(row)使CF能够针对#columnListloop的每次迭代检索行及其属性;最重要的是,我们要在columnlist的每次迭代中执行两次TWICE(一次用于循环,另一次用于内部cfset)。如果我们考虑一下,它只需要为外部循环的每次迭代检索行(从#sfstart到#cfend)。因此,用伪代码执行此操作:
for each row between start and end cfset props = #theobject.get_item(row).get_properties()# for each col in #columnlist# cfset currentcol = #props.getitem(col)# cfset count = #currentcol.getcount() - 1# foreach item from 0 to #count# cfset #currentcol.getItem(item)# etc...
有道理?每次我们进入循环时,都将在该作用域(或者子作用域)中将重用的对象缓存在变量中。这意味着我们每次在列循环的迭代中只获取一次列对象。外部作用域中定义的所有变量都可以在内部作用域中使用,正如我们在上面所做的操作中所看到的那样。我知道它很想从以前的行中剪切和粘贴,但是事实并非如此。最后只会伤害你。
希望这可以帮助,
爱信
回答
此外,在每个循环中使用cftry块可能会大大降低此速度。除非我们期望单个行失败(并且我们需要从这一点继续),否则我建议对整个过程使用一个try / catch块。尝试/捕获是一项昂贵的操作。
回答
内循环的项目列表有多大?
如果存在大量项目,则切换到阵列可能会更快。
我已经与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>
回答
我认为我们想停止在循环内进行大量评估,而是使用变量来保存计数,指向col对象的指针并保存pipe-delim字符串,直到准备好提交给查询对象。如果我正确地完成了重构,那么使用以下代码应该会注意到一个改进:
<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>