2

問題の説明:

タグ (tags) は、ジャンクション テーブル (tagged_as) を介して任意のオブジェクトに関連付けることができます。特定のオブジェクト タイプ (specific_object) について、一連のタグに関連付けられたすべてのオブジェクトの結合または共通部分を選択し、オブジェクトの数値列で結果を並べ替え、ページ付けのために結果を制限します。

考案されたスキーマ:

CREATE TABLE tags (
    id INT NOT NULL AUTO_INCREMENT,
    name VARCHAR(45) NOT NULL,
    PRIMARY KEY (id)
);

CREATE TABLE specific_object(
    id INT NOT NULL AUTO_INCREMENT,
    name VARCHAR(45) NOT NULL,
    vote_sum INT NOT NULL DEFAULT 0,
    PRIMARY KEY (id)
);

CREATE TABLE tagged_as(
    id INT NOT NULL AUTO_INCREMENT,
    tag_id INT NOT NULL,
    content_type_id INT NOT NULL,
    object_id INT NOT NULL,
    PRIMARY KEY (id)
);

この例では、specific_object テーブルの他の多くの列を省略しています。

テーブルの行数:

タグ: 12,297

tagged_as: 46,642,064

特定のオブジェクト: 2,444,944

単純な MySQL ソリューション:

SELECT
    specific_object.*
FROM
    specific_object
JOIN
    tagged_as
ON
    specific_object.id = tagged_as.object_id
    AND
    tagged_as.content_type_id = <SPECIFIC_OBJECT_CONTENT_TYPE_ID>
WHERE
    tagged_as.tag_id = <TAG_ONE_ID>
    AND
    tagged_as.tag_id = <TAG_TWO_ID>
    ...
ORDER BY specific_object.vote_sum DESC
LIMIT 50

このソリューションの問題は、「行をフェッチするために使用されるキーが ORDER BY で使用されるものと同じではない」ため、MySQL が ORDER BY 句を解決するためにインデックスを利用できないことです ( http://dev.mysql.com /doc/refman/5.0/en/order-by-optimization.html )。実行時間: 20 秒以上

単純な Redis ソリューション:

for each specific object: SET specfic_object:<ID> <ID>
for each tagged as: SADD tag:<TAG ID> specific_object:<ID>

specific_object_ids = SUNION tag:<TAG_ONE_ID> tag:<TAG_TWO_ID> ...
specific_object_ids = SINTER tag:<TAG_ONE_ID> tag:<TAG_TWO_ID> ...

SELECT * FROM specific_object WHERE id IN (<specific_object_ids>) ORDER BY vote_sum DESC

このソリューションの問題は、ORDER BY が MySQL によって実行されなければならないことです。また、タグは、移動する大量のデータである何十万もの特定のオブジェクトに関連付けられる可能性があります。実行時間: 大きなタグの場合は 20 秒以上

まだ試していない可能な解決策

非正規化

おそらく、vote_sum 列を tagged_as テーブルに移動します。結合で順序付けを行う必要がなくなります。これには、素朴な解決策と同じ問題がある可能性があります。

Redis ソートセット

for each specific object: SET specific_object:<ID> <ID>
for each specific object: SET specific_object_weight:<ID> <VOTE_SUM>
for each tagged as: SADD tag:<TAG_ID> specific_object:<ID>

SINTERSTORE result:<timestamp> <TAG_ONE_ID> <TAG_TWO_ID> ...
SORT result:<timestamp> BY specific_object_weight_* LIMIT 0 50 
specific_object_ids = SMEMBERS result:<timestamp>
DEL result:<timestamp>

SELECT * FROM specific_object WHERE id IN (<specific_object_ids>)

すべての並べ替えを Redis に移動します。これにより、さらに複雑さが増します。これは、Redis での vote_sum 値も維持する必要があるためです。これが十分に速いかどうかはわかりません。

質問:

考えられる解決策のいずれかが実行可能ですか? 他に役立つソリューションや別のテクノロジーはありますか? この問題を解決するために、私はかなり大きな変更を受け入れる用意があります。

4

1 に答える 1

0

問題がDESCソートのパフォーマンスであった場合、私が過去に行ったことは、問題を解決するために-1*vote_sum別の列に値を格納し、次にその列ASCでORDER BYすることです。MySQL でインデックスを使用してその列の並べ替えを行うことができました。

冗長列 (vote_sumneg_vote_sumの両方) を格納するか、負の値を格納して、正の値として返す必要がある場合は -1 を掛けるだけです。

しかし、パフォーマンスの問題の原因はソート操作にあるのではないかと思います。?を実行したときのステートメントのパフォーマンスは、テストとして比較してORDER BY vote_sum ASCどうですか

于 2012-12-17T22:38:39.823 に答える