23

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

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

例子:

Schema (comments table)

id (PRIMARY) title(INDEX) content time stamp

Query

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

5 回答 5

17

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

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

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

他们不使用 50% 的阈值。

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

于 2012-07-10T17:52:02.007 回答
14

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

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

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

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

如果仅使用 + 运算符,这与全文 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 的额外数据。

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

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

这允许从最稀有的单词开始构建复合查询或公共表表达式。实际上,如果任何单词的计数低于 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 。最后一次搜索也应该很快。

于 2012-07-10T17:56:42.710 回答
8

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

于 2012-07-10T17:47:34.560 回答
0

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

于 2019-12-05T10:56:19.710 回答
0

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

select s.* from (select * from my_table order by "create" desc  limit 10) as s
where   s.event like '%status%'   
于 2021-05-31T16:24:15.970 回答