10

アーティスト、アルバム、トラックの大規模なデータベースがあります。これらの各アイテムには、接着剤テーブル(track_attributes、album_attributes、artist_attributes)を介して割り当てられた1つ以上のタグがあります。各アイテムタイプに適用できるタグは数千(または10万)あります。

私は2つのタスクを実行しようとしていますが、クエリを適切に実行するのに非常に苦労しています。

タスク1)特定のタグ(提供されている場合)のあるアルバムで、特定のタグ(提供されている場合)を持つアーティストによって、特定のタグ(提供されている場合)を持つすべてのトラックを取得します。タグのセットは存在しない可能性があります(つまり、トラックタグのみがアクティブで、アーティストまたはアルバムタグはありません)

バリエーション:結果は、トラックではなく、アーティストまたはアルバムごとに表示することもできます

タスク2)前のフィルターの結果に適用されるタグのリストと、それぞれにタグが付けられているトラックの数を取得します。

私が求めているのは、アプローチの一般的なガイダンスです。一時テーブル、内部結合、IN()を試しましたが、これまでのすべての努力の結果、応答が遅くなりました。私が求めている結果の良い例はここで見ることができます:http ://www.yachtworld.com/core/listing/advancedSearch.jsp、タグの層が1つしかないことを除いて、私は3つを扱っています。

テーブル構造:

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リファレンスドキュメントを確認してください。

念のため、PHPでSolrを使用する方法に関する記事があります: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')

1 日に 1 回、このテーブルを段階的に再構築します。

于 2011-08-08T14:49:29.953 に答える
2

答えは、プロジェクトにどれだけのお金を費やしたいかによって大きく異なると思います。厳密な条件を指定すると、理論的には達成できないタスクもあります (たとえば、弱いサーバーを 1 つだけ使用する必要があるなど)。システムをアップグレードする準備ができていると仮定します。

まず第一に、あなたのテーブル構造はJOINを強制します.高性能アプリケーションを書くときは、可能であればそれらを避けるべきだと思います. 「attribute_tag_groups」が何であるかわからないので、次のテーブル構造を提案します: tag(varchar 255), id(int), id_type(enum (track, album, artist))。ID は、id_type に応じて、artist_id、track_id、または album_id になります。このようにして、すべてのデータを 1 つのテーブルにまとめることもできますが、もちろん、より多くのメモリを使用します。

次に、いくつかのデータベースの使用を検討する必要があります。各データベースにデータの一部しか含まれていない場合は、さらに役立ちます (各検索が高速になります)。データベース間でデータを分散する方法を決定することは、通常、かなり難しい作業です。タグの長さに関する統計を作成し、trac/artists の結果数が類似する長さの範囲を見つけて、ルックアップ コードにハードコードすることをお勧めします。

もちろん、MySql のチューニングを検討する必要があります (確かにそうしましたが、念のため) - すべてのテーブルは RAM に配置する必要があります - それが不可能な場合は、SSD ディスク、raid などを取得してみてください。適切なインデックス作成とデータベースの種類/設定も非常に重要です (MySql は内部統計でボトルネックを示すことさえあります)。

この提案はばかげているように聞こえるかもしれませんが、MySql 自体で実行できる計算を PHP に任せるとよい場合もあります。PHP 処理用のサーバーは数分で追加できますが、MySql データベースはスケーリングがはるかに困難です。また、異なる 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. グルー テーブルでは、2 つのフィールドに主キーを設定します。最初に件名、次にタグです。これは通常のブラウジング用です。次に、タグ ID に通常のインデックスを作成します。これは検索用です。
  4. まだ遅いですか?テーブルに MyISAM を使用していますか? クイッククエリ用に設計されています。
  5. それでも遅い場合は、遅いクエリで EXPLAIN を実行し、クエリと結果の両方を質問に投稿します。できれば、完全なデータベース構造のインポート可能な sql ダンプを使用してください。
于 2011-08-14T12:34:35.210 に答える
0

あなたが試してみることができること:

  • クエリ アナライザを使用して、クエリのボトルネックを調査します。(ほとんどの場合、基礎となる DBS は最適化において驚くべき仕事をしています)

  • テーブル構造は十分に正規化されていますが、個人的な経験から、結合とサブクエリを回避できる構造を使用すると、はるかに優れたパフォーマンス レベルをアーカイブできることがわかりました。あなたの場合、タグ情報を1つのフィールドに保存することをお勧めします。(これには、基礎となる DBS によるサポートが必要です)

ここのところ。

于 2011-08-08T14:54:01.493 に答える
0

インデックスを確認し、それらが正しく使用されているかどうかを確認してください。たぶん、MySQL はそのタスクに対応していません。PostgreSQL の使い方は似ているはずですが、複雑な状況ではより優れたパフォーマンスを発揮します。

まったく別の方法で、Google map-reduce を使用して、非常に大きなデータ セットにこれらの新しいファンシーな非 SQL データベースの 1 つを使用します。これにより、複数のサーバーで並行して分散検索を実行できます。

于 2011-08-15T09:57:31.970 に答える