我们的应用程序有一个过程,它从Active获取所有用户,并用它们的信息更新相关的SQL表。这个过程是在几年前编写的--所以它是可以工作的遗留代码,“如果它没有损坏,就不要修复它”。然而,我们在应用程序中引入了一个新特性,它需要对此代码进行修改,而且由于它已经多年没有被修改过,所以我想我最好把它清理一下。
该进程只在夜间运行,除非出现罕见的服务器故障,在这种情况下,我们必须在白天手动运行。这个过程使用好的旧System.DirectoryServices库来完成它的工作,尽管它可以工作,但是运行非常慢。
我考虑使用更新的System.DirectoryServices.AccountManagement库,所以我开始重写整个过程(几百行代码),我惊讶地看到PrincipalSearcher 显着地超过了DirectorySearcher。
我一直试图找出原因,然后遇到了the following SO answer,它给出了两者之间的比较,说明DirectorySearcher应该比PrincipalSearcher更快。
我启动了一个测试项目,以确保我没有产生幻觉:
class Program
{
static void Main(string[] args)
{
// New stuff
var context = new PrincipalContext(ContextType.Domain, "mydomain.com");
var properties = new[] { "cn", "name", "distinguishedname", "surname", "title", "displayname" };
var i = 0;
var now = DateTime.Now;
new Thread(delegate()
{
while (true)
{
Console.Write("\r{0} ms, {1} results", (DateTime.Now - now).TotalMilliseconds, i);
Thread.Sleep(1000);
}
}).Start();
using (var searcher = new PrincipalSearcher(new UserPrincipal(context)))
{
var underlying = searcher.GetUnderlyingSearcher() as DirectorySearcher;
underlying.PageSize = 1000;
underlying.PropertiesToLoad.Clear();
underlying.PropertiesToLoad.AddRange(properties);
underlying.CacheResults = false;
using (var results = searcher.FindAll())
{
foreach (var result in results)
{
i++;
}
}
}
Console.WriteLine("It took {0}", (DateTime.Now - now).TotalMilliseconds);
now = DateTime.Now;
i = 0;
// Old stuff
var root = new DirectoryEntry("LDAP://DC=mydomain,DC=com");
var filter = "(&(objectCategory=user)(objectClass=user))";
using (var searcher = new DirectorySearcher(root, filter, properties))
{
searcher.PageSize = 1000;
searcher.CacheResults = false;
using (var results = searcher.FindAll())
{
foreach (var result in results)
{
i++;
}
}
}
Console.WriteLine("It took {0}", (DateTime.Now - now).TotalMilliseconds);
}
}对大约1000个用户进行查询,结果是使用PrincipalSearcher的每个用户约0.9ms (34k用户约30秒),使用DirectorySearcher的每个用户约5.2ms (34k用户约为2分30秒)-- PrincipalSearcher快6倍。
我试着调试和比较PrincipalSearcher的底层DirectorySearcher和我创建的底层DirectorySearcher,它们看起来非常相似。
我试着进一步研究,如果我使用PrincipalSearcher底层搜索器中的搜索根,那么我创建的DirectorySearcher实际上优于PrincipalSearcher。
// ...
DirectoryEntry psRoot;
using (var searcher = new PrincipalSearcher(new UserPrincipal(context)))
{
var underlying = searcher.GetUnderlyingSearcher() as DirectorySearcher;
psRoot = underlying.SearchRoot;
// ...
}
// ...
using (var searcher = new DirectorySearcher(psRoot, filter, properties))
{
// ...
}在调试过程中,我发现搜索根在很大程度上是相同的--也就是说,它们代表相同的域。
是什么原因导致搜索速度像这样减慢?
发布于 2017-07-27 17:55:50
在写这个问题时,我正在修改测试代码,并设法找到了这个问题。通过在构建根DirectoryEntry时提供域地址
// var root = new DirectoryEntry("LDAP://DC=mydomain,DC=com");
var root = new DirectoryEntry("LDAP://mydomain.com/DC=mydomain,DC=com");使用DirectorySearcher进行的搜索优于PrincipalSearcher的搜索。我不太清楚为什么--也许这与搜索者查找结果的位置有关--但它确实提高了搜索速度。
发布于 2018-07-11 17:27:06
看看my question and answer on the differences between the two methods。PrincipalSearcher只是DirectorySearcher的包装器。它的目的是使使用Active Directory更容易,同时提供一些自动化的速度增强。DirectorySearcher可以比PrincipalSearcher快得多,但它需要更多的工作。
从“旧东西”代码中看到缓慢行为的主要原因是,当您在“新东西”中使用PrincipalSearcher时,您得到了底层的DirectorySearcher并提供了它的PropertiesToLoad集合。你不是在你的“旧东西”代码里这么做的。
var properties = new[] { "cn", "name", "distinguishedname", "surname", "title", "displayname" };
//...
var underlying = searcher.GetUnderlyingSearcher() as DirectorySearcher;
//...
underlying.PropertiesToLoad.AddRange(properties);结果,您的“老东西”代码为匹配的结果提取了每个AD属性(即,大量传输的数据),而使用PrincipalSearcher的实现只读取6个属性。
在使用PrincipalSearcher时这样做通常也是不必要的,因为它自己处理缓存和属性的选择。实际上,在使用PrincipalSearcher时,您唯一需要获得的底层DirectorySearcher是设置PageSize,因为PrincipalSearcher没有提供一种标准的方法来设置它。
我怀疑您在指定域时看到了改进的原因是它不需要做任何工作来确定域名。在这方面,你不公平地给了“新东西”一个先机,因为你在启动时钟之前就做了PrincipalContext。
// New stuff
var context = new PrincipalContext(ContextType.Domain, "mydomain.com");
var properties = new[] { "cn", "name", "distinguishedname", "surname", "title", "displayname" };
var i = 0;
var now = DateTime.Now; // you should have done this BEFORE setting `context`. 我在您的代码中注意到的其他一些事情实际上会使时间向相反的方向倾斜,在“新事物”中,您不进行任何筛选,并且在记录开始时间之后会对委托线程进行初始化以显示进度。
https://stackoverflow.com/questions/45357892
复制相似问题