Php 如何创建跨大型动态数据集计算标记的性能系统

Php 如何创建跨大型动态数据集计算标记的性能系统,php,mongodb,design-patterns,database-design,architecture,Php,Mongodb,Design Patterns,Database Design,Architecture,概述 我有一个iOS应用程序,人们可以通过标签进行搜索一些标签是预定义的,一些是用户定义的 当用户编写他/她想要搜索的标记时,我想显示一行,显示那些标记可用的结果数(参见示例搜索图片) 注意:#练习或#例行程序是父母标签,这意味着此人总是先使用其中一个标签 我正在使用PHP和MongoDB服务器端。 我想创建一个每小时计数一次的带有标记的文件,这样所有客户机都可以获取它并将资源消耗降至最低 考虑到被操纵的信息是用户控制的标签,列表将随着时间的推移而大大扩展 挑战 我不知道什么是考虑问题的最佳方

概述

我有一个
iOS
应用程序,人们可以通过
标签进行搜索
一些标签是预定义的,一些是用户定义的

当用户编写他/她想要搜索的
标记时,我想显示一行,显示那些
标记可用的结果数(参见示例搜索图片)

注意:
#练习
#例行程序
是父母
标签
,这意味着此人总是先使用其中一个标签

我正在使用
PHP
MongoDB
服务器端。 我想创建一个每小时计数一次的带有标记的文件,这样所有客户机都可以获取它并将资源消耗降至最低

考虑到被操纵的信息是用户控制的
标签
,列表将随着时间的推移而大大扩展

挑战

  • 我不知道什么是考虑问题的最佳方法 创建、操作和存储此类文件的性能和开销 名单
我的第一个想法是创建一个
2d数组
(见图),它将存储我的所有值。然后将其转换为JSON,以便将其存储到MongoDB中

但这种方法将使我获取所有标记并将它们加载到内存中,以执行任何+1或-1操作。因此,我认为这可能不是最好的

所有操作都将在插入、更新和删除每个元素时进行。因此将有相当大的
RAM
开销

我的第二个想法是创建一个
文档
,在那里我存储所有使用的
标签
,并每小时进行计数查询,以生成客户使用的列表

这意味着每次删除、更新和插入时,都要检查标签是否存在于该
文档中,并根据条件创建或删除标签或不执行任何操作

每小时,获取所有标记,生成一个包含所有标记组合的数组。根据所有标记组合查询数据库,计算返回的结果数并创建文件

考虑到我使用的是
MongoDB
,而不是
MySQL
,我认为这种方法可能会更好。但我仍然不确定它的性能

有没有人做过类似的系统,可以就更好的方法提出建议

搜索示例图片

2d阵列


由于问题很长,我将列出我在反复阅读问题几遍后所做的一些假设:

  • 人们可以输入n个标签进行搜索
  • 标记是预定义的或用户定义的
  • 标签将随着时间的推移而显著增加
  • 我们需要一组给定标记的文档总数。如果#tag1有10个,而#tag2有13个,那么我们大约有23个文档(考虑到有些文档可能同时有两个标记)
  • 我有一些关于方法的建议:

  • 您已经开始这样想了,但计划将读和写分开。如果有人将文档标记为#cost,请立即继续写,但不要在所有读取中都提供该信息。(这将返回到每小时运行的作业。)

  • 您可以立即向标记文档的用户提供反馈。我听说YouTube就是这么做的。你喜欢YouTube上的内容,它会立即增加数字,即使写的内容还没有提交。这个想法是你立即给用户一些东西——即使它不是完全准确的

  • (2)认为数字只是“足够好”。搜索#胸部#常规。。。假设返回100结果。请随意显示98或99。在球场上。换句话说,只要用户能够感觉到有多少结果,就可以不立即说明+1和-1

  • 考虑运行一个将数据移出Mongo的作业。您可以让Mongo运行map/reduce查询,该查询输出到Mongo中的集合。然后你可以把它放在像Redis这样的东西里来服务数据。或者我想你可以把它放在蒙戈

  • 我不确定我会创造出所有可能的组合。这将很快失去控制-尤其是如果人们可以创建自己的标签。相反,只需创建一个简单的数据结构,其中包含带有计数的搜索项。[“胸部”,100],“常规”,543],“三头肌”,12]

  • 可以指出的是,如果一个文档有多个标记,我们将计算它两次。这是真的,但我会主张简单化,不去管它。如果您愿意在计数方面牺牲一点精度,那么您的代码将更易于维护和执行


  • 更新:重写此概念以避免对MySQL示例的混淆

    正如原始答案中所述,我是蒙哥达无知者。因此,我将简单地解释这个概念

    也许仍然有一种方法可以获得性能良好的准确计数。这是基于这样的假设,即您希望标记列表(包含所有请求的标记的所有文档)的交集

    1) 创建包含3个字段的新集合(标记\查询\缓存):

    • 标记\主键:主键-查询中所有标记的字符串
    • tags_count:包含tags_列表中所有标记的文档数
    • 标签列表:标签数组-标签键中的所有标签(应该是标签的索引)
    2) 更新|添加|删除原始收藏中的文档时:

    • 识别文档中已更改(添加或删除)的所有标记
    • 删除标记\u查询\u cach中的所有文档
      > db.performant.find()
      { "_id" : ObjectId("522bf7166094a4e72db22827"), "name" : "abc", "tags" : [  "chest",  "bicep",  "tricep" ] }
      { "_id" : ObjectId("522bf7406094a4e72db22828"), "name" : "def", "tags" : [  "routine",  "trufala",  "tricep" ] }
      { "_id" : ObjectId("522bf75f6094a4e72db22829"), "name" : "xyz", "tags" : [  "routine",  "myTag",  "tricep",  "myTag2" ] }
      { "_id" : ObjectId("522bf7876094a4e72db2282a"), "name" : "mno", "tags" : [  "exercise",  "myTag",  "tricep",  "myTag2",  "biceps" ] }
      
      > db.performant.ensureIndex({tags:1})
      > db.performant.getIndexes()
      [
          {
                  "v" : 1,
                  "key" : {
                          "_id" : 1
                  },
                  "ns" : "test.performant",
                  "name" : "_id_"
          },
          {
                  "v" : 1,
                  "key" : {
                          "tags" : 1
                  },
                  "ns" : "test.performant",
                  "name" : "tags_1"
          }
      ]
      
      > db.performant.find({tags:{$in:["bicep","chest","trufala"]}}).explain()
      {
          "cursor" : "BtreeCursor tags_1 multi",
          "isMultiKey" : true,
          "n" : 2,
          "nscannedObjects" : 3,
          "nscanned" : 5,
          "nscannedObjectsAllPlans" : 3,
          "nscannedAllPlans" : 5,
          "scanAndOrder" : false,
          "indexOnly" : false,
          "nYields" : 0,
          "nChunkSkips" : 0,
          "millis" : 0,
          "indexBounds" : {
                  "tags" : [
                          [
                                  "bicep",
                                  "bicep"
                          ],
                          [
                                  "chest",
                                  "chest"
                          ],
                          [
                                  "trufala",
                                  "trufala"
                          ]
                  ]
          },
          "server" : "none-6674b8f4f2:27017"
      }
      
      > db.performant.find({$or:[{tags:"bicep"},{tags:"chest"},{tags:"trufala"}]}).explain()
      {
          "clauses" : [
                  {
                          "cursor" : "BtreeCursor tags_1",
                          "isMultiKey" : true,
                          "n" : 1,
                          "nscannedObjects" : 1,
                          "nscanned" : 1,
                          "nscannedObjectsAllPlans" : 1,
                          "nscannedAllPlans" : 1,
                          "scanAndOrder" : false,
                          "indexOnly" : false,
                          "nYields" : 0,
                          "nChunkSkips" : 0,
                          "millis" : 10,
                          "indexBounds" : {
                                  "tags" : [
                                          [
                                                  "bicep",
                                                  "bicep"
                                          ]
                                  ]
                          }
                  },
                  {
                          "cursor" : "BtreeCursor tags_1",
                          "isMultiKey" : true,
                          "n" : 0,
                          "nscannedObjects" : 1,
                          "nscanned" : 1,
                          "nscannedObjectsAllPlans" : 1,
                          "nscannedAllPlans" : 1,
                          "scanAndOrder" : false,
                          "indexOnly" : false,
                          "nYields" : 0,
                          "nChunkSkips" : 0,
                          "millis" : 0,
                          "indexBounds" : {
                                  "tags" : [
                                          [
                                                  "chest",
                                                  "chest"
                                          ]
                                  ]
                          }
                  },
                  {
                          "cursor" : "BtreeCursor tags_1",
                          "isMultiKey" : true,
                          "n" : 1,
                          "nscannedObjects" : 1,
                          "nscanned" : 1,
                          "nscannedObjectsAllPlans" : 1,
                          "nscannedAllPlans" : 1,
                          "scanAndOrder" : false,
                          "indexOnly" : false,
                          "nYields" : 0,
                          "nChunkSkips" : 0,
                          "millis" : 0,
                          "indexBounds" : {
                                  "tags" : [
                                          [
                                                  "trufala",
                                                  "trufala"
                                          ]
                                  ]
                          }
                  }
          ],
          "n" : 2,
          "nscannedObjects" : 3,
          "nscanned" : 3,
          "nscannedObjectsAllPlans" : 3,
          "nscannedAllPlans" : 3,
          "millis" : 10,
          "server" : "none-6674b8f4f2:27017"
      }
      
      > db.performant.find({$or:[{tags:"bicep"},{tags:"chest"},{tags:"trufala"}]}).sort({tags:1}).explain()
      {
          "cursor" : "BtreeCursor tags_1",
          "isMultiKey" : true,
          "n" : 2,
          "nscannedObjects" : 15,
          "nscanned" : 15,
          "nscannedObjectsAllPlans" : 15,
          "nscannedAllPlans" : 15,
          "scanAndOrder" : false,
          "indexOnly" : false,
          "nYields" : 0,
          "nChunkSkips" : 0,
          "millis" : 0,
          "indexBounds" : {
                  "tags" : [
                          [
                                  {
                                          "$minElement" : 1
                                  },
                                  {
                                          "$maxElement" : 1
                                  }
                          ]
                  ]
          },
          "server" : "none-6674b8f4f2:27017"
      }