10

我有一个包含艺术家、专辑和曲目的大型数据库。这些项目中的每一个都可以通过粘合表(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,我的第一个问题是返回速度。

4

6 回答 6

3

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

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

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

于 2011-08-05T18:30:02.530 回答
2

您可能应该尝试对数据进行非规范化。您的结构针对插入/更新负载进行了优化,但不适用于查询。据我所知,您将拥有比插入/更新查询更多的选择查询。

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

以标准化结构存储您的数据。

像这样创建聚合表

  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')

每天一次增量地重建此表。

于 2011-08-08T14:49:29.953 回答
2

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

首先-您的表结构强制加入-我认为在编写高性能应用程序时应该尽可能避免它们。我不知道“attribute_tag_groups”是什么,所以我提出一个表结构:tag(varchar 255), id(int), id_type(enum (track, album, artist))。根据 id_type,ID 可以是艺术家 ID、轨道 ID 或专辑 ID。这样,您也可以在一个表中查找所有数据,但当然会使用更多内存。

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

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

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

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

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

于 2011-08-10T08:59:20.210 回答
1

根据我的经验,最“慢”的 MySQL 数据库没有正确的索引和/或查询。所以我会先检查这些:

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

您可以尝试的事情:

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

  • 您的表结构已经很好地规范化,但个人经验告诉我,您可以使用能够避免连接和子查询的结构归档更高的性能级别。对于您的情况,我建议将标签信息存储在一个字段中。(这需要底层星展银行的支持)

至今。

于 2011-08-08T14:54:01.493 回答
0

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

在完全不同的轨道上,google map-reduce 并使用这些新奇特的 no-SQL 数据库之一来处理非常大的数据集。这可以在多个服务器上并行进行分布式搜索。

于 2011-08-15T09:57:31.970 回答