MySQL中的复合FULLTEXT索引

时间:2013-09-22 22:59:05

标签: mysql full-text-search full-text-indexing

我想让系统whitch允许搜索特定用户的用户消息。 假设有下表

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

那么我应该在这里使用什么样的索引,如果我想搜索来自用户1的所有消息,包含单词'foo'。

  1. 简单,非唯一索引 user_id
    它将仅过滤特定用户消息,然后仅扫描特定单词的完整扫描。
  2. 消息上的
  3. FULLTEXT 索引 这将查找来自所有用户的所有消息,然后按ID过滤,在大量用户的情况下效率非常低。
  4. user_id 消息的
  5. comopound 索引
    因此,为每个用户单独创建全文索引树,因此可以单独搜索它们。在查询系统中,按 ID 过滤消息,然后对索引中的其余行执行文本搜索。
  6. A.F.A.I.K。最后一个是不可能的。那么我假设我将使用第一个选项,它会在几千个用户的情况下表现更好?

    如果每个消息都有~100条消息,那么完整的迭代不会花费太多资源吗?

    也许我可以在邮件中加入用户名并使用BOOLEAN全文搜索模式,但我认为它比使用索引 user_id 要慢。

2 个答案:

答案 0 :(得分:2)

您应该在message上添加全文索引,在user_id上添加常规索引,并使用查询:

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

你是对的,你不能做选项3.但是,不要试图在1和2之间选择,让MySQL为你做的工作。 MySQL将只使用两个索引中的一个,并将进行线性扫描以完成第二个过滤器,但它将估计每个索引的有效性并选择最佳索引。

注意:只有在能够负担两个索引(较慢的插入/更新/删除)的开销时才执行此操作。此外,如果您知道每个用户只会有一些消息,那么可以使用简单的索引并在应用程序层或类似的东西中执行正则表达式。

答案 1 :(得分:2)

@Alden Quimby的回答是正确的,但是故事还有更多,因为MySQL只会尝试来选择最佳指数,并且它做出决定的能力有限因为全文索引与优化器交互的方式。

实际发生的是:

如果指定的user_id存在于表中的0或1个匹配行中,优化器将实现此目的,并将选择user_id作为该查询的索引。快速执行。

否则,优化器将选择全文索引,过滤与fulltext索引匹配的每一行,以消除不包含与WHERE子句匹配的user_id的行。不太快。

所以这不是真正的“最佳”路径。它更像是全文,有一个很好的优化,以避免在我们知道表格中几乎没有任何兴趣的条件下进行全文搜索。

这种情况发生故障的原因是全文索引没有向优化器提供任何有意义的统计信息。它只是说“是的,我认为查询应该只需要我检查1行”......当然,这对于优化器来说非常好,所以全文索引赢得了最低成本的出价,除非索引带有整数价值也相对较低或较低。

不过,这并不意味着我不会先这样尝试。

有另一种选择,这将与全文查询效果最好IN BOOLEAN MODE,那就是创建另一个列,你会喜欢的东西CONCAT填充(“USER_ID _”,USER_ID)或类似的东西,然后宣布2 -column全文索引。

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”出现在两列的组合全文索引中的行,但是您仍然希望在那里使用整数过滤器尽管消息中出现“user_id_500”随机出现,但要确保最终结果受到限制。