3

ユーザーメッセージを特定のユーザーで検索できるシステムを作りたいです。次の表があると仮定します

create table messages(
  user_id int,
  message nvarchar(500));

'foo' という単語を含むユーザー 1 からのすべてのメッセージを検索するには、ここでどのようなインデックスを使用すればよいでしょうか。

  1. シンプルで一意でないインデックスuser_id
    特定のユーザー メッセージのみをフィルタリングし、特定の単語をフル スキャンします。
  2. メッセージのFULLTEXTインデックスは、すべてのユーザーからのすべてのメッセージ
    を検索し、ID でフィルター処理します。ユーザー数が多い場合、非常に非効率的です。
  3. user_idmessageの両方に複合インデックスを作成
    するため、ユーザーごとに個別に全文インデックス ツリーが作成されるため、ユーザーを個別に検索できます。クエリ中、システムはメッセージをIDでフィルタリングし、インデックス内の残りの行に対してテキスト検索を実行します。

私の知る限り、最後のものは不可能です。それでは、最初のオプションを使用すると仮定します。数千人のユーザーの場合、パフォーマンスが向上しますか?

また、それぞれに最大 100 個のメッセージがある場合、完全な反復は多くのリソースを消費しませんか?

おそらく、ユーザー名をメッセージに含めて BOOLEAN 全文検索モードを使用できますが、インデックス付きのuser_idを使用するよりも遅くなると思います。

4

2 に答える 2

2

にフルテキスト インデックスを追加しmessage、 に通常のインデックスを追加してuser_id、次のクエリを使用する必要があります。

SELECT *
FROM messages
WHERE MATCH(message) AGAINST(@search_query)
AND user_id = @user_id;

オプション 3 を実行できないのはあなたの言うとおりです。ただし、オプション 1 とオプション 2 のどちらかを選択しようとするのではなく、MySQL に作業を任せてください。MySQL は 2 つのインデックスの 1 つだけを使用し、線形スキャンを実行して 2 番目のフィルターを完成させますが、各インデックスの有効性を推定し、最適なものを選択します。

注: これは、2 つのインデックスのオーバーヘッドを許容できる場合にのみ行ってください (挿入/更新/削除が遅くなります)。また、各ユーザーが少数のメッセージしか持たないことがわかっている場合は、単純なインデックスを使用してアプリケーション層で正規表現を実行するなどの方法が理にかなっています。

于 2013-09-23T00:21:10.527 に答える
2

@Alden Quimby の答えは、これまでのところ正しいですが、MySQL は最適なインデックスのみを選択しようとするため、その決定を行う能力はフルテキスト インデックスがオプティマイザとやり取りする方法のために制限されるため、話にはさらに多くのことがあります。 .

実際に何が起こるかは次のとおりです。

指定された user_id がテーブル内の 0 または 1 つの一致する行に存在する場合、オプティマイザーはこれを認識し、そのクエリのインデックスとして user_id を選択します。高速実行。

それ以外の場合、オプティマイザはフルテキスト インデックスを選択し、フルテキスト インデックスに一致するすべての行をフィルタリングして、WHERE 句に一致する user_id を含まない行を除外します。それほど速くはありません。

したがって、それは本当に「最適な」パスではありません。これはより全文に似ており、テーブルにほとんど関心がないことがわかっている 1 つの条件下で全文検索を回避するための適切な最適化が行われています。

これが失敗する理由は、フルテキスト インデックスが意味のある統計情報をオプティマイザーに返さないためです。「ええ、クエリではおそらく1行をチェックするだけでよいと思います」と言うだけです...もちろん、これはオプティマイザを大いに喜ばせるので、整数のインデックスがない限り、フルテキストインデックスが最低コストの入札に勝ちます値も比較的低くまたは低くなります。

それでも、それは私が最初にこの方法を試さないという意味ではありません.

フルテキスト クエリIN BOOLEAN MODEで最適に機能する別のオプションがあります。それは、CONCAT('user_id_',user_id) などを入力する別の列を作成し、2 列のフルテキスト インデックスを宣言することです。

filter_string VARCHAR(48) # populated with CONCAT('user_id_',user_id);
....
FULLTEXT KEY (message,filter_string)

次に、クエリですべてを指定します。

SELECT ...
 WHERE user_id = 500 AND
 MATCH (message,filter_string) AGAINST ('+kittens +puppies +user_id_500' IN BOOLEAN MODE);

これで、フルテキスト インデックスは、子猫、子犬、および「user_id_500」が 2 つの列の結合されたフルテキスト インデックスに表示される行のみを照合する役割を果たしますが、整数フィルターをそこにも配置して、メッセージに「user_id_500」がランダムに出現しても、最終結果は制限されます。

于 2013-09-23T01:00:03.620 に答える