带通配符的Active Directory查询的性能较差 [英] Active directory query with wildcards has poor performance

查看:93
本文介绍了带通配符的Active Directory查询的性能较差的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在用C#编写一种方法,该方法应查询Active Directory并找到显示名称为 {displayName} 格式的所有用户和组(通配符搜索同时包含前导和尾随通配符),方法将用于自动填充字段.

I am writing a method in C# which should query Active Directory and find all users and groups with a display name of the format {displayName} (wildcard search with both leading and trailing wildcard), the method will be used for an autocomplete field.

问题是我编写的方法的性能真的很差,尝试查询AD花费的时间在30秒到一整分钟之间,具体取决于查询字符串.

The problem is the performance of the method I wrote is really poor, attempting to query AD takes anything between 30 seconds and a full minute, depending on the query string.

我组织的广告非常大,但是如果花费这么长时间,则自动完成字段将毫无意义.

My organization's AD is very large but if it takes this long the autocomplete field will be pointless.

这是我现在正在使用的代码:

Here is the code I am using right now:

// Intialize the results list.
result.queryResult = new List<Classses.ADSearchObject>();

// Set up domain context.
PrincipalContext pc = new PrincipalContext(ContextType.Domain, Domain, Constants.adQueryUser, Constants.adQueryPassword);

// Set up a directory searcher.
DirectorySearcher dSearcher = new DirectorySearcher();
// Define a SearchCollection to store the results.
SearchResultsCollection searchCol;
// Define returned result paging for performance.
dSearcher.PageSize = 1000;
// Define the properties to retrieve
dSearcher.PropertiesToLoad.Add("sAMAccountName");
dSearcher.PropertiesToLoad.Add("displayName");
// Define the filter for users.
dSearcher.Filter = $"(|(&(displayName = {result.querystring}*)(objectCategory=person))(&(displayName=*{result.querystring})(objectCategory=person)))";

// Search based in filter and save the results.
searchCol = dSearcher.FindAll();

// Add the results to the returned object 
foreach (SearchResult searchResult in searchCol)
{
   DirectoryEntry de = searchResult.GetDirectoryEntry();
   // Code to get data from the results...
}

// Define the filter for groups.
dSearcher.Filter = $"(|(&(displayName={result.querystring}*)(objectCategory=person))(&(displayName=*{result.querystring})(objectCategory=person)))";

// Search based in filter and save the results.
searchCol = dSearcher.FindAll();

// Add the results to the returned object 
foreach (SearchResult searchResult in searchCol)
{
   DirectoryEntry de = searchResult.GetDirectoryEntry();
   // Code to get data from the results...
}

当前,搜索分为用户和组,以便于区分它们,但是如果能够显着提高性能,我将把它们统一为一个搜索.

Currently the search is divided to users and groups to make it easy to distinguish between them but if it increases performance substantially I will unify them to a single search.

根据用户的建议,我使用了Stopwatch来检查FindAll所花费的时间,并且还检查了foreach循环所花费的时间.

As the user rene suggested, I used a Stopwatch to check the time it takes for FindAll and I also checked how long my foreach loops take.

我发现FindAll调用大约需要100毫秒(非常快),即使使用AD索引的前导通配符(不是通配符)进行搜索也是如此.

I found out that the FindAll calls take about 100ms (very fast) even when searching with a leading wildcard (which isn't) indexed by AD.

显然,花费时间最长的是我的foreach循环,大约需要40秒(40,000毫秒).

Apparently the calls that take longest are my foreach loops which take about 40 seconds (40,000ms).

由于我还没有弄清楚如何提高其性能,因此我正在用foreach循环中的代码块更新问题:

I am updating the question with the code block in my foreach loops as I haven't figured out how to improve its performance:

// --- I started a stopwatch here
foreach (SearchResult searchResult in searchCol)
{
   // --- I stopped the stopwatch here and noticed it takes about 30,000ms
   result.code = 0;

   DirectoryEntry de = searchResult.GetDirectoryEntry();

   ADSearchObject adObj = new ADSearchObject();

   adObj.code = 0;

   if (de.Properties.Contains("displayName")
   {
        adObj.displayName = de.Properties["displayName"].Value.ToString();
   }

    adObj.type = "user";

    result.queryResults.Add(adObj);
}

请注意,在更新后的代码中我在哪里启动和停止了"Stopwatch",我不知道为什么开始循环需要这么长时间.

Note where I started and stopped my 'Stopwatch' in my updated code, I don't know why beginning the loop takes so long.

推荐答案

当然,对于唯一值,子字符串匹配比等式匹配花费更高.同样也不会感到惊讶的是,经过时间的大部分都落在了迭代器块中,根据您的分析,该块总共消耗40s.

Of course, a substring match is more costly than an equality match for a unique value. Also it doesn't surprise the lion's share of elapsed time falls into your iterator block, which consumes 40s overall according to your profiling.

如果您确信仅通过设置迭代器就会导致性能大幅下降,那么我不是-那是因为您选择了时间点.

If you are convinced that a huge drop in performance occurs just by setting up an iterator, I'm not - and that's because of your choice of timing points.

StartClock("foreach");
foreach (SearchResult searchResult in searchCol)
{
    // use an empty block to speed things up or
    StopClock("foreach");
    // whatever
    RestartClock("foreach");
}
StopClock("foreach");
LogClock("foreach");

如果您注意我已经评论过的最佳实践,我希望性能提高(对于大条目数):将单个请求发送到服务器,以接收搜索结果中需要的所有内容,而不发送其他请求要求每个项目.虽然对GetDirectoryEntry()的单个调用仅消耗<1毫秒,但大量的条目将使您的代码对您的应用程序自动完成功能毫无用处.

I expect a huge performance gain (for large entry numbers) if you pay heed to a best practice I already commented on: Send a single request to the server recieving all you need in your search result, and don't send another request for each item. While a single call to GetDirectoryEntry() will only consume <1ms, the large number of entries will make your code useless for your application autocompletion feature.

为@rene表示感谢,以表示该过滤器表达式的正常形式.我不了解Active Directory中的过滤器优化,因此我会使用

Kudos to @rene for presenting a normal form for that filter expression. I don't know about filter optimization in Active Directory, so I would take the sure path with

(&(objectCategory=person)(displayName=*{result.querystring}*))

这篇关于带通配符的Active Directory查询的性能较差的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆