【问题标题】:Search through Reliable Dictionary通过可靠字典搜索
【发布时间】:2016-12-05 06:42:10
【问题描述】:

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

HTTP 1.1 GET http://sf.cluster:80/
Path /search/users?q=Aa&take=10

其中 q 是用户在搜索字段中输入的字符串。 take - 手机想要显示多少条目。

我在我的可靠字典中上传了 Azure 存储表中的 89000 个项目。它有结构:

IReliableDictionary<Guid, string>

我的搜索方法如下:

    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;
    }

问题:它在手机上运行良好,我得到了我的预期。但是当我开始用一堆请求推送我的端点时(大约 60 个线程同时使用Soap UI 工具),开始的超时从 1 秒增加到 35 秒!看来我在某个地方犯了错误,或者选择了错误的搜索实现方式。

有人实现了这样的功能吗?任何人都可以帮助正确的搜索方法吗?

UPD: 实现了无状态服务,我将 List&lt;string&gt; 与名称一起存储并执行相同的操作(通过列表搜索)。结果:150-300 毫秒。看起来我应该将 List 存储在一个状态中(在有状态服务中)并根据请求获取它..

【问题讨论】:

    标签: search azure-service-fabric


    【解决方案1】:

    我不确定您的 ToEnumerable 方法的实现是什么,但我所看到的大多数都是一个相当懒惰的实现,即只采用异步枚举并将其复制到列表中。现在,有了一个包含 890,000 个元素的可靠字典,效率非常低。此外,事务就像一个互斥体,所以当你复制这个巨大的列表时,你会锁定底层集合。我建议检查 this library 中的 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);
        }
    

    另外,作为旁注,由于您没有以任何方式修改基础集合,因此您不需要提交事务。提交事务只是告诉状态管理器您已修改状态并且已完成更改的一种方式,然后它将更改的值传播到辅助节点。如果这是一个读取繁重的状态,您甚至可以在辅助节点上调用此方法,但请注意写入可能尚未传播。

    【讨论】:

      【解决方案2】:

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

      如果担心读取延迟,您可以使用通知来构建完全在内存中的二级索引。您还可以按值对二级索引进行排序,以提高前缀匹配搜索的效率。以下是相关文档:https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-reliable-services-notifications

      对 pdylanross 答案的小幅更正: CreateEnumerableAsync 使用不锁定集合的 mvcc 模型提供快照隔离。因此,其他事务可以在快照读取事务正在进行时继续执行读取和写入操作。有关隔离级别的更多信息:https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-reliable-services-reliable-collections

      希望这会有所帮助,

      【讨论】:

        猜你喜欢
        • 2015-03-29
        • 2019-09-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-04-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多