高性能多层标签过滤

时间:2011-08-05 18:03:51

标签: php sql performance join large-data

我有一个庞大的艺术家,专辑和曲目数据库。这些项目中的每一个都可以通过胶合表(track_attributes,album_attributes,artist_attributes)分配一个或多个标签。每种物品类型都有数千(甚至数十万)个标签。

我正在尝试完成两项任务,而且我很难让查询执行得令人满意。

任务1)获取具有任何给定标签(如果提供)的所有曲目,这些曲目由具有任何给定标签(如果提供)的专辑上具有任何给定标签(如果提供)的艺术家提供。任何一组标签可能都不存在(即只有一个曲目标签是活动的,没有艺术家或专辑标签)

变化:结果也可以由艺术家或专辑而非曲目呈现

任务2)获取应用于前一过滤器结果的标签列表,以及每个给定标签的轨道数量。

我所追求的是方法中的一般指导。我已经尝试过临时表,内部联接,IN(),到目前为止我所做的所有努力都会导致响应缓慢。我可以在这里看到我所追求的结果的一个很好的例子:http://www.yachtworld.com/core/listing/advancedSearch.jsp除了他们只有一层标签,我正在处理三个。

表格结构:

Table: attribute_tag_groups
   Column   |          Type               |   
------------+-----------------------------+
 id         | integer                     |
 name       | character varying(255)      | 
 type       | enum (track, album, artist) | 

Table: attribute_tags
   Column                       |          Type               |   
--------------------------------+-----------------------------+
 id                             | integer                     |
 attribute_tag_group_id         | integer                     |
 name                           | character varying(255)      | 

Table: track_attribute_tags
   Column   |          Type               |   
------------+-----------------------------+
 track_id   | integer                     |
 tag_id     | integer                     | 

Table: artist_attribute_tags
   Column   |          Type               |   
------------+-----------------------------+
 artist_id  | integer                     |
 tag_id     | integer                     | 

Table: album_attribute_tags
   Column   |          Type               |   
------------+-----------------------------+
 album_id   | integer                     |
 tag_id     | integer                     | 

Table: artists
   Column   |          Type               |   
------------+-----------------------------+
 id         | integer                     |
 name       | varchar(350)                | 

Table: albums
   Column   |          Type               |   
------------+-----------------------------+
 id         | integer                     |
 artist_id  | integer                     | 
 name       | varchar(300)                | 

Table: tracks
   Column    |          Type               |   
-------------+-----------------------------+
 id          | integer                     |
 artist_id   | integer                     | 
 album_id    | integer                     | 
 compilation | boolean                     | 
 name        | varchar(300)                | 

编辑我正在使用PHP,我不反对在脚本中进行任何排序或其他hijinx,我的第一个问题是返回的速度。

6 个答案:

答案 0 :(得分:3)

如果你想要速度,我建议你看看Solr / Lucene。您可以通过调用Solr并从PHP解析结果来存储数据并进行非常快速的查找。而且作为额外的好处,您也可以获得分面搜索(如果我正确解释,这是您问题的任务2)。缺点当然是您可能有冗余信息(一旦存储在DB中,一次存储在Solr文档存储中)。设置需要一段时间(好吧,你可以从Drupal Solr集成中学到很多东西)。

只需查看Solr的PHP参考文档。

以下是有关如何将Solr与PHP结合使用的文章,以防万一:http://www.ibm.com/developerworks/opensource/library/os-php-apachesolr/

答案 1 :(得分:2)

您可能应该尝试对数据进行非规范化。您的结构针对插入/更新加载进行了优化,但不适用于查询。当我得到它时,你会有比插入/更新查询更多的选择查询。

例如,您可以执行以下操作:

将您的数据存储在规范化结构中。

像这样创建agregate表

  track_id, artist_tags, album_tags, track_tags
   1 , jazz/pop/,  jazz/rock, /heavy-metal/  

    or 

    track_id, artist_tags, album_tags, track_tags
    1 , 1/2/,  1/3, 4/

要进行搜索,你可能应该在* _tags列上创建FULLTEXT索引

使用像

这样的sql查询此表
select * from aggregate where album_tags  MATCH (track_tags) AGAINST ('rock')

每天以增量方式重建此表格。

答案 2 :(得分:2)

我认为答案在很大程度上取决于您希望在项目上花多少钱 - 在严格的条件下(例如,您必须只使用一个弱服务器),理论上甚至无法完成某些任务。我将假设您已准备好升级系统。

首先 - 你的表结构强制加入JOIN - 我认为在编写高性能应用程序时应尽可能避免使用它们。我不知道“attribute_tag_groups”是什么,所以我提出了一个表结构:tag(varchar 255),id(int),id_type(enum(track,album,artist))。 ID可以是artist_id,track_id或album_id,具体取决于id_type。通过这种方式,您可以将所有数据放在一个表中,但是对于cource,它将使用更多的内存。

接下来 - 您应该考虑使用多个数据库。如果每个数据库只包含部分数据(每次查找都会更快),它将会有更多帮助。决定如何在数据库之间传播数据通常是一项相当艰巨的任务:我建议您对标记长度进行一些统计,查找将获得类似trac /艺术家结果计数的长度范围,并将其硬编码到查找代码中。

对于cource你应该考虑MySql调优(我相信你做到了,但以防万一) - 你的所有表应该驻留在RAM中 - 如果不可能尝试获取SSD光盘,raid等。正确的索引和数据库类型/设置也非常重要(MySql甚至可能在内部统计中显示一些瓶颈)。

这个建议可能听起来很疯狂 - 但有时让PHP做一些MySql可以做的计算是件好事。 MySql数据库更难扩展,而PHP处理服务器可以在几分钟内添加。不同的PHP线程可以在不同的CPU核心上运行 - MySql有问题。您可以通过使用一些高级模块来增加PHP性能(您甚至可以自己编写它们 - 在快速C代码中配置PHP脚本和硬编码瓶颈)。

最后但我认为最重要的是 - 你必须使用某种类型的缓存。我知道这真的很难,但我不认为没有一个非常好的缓存系统有任何大项目。在你的情况下,一些标签肯定会比其他标签更受欢迎,所以它应该大大提高性能。缓存是一种艺术形式 - 取决于您可以在其上花费多少时间以及可以使用多少资源,您可以使99%的请求使用缓存。

使用其他数据库/索引工具可能会对您有所帮助,但您应该始终考虑理论查询速度比较(O(n),O(nlog(n))...)以了解它们是否真的可以帮助您 - 使用此工具有时会给你带来低性能增益(比如常数20%),但它们可能会使你的应用程序设计复杂化,而且大部分时间都不值得。

答案 3 :(得分:1)

根据我的经验,大多数“慢”MySQL数据库没有正确的索引和/或查询。所以我先检查一下:

  1. 确保所有数据talbes的id字段都是主索引。以防万一。
  2. 对于所有数据表,在外部id字段和id上创建索引,以便MySQL可以在搜索中使用它。
  3. 对于胶水表,在两个字段上设置主键,首先是主题,然后是标记。这是为了正常浏览。然后在标记ID上创建一个普通索引。这是为了搜索。
  4. 还慢吗?你在桌子上使用MyISAM吗?它专为快速查询而设计。
  5. 如果仍然很慢,请在慢速查询上运行EXPLAIN并发布查询并导致问题。最好使用完整数据库结构的可导入sql转储。

答案 4 :(得分:0)

你可以尝试的事情:

  • 使用Query Analyzer来探索查询的瓶颈。 (在大多数情况下,潜在的DBS在优化方面做得非常出色)

  • 您的表格结构已经很好地规范化了,但是个人经验告诉我,您可以使用可以避免连接和结构的结构来存档更高的性能级别。 subquerys。对于您的情况,我建议将标签信息存储在一个字段中。 (这需要基础DBS的支持)

到目前为止。

答案 5 :(得分:0)

检查您的指数,以及它们是否正确使用。也许MySQL不能胜任这项任务。 PostgreSQL应该类似于使用,但在复杂情况下具有更好的性能。

在一个完全不同的轨道上,google map-reduce并使用其中一个新的花哨的无SQL数据库来实现非常大的数据集。这可以并行地在多个服务器上进行分布式搜索。