【问题标题】:How to improve performance of matching algorithm如何提高匹配算法的性能
【发布时间】:2017-09-20 19:21:56
【问题描述】:

我正在编写基于兴趣和位置进行匹配的算法。假设我有这些用户数据

{
    "users": [{
            "location": "Delhi, India",
            "interests": ["Jogging", "Travelling", "Praying"],
            "groups": ["exercise", "travelling", "Praying"]
        },
        {
            "location": "Delhi, India",
            "interests": ["Running", "Eating", "Praying"],
            "groups": ["exercise", "Eating", "Praying"]
        }, {
            "location": "Delhi, India",
            "interests": ["Shopping"],
            "groups": ["Shopping"]
        }
    ]
}

在这里,他们 user1 和 user2 有相似的兴趣“锻炼”和“祈祷”,而 user1 和 user3 没有相似的兴趣。

如果我每次在接收来自移动应用的请求时使用带有 where 子句的 SQL 查询,在拥有 10+ 百万用户的数据库中找到相似兴趣的人可能会影响我的数据库性能。

SELECT * FROM users WHERE groups = "exercise" OR groups = "travelling" OR groups = "Praying";

这将检查可能影响我的应用程序性能的每个配置文件。我不想使用这种方法,因为这不会长久。我应该使用什么算法来获得高性能?

【问题讨论】:

标签: algorithm performance firebase firebase-realtime-database


【解决方案1】:

数据结构看起来就像是像 MongoDb 这样的 NoSql db。无论如何,检查全文索引是否对您有帮助。我刚刚在 MSSQL (https://docs.microsoft.com/en-us/sql/t-sql/statements/create-fulltext-index-transact-sql) 中看到了全文索引。我之前对此一无所知。 MongoDb 也有全文索引。如果实施得当,索引肯定会对您的查询有所帮助。我不确定在一个表上可以实现多少个全文索引。请对此进行研究。

【讨论】:

    【解决方案2】:

    插图:

    如果您有某种方法可以获得完整的兴趣列表(也许您让他们从一组兴趣中选择一个特定条目),您可以使用简单的矩阵乘法与相应的搜索向量。

    编辑:这种方法也适用于倒置,即只要您正确转置,您就可以将用户映射到组而不是组到用户,并且您可能喜欢这样做,因为您可能会很远用户多于组,尽管示例大体相同。

    Let groups = [
      1: "exercise" 
      2: "traveling"
      3: "praying"
      4: "eating"
      5: "running"
      6: "shopping"
    ]
    
    Let U = [
      1 1 1 0 0 0  // user 1
      0 0 1 1 1 0  // user 2
      0 0 0 0 0 1  // user 3
    ]  
    

    您正在使用 OR,因为您希望 任何 组中的成员

    Let V = [
      1  // exercise
      1  // traveling
      1  // praying
      0  // eating
      0  // running
      0  // shopping
    ]
    

    乘法:

    U · V = [
      3  // user 1 is in all 3 groups => match
      1  // user 2 is in one group => match
      0  // user 3 is in no groups => no match
    ]
    

    这会检查每个用户是否存在一个或多个请求的列 (OR),并且结果向量中的任何非零条目都是匹配的。

    或者,对于相同的精确查询,仅选择具有 2 列或更多列 (AND) 的特定集合的用户会将匹配视为结果向量中的任何 n 或更大值的条目,其中 n 是参数的数量。

    仅选择具有特定一或多个列且不一或多个其他列 (XOR) 的那些将被视为仅匹配具有 恰好 n 值的结果条目。

    这真的是个好主意吗? 这种方法可以使用如果你认为真正的问题是查询可能变得足够复杂以至于查询分析器会成为瓶颈,查询将变得非常难以管理,或者您需要处理不断变化的“组”列表,或者如果您只是打算在您的应用程序中做很多线性代数。

    解决方案首先取决于您的用例。例如,如果查询速度是最重要的并且数据传输不是问题,那么这种方法将允许一个非常简单的查询返回所有(带有 LIMIT)行,然后您可以优化筛选,直到找到给定页面所需的用户,仅根据需要运行后续查询以加载更多页面。由于您提到每次收到来自移动应用程序的请求时都会发生这种情况,也许您最好缓存可管理数量的用户并每次轮询它而不是数据库,除非找到不充分的匹配项,实施合适的时间测试缓存替换算法(在某种程度上也可以潜在地卸载到客户端)。

    结论/tl;dr 这里重要的一点是您想要的结构完全取决于应用程序的业务需求。您可以将数据结构设置为您喜欢的深奥,以提高性能,但这通常不如简单地使用经过时间考验的解决方案(例如基本缓存)富有成效。

    如果您认为像 Yavar 建议的那样对倒键方法进行重构最适合您的需求,那么这可能是您的解决方案。

    如果您认为图形数据库是必要的,可以满足您的业务需求,并且速度更快、易于管理,那么这可能是您的解决方案。

    如果您的需求非常具体,以至于您需要一个完全针对您的应用程序完全优化且不一定在其他地方有用的完全特定的定制实现,那么这可能是您的解决方案。

    设计标准的存在是有充分理由的,但优化有时可能是特定领域的。如您所见,有多种可能的解决方案,但要准确选择最适合您的解决方案取决于许多未知因素,例如业务需求,最终正确的解决方案将是足够 快速而不牺牲可维护性/理智/头发。

    【讨论】:

    • 非常感谢 semchev。真的是很好的答案。我也想给你的答案加分..想再次给赏金+1..
    • 感谢您的赞赏。我已经多次处理过这个问题,很容易陷入优化和/或“简化”的兔子洞。祝您找到最佳解决方案好运!
    【解决方案3】:

    您可以构造一个inverted index,其中键将是“组”中的令牌之一(即锻炼、旅行等),值将是属于该组的用户列表。例如,您的倒排索引看起来像这样:

    Key: ListOfValues
    Exercise: User1 -> User2
    Praying: User1 -> User2
    Travelling: User1 -> User3 -> User8 -> User14
    Shopping: User3
    

    您是否想要基于树、位图或基于哈希表的倒排索引将是您的选择,具体取决于您的空间/时间权衡。

    现在,当您获得一个新用户时,例如 User99 具有组(锻炼和祈祷),您可以快速检索“锻炼”令牌的值(即用户),然后检索“祈祷”令牌的值,然后最终执行两者的'AND'(交集)。

    请注意,第一次运行它将是批处理,但是当您开始获得新用户时,您的运行时间复杂度几乎是恒定的(如果您有智能数据结构,例如压缩位图您在倒排索引中的“用户”值的发布列表,否则交集不会比 O(n) AFAIK 快)

    【讨论】:

    • 谢谢!举个例子,如果我的firebase-database 中有数百万用户,那么在收到来自 android 移动应用程序的请求时,firebase 也会迭代具有相似兴趣的组中的每个用户。它不会影响性能吗?
    • 为什么不为此功能编写一些单独的代码?好吧,最好不要直接访问数据库,也许您的应用程序服务器应该调用您的自定义代码(例如 REST API)来完成这项工作。通过迭代数据库构建倒排索引后,您无需返回数据库进行查询。优化/压缩的倒排索引可以存储在 RAM 或磁盘上(可能作为平面文件),它会回答您的查询。这是关于布尔检索如何在 Lucene/Google 等搜索引擎中工作的简化 :)
    • 考虑时间/空间权衡的其他替代方法是位图发布列表,您实际上可以在其中执行按位“与”来得出结果。但是它会很稀疏,您将不得不通过构建智能压缩位图来处理更高的空间复杂度。然而,检索将是超快速的、恒定的时间。
    • “优化/压缩的倒排索引可以存储在 RAM 或磁盘上(可能作为平面文件),它会回答您的查询。” - 我希望 firebase 支持这个来实现 redis 以将数据保存在缓存中,Afaik 它没有
    • 在您使用的框架中,一定有更简单的替代解决方案来解决您的问题,让我们拭目以待,很快会有人回答:)
    猜你喜欢
    • 2014-11-26
    • 1970-01-01
    • 2019-10-06
    • 2018-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-01
    • 2013-06-23
    相关资源
    最近更新 更多