Search 查可靠的词典

Search 查可靠的词典,search,azure-service-fabric,Search,Azure Service Fabric,我需要通过系统中的所有用户创建Web API搜索功能。 客户端(使用电话)使用端点向我发送请求: HTTP 1.1 GET http://sf.cluster:80/ Path /search/users?q=Aa&take=10 其中q是用户在搜索字段中输入的字符串获取-手机想要显示多少条目 我在我的可靠字典中上传了来自Azure存储表的89000个项目。其结构如下: IReliableDictionary<Guid, string> IReliableDictiona

我需要通过系统中的所有用户创建Web API搜索功能。 客户端(使用电话)使用端点向我发送请求:

HTTP 1.1 GET http://sf.cluster:80/
Path /search/users?q=Aa&take=10
其中q是用户在搜索字段中输入的字符串获取-手机想要显示多少条目

我在我的可靠字典中上传了来自Azure存储表的89000个项目。其结构如下:

IReliableDictionary<Guid, string>
IReliableDictionary
我的搜索方法如下所示:

    public async Task<IEnumerable<UserInfo>> Search(string q, int take)
    {
        var usersDictionary = await GetUsersDictionary();

        IEnumerable<UserInfo> results;
        using (var tx = StateManager.CreateTransaction())
        {
            var searchResults = (from r in (await usersDictionary.CreateEnumerableAsync(tx)).ToEnumerable()
                          where r.Value.StartsWith(q, StringComparison.InvariantCultureIgnoreCase)
                          select new UserInfo()
                          {
                              Id = r.Key,
                              Name = r.Value
                          }).Take(take);

            results = new List<UserInfo>(searchResults);

            await tx.CommitAsync();
        }

        return results;
    }
公共异步任务搜索(字符串q,int-take)
{
var usersDictionary=await GetUsersDictionary();
i数不清的结果;
使用(var tx=StateManager.CreateTransaction())
{
var searchResults=(从(wait usersDictionary.CreateEnumerableAsync(tx)).ToEnumerable()中的r)
其中r.Value.StartsWith(q,StringComparison.InvariantCultureInogoreCase)
选择newuserinfo()
{
Id=右键,
名称=r.值
}).拿(拿);
结果=新列表(搜索结果);
wait tx.CommitAsync();
}
返回结果;
}
问题:它在手机上运行得很好,我得到了我期望的结果。但是,当我开始用一堆请求推送我的端点时(使用工具同时推送大约60个线程),启动的超时时间从1秒增加到35秒!看起来我在某个地方犯了一个错误,或者选择了一种错误的搜索实现方式

有人实现过这样的功能吗?有人能帮助我们找到正确的搜索方法吗


UPD:实现了无状态服务,我在其中使用名称存储
列表
,并执行与其相同的操作(搜索列表)。结果:150-300ms。看起来我应该将列表存储在一个状态中(在有状态服务中),并根据请求获取它

我不确定您的
ToEnumerable
方法的实现是什么,但我所看到的大多数方法都是相当懒惰的实现,只是获取异步可枚举项并将其复制到列表中。现在,有了一本890000个元素的可靠字典,这是相当低效的。此外,事务的行为类似于互斥锁,因此当您复制这个庞大的列表时,您会锁定底层集合。我建议在中检查AsyncEnumerable linq实现,因为它实现了将linq与服务结构AsyncEnumerable一起使用的有效方法。使用该选项,您的搜索将如下所示:

    using (var tx = StateManager.CreateTransaction())
    {
        var enumerable = await usersDictionary.CreateEnumerableAsync(tx);
        results = await enumerable.Where(kvp=>kvp.Value.StartsWith(q, StringComparison.InvariantCultureIgnoreCase))
            .Select(kvp=> new UserInfo()
                  {
                      Id = r.Key,
                      Name = r.Value
                  })
            .Take(take)
            .ToListAsync(tx);
    }

另外,作为补充说明,由于您没有以任何方式修改基础集合,因此不需要提交事务。提交事务只是告诉状态管理器您已经修改了状态并完成了更改的一种方式,然后它会将更改的值传播到二级数据库。如果这是一个读取量很大的状态,甚至可以在二级上调用此方法,但请注意,写入可能尚未传播

我不确定您的
ToEnumerable
方法的实现是什么,但我所看到的大多数方法都是相当懒惰的实现,只是获取异步可枚举项并将其复制到列表中。现在,有了一本890000个元素的可靠字典,这是相当低效的。此外,事务的行为类似于互斥锁,因此当您复制这个庞大的列表时,您会锁定底层集合。我建议在中检查AsyncEnumerable linq实现,因为它实现了将linq与服务结构AsyncEnumerable一起使用的有效方法。使用该选项,您的搜索将如下所示:

    using (var tx = StateManager.CreateTransaction())
    {
        var enumerable = await usersDictionary.CreateEnumerableAsync(tx);
        results = await enumerable.Where(kvp=>kvp.Value.StartsWith(q, StringComparison.InvariantCultureIgnoreCase))
            .Select(kvp=> new UserInfo()
                  {
                      Id = r.Key,
                      Name = r.Value
                  })
            .Take(take)
            .ToListAsync(tx);
    }

另外,作为补充说明,由于您没有以任何方式修改基础集合,因此不需要提交事务。提交事务只是告诉状态管理器您已经修改了状态并完成了更改的一种方式,然后它会将更改的值传播到二级数据库。如果这是一个读取量很大的状态,甚至可以在二级上调用此方法,但请注意,写入可能尚未传播

ReliableDictionary返回一个IAsyncEnumerable,因为ReliableDictionary会分页掉一些值。这意味着可能需要磁盘IO来读取某些值。IAsyncEnumerable允许我们尽可能少地阻塞线程

如果读取延迟是一个问题,您可以使用通知来构建完全在内存中的辅助索引。您还可以按值对辅助索引排序,以提高前缀匹配搜索的效率。以下是相关文件:

对pdylanross的回答进行了细微的更正:CreateEnumerableAsync使用不锁定集合的mvcc模型提供快照隔离。因此,其他事务可以在快照读取事务运行时继续执行读写操作。有关隔离级别的更多信息,请参见:


希望这能有所帮助,

ReliableDictionary返回一个IAsyncEnumerable,因为ReliableDictionary会分页掉一些值。这意味着可能需要磁盘IO来读取某些值。IAsyncEnumerable允许我们尽可能少地阻塞线程

如果读取延迟是一个问题,您可以使用通知来构建完全在内存中的辅助索引。您还可以按值对辅助索引排序,以提高前缀匹配搜索的效率。以下是相关文件:

对pdylanross的回答进行了细微的更正:CreateEnumerableAsync使用不锁定集合的mvcc模型提供快照隔离。因此,其他事务可以在快照读取事务运行时继续执行读写操作。有关隔离级别的更多信息,请参见:

希望这有帮助