3

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

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

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

  1. 简单,非唯一索引user_id
    它将仅过滤特定的用户消息,然后完全扫描特定的单词。
  2. 消息上的FULLTEXT索引
    这将查找来自所有用户的所有消息,然后按 ID 过滤,在大量用户的情况下似乎效率很低。
  3. user_idmessage上的复合索引
    因此为每个用户分别创建全文索引树,因此可以单独搜索它们。在查询期间,系统按ID过滤消息,然后对索引中的剩余行执行文本搜索。

AFAIK 最后一个是不可能的。那么我假设我将使用第一个选项,如果有几千个用户,它会表现得更好吗?

如果每个都有约 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 只会使用两个索引中的一个,并且会做一次线性扫描来完成第二个过滤器,但它会估计每个索引的有效性并选择最优的一个。

注意:只有在你能承受两个索引的开销(更慢的插入/更新/删除)的情况下才这样做。此外,如果您知道每个用户只会收到几条消息,那么使用简单的索引并在应用程序层中执行正则表达式或类似的东西可能是有意义的。

于 2013-09-23T00:21:10.527 回答
2

@Alden Quimby 的答案就其而言是正确的,但还有更多的故事,因为 MySQL 只会尝试选择最佳索引,并且由于全文索引与优化器交互的方式,它做出该决定的能力是有限的.

实际发生的是这样的:

如果指定的 user_id 存在于表中的 0 或 1 个匹配行中,优化器将意识到这一点,并将选择 user_id 作为该查询的索引。快速执行。

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

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

出现这种情况的原因是全文索引没有将任何有意义的统计信息返回给优化器。它只是说“是的,我认为查询可能只需要我检查 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”出现在两列的组合全文索引中的那些行,但您仍然希望在那里也有整数过滤器以确保尽管消息中随机出现“user_id_500”,但最终结果受到限制。

于 2013-09-23T01:00:03.620 回答