【问题标题】:Performance of LIKE queries on multmillion row tables, MySQL对百万行表、MySQL 的 LIKE 查询的性能
【发布时间】:2012-07-10 06:16:01
【问题描述】:

从任何有实际经验的人那里,如果字段有一个普通的 INDEX,LIKE 查询在 MySQL 中如何在数百万行表上执行,在速度和效率方面?

是否有更好的替代方法(不会过滤掉结果,如 FULLTEXT 50% 规则)对数百万行表执行数据库字段搜索?

示例:

Schema (comments table)

id (PRIMARY) title(INDEX) content time stamp

Query

SELECT * FROM 'comments' WHERE 'title' LIKE '%query%'

【问题讨论】:

  • 请举例说明有问题的 LIKE 子句和架构。
  • 在开头使用通配符会使情况变得更糟。示例:名称如 '%jim%'
  • 使用示例查询和架构更新

标签: mysql database performance processing-efficiency


【解决方案1】:

如果您在模式的开头有 %,LIKE 将执行全表扫描。

您可以在布尔(而不是自然语言)模式下使用 FULLTEXT 来避免 50% 规则。

布尔全文搜索具有以下特点:

他们不使用 50% 的阈值。

http://dev.mysql.com/doc/refman/5.0/en/fulltext-boolean.html

【讨论】:

  • 很好的答案和@roozbubu 我会考虑改变这个问题的答案。应该使用 FULLTEXT 来解决这个问题。
【解决方案2】:

来自任何有实际经验的人,LIKE 查询在 数百万行表上的 MySQL,在速度和效率方面,如果 该字段有一个普通的 INDEX?

不太好(我想我的搜索量在 900k 范围内,不能说我有数百万行 LIKE 的经验)。

通常您应该尽可能地限制搜索,但这取决于表结构和应用程序用例。

此外,在某些 Web 用例中,可以通过一些技巧来实际提高性能和用户体验,例如为单独的关键字编制索引并创建关键字表和 rows_contains_keyword (id_keyword, id_row) 表。关键字表与 AJAX 一起使用以建议搜索词(简单词)并将它们编译为整数 - id_keywords。在这一点上,找到包含这些关键字的行变得真的快。一次更新一行表格也很高效;当然,批量更新成为一个明确的“不要”。

如果只使用 + 运算符,这与 full text MATCH..IN BOOLEAN MODE 已经完成的操作没有太大不同:

SELECT * FROM arts WHERE MATCH (title) AGAINST ('+MySQL +RDBMS' IN BOOLEAN MODE);

您可能需要一个 InnoDB 表来执行此操作:

布尔全文搜索具有以下特点:

  • 它们不会自动按照相关性递减的顺序对行进行排序。 ...
  • InnoDB 表需要 MATCH() 表达式的所有列上的 FULLTEXT 索引才能执行布尔查询。即使没有 FULLTEXT 索引,针对 MyISAM 搜索索引的布尔查询也可以工作,尽管以这种方式执行的搜索会很慢。 ...
  • 他们不使用适用于 MyISAM 搜索索引的 50% 阈值。

您能否提供有关具体案例的更多信息?

更新:AJAX 方式

设置:您将所有titles 分解为单词。这很快就会给你一个title_words( id integer not null autoincrement, word varchar(50) ) 和一个大的title_contains_word ( word_id integer, title_id integer ) 表。

如果您有 1000 万个标题,平均有 4 个单词(对于书籍来说似乎是这样,对于论文来说则不太可能),您可以期望一个 5000 行的 title_words 表和一个包含两个 INTEGER 列的 4000 万行的表;这大约是 400 MB 的额外数据。

对于搜索,用户开始输入一个词,您可以从标题词中自动完成。完成此操作后,查询将变为单词 ID 列表;当然,不在任何标题中的单词甚至都不能输入,因此立即给出否定结果,并且免费。

现在可以通过多种方式进行实际搜索,但我喜欢的一种方式是在每个用户选择之后运行SELECT COUNT(*) FROM title_contains_word WHERE word_id={id}真正的搜索开始之前。

这允许从 rarest 单词开始构建复合查询或公共表表达式。实际上,如果任何单词的计数低于 20,您可以选择所有这些(平均)八个 TCW 行并获取所有相关单词的 ID,然后简单地验证(在 MySQL 之外)是否有一个标题 ID,例如您的查询的所有 wordID 都存在一对 (titleID, wordID)。

即使你不得不诉诸最粗略的形式,

SELECT a.title_id 
FROM title_contains_word AS tcw1
JOIN title_contains_word AS tcw2 USING (title_id)
JOIN title_contains_word AS tcw3 USING (title_id)
JOIN title_contains_word AS tcw4 USING (title_id)
...
WHERE (tcw1.word_id = {id1})
  AND (tcw2.word_id = {id2})
  ...

JOIN 将由非常小的虚拟缓冲表组成,扫描时间非常短。

获得所有相关的标题 ID 后,您就可以使用主键 title_id 从数百万行的大型数据库中直接运行 SELECT。最后的搜索也应该很快。

【讨论】:

  • 嗯...非常有趣的想法,会尝试一下!
  • 这种方法有教程吗?我希望更详细地阅读它,陷阱等。谢谢。
  • 可能,但我目前手头没有任何东西。今晚我会试着想出点什么来。但是,您最好的选择是访问 Google 并寻找一些真实的体验。
【解决方案3】:

我建议您也通过其他子句(例如日期范围)限制您的查询,因为 LIKE '%something' 保证您进行全表扫描

【讨论】:

  • 换句话说 like '&...' 和“数百万行表”,不要混入 MySQL :-(
【解决方案4】:

使用 Workbench,在您的 SELECT 之前使用 EXPLAIN 来测试 LIKE 的不同条件使用,有和没有 INDEX,在搜索词的不同部分使用通配符。你会根据你的测试得出你自己的结论,因为每个案例都是一个特定的案例。

【讨论】:

    【解决方案5】:

    您可以执行 Subselect 以获取最新的寄存器。

    select s.* from (select * from my_table order by "create" desc  limit 10) as s
    where   s.event like '%status%'   
    

    【讨论】:

      猜你喜欢
      • 2018-12-26
      • 2023-01-05
      • 2021-06-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-02
      • 1970-01-01
      • 2017-10-01
      相关资源
      最近更新 更多