【发布时间】:2021-03-06 14:58:06
【问题描述】:
我觉得我已经在 Stackoverflow 上阅读了 20 篇关于此的帖子,但仍然不太确定如何回答我的问题。
今天我有一个名为documents 的表,大小约为 10GB,有 400 万行。该表是一个多租户应用程序,因此有一个名为system_id 的列对每个租户进行分段。今天我有一个类似的查询
SELECT *
FROM documents
WHERE system_id = 1 AND
status = 100 AND
MATCH(content,notes) AGAINST ('office' IN BOOLEAN MODE);
我在这个表上创建了两个索引:
documents_index BTREE system_id,status
documents_fulltext_index FULLTEXT content,notes
据我了解,MySQL 优化器只会在此处运行其中一个索引,当我执行explain 时,我知道它将使用FULLTEXT 索引。这是否意味着查询将运行全表扫描并检查每一行是否有“office”,然后根据system_id 和status 过滤掉?在阅读this post 之后,我看到您应该尝试隔离 FULLTEXT,因为我想同时使用system_id 和status 和FULLTEXT 的索引(这甚至可能吗?)
SELECT B.id
FROM (
SELECT id
FROM documents
WHERE system_id = 95 AND
status = 900
) A
LEFT JOIN documents B using (id)
WHERE MATCH(content,notes) AGAINST ('office' IN BOOLEAN MODE);
这显着加快了查询速度,但当我解释时它显示:
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: documents
partitions: NULL
type: ref
possible_keys: documents_table_index
key: documents_table_index
key_len: 4
ref: const
rows: 864
filtered: 10.00
Extra: Using where; Using index
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: B
partitions: NULL
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: documents.id
rows: 1
filtered: 11.11
Extra: Using where
2 rows in set, 1 warning (1.09 sec)
所以现在它正在使用system_id 索引,但它看起来不像在另一个查询中使用FULLTEXT 索引,因为它显示PRIMARY。这个查询根本没有使用FULLTEXT 索引吗?
我想要布尔搜索功能,但如果我使用 LIKE 而不是 FULLTEXT,对于拥有大约 10 000 条记录的租户来说,速度要快得多。但是,如果我尝试使用 LIKE 在文档表上查询具有例如 400 000 条记录的租户,那么 FULLTEXT 会更快。 performance analysis 对此进行了解释。
我希望它对拥有 10 000 个文件和 400 000 个文件的租户都有好处,我应该使用哪些文件? FULLTEXT 还是 LIKE?如果我能让它们具有相同的性能,我想使用FULLTEXT,因为您可以在布尔模式下执行额外的操作。
更新 1
从下面的 Rolando 和 Rick James 那里得到了一些很好的答案。
我正在使用 InnoDB 和 mysql 5.7
我有一件事很难得到。在我的情况下,感觉必须先使用system_id, status 索引,因为它会从 400 万行中“提取”出感兴趣的 10 000 行。然后您将对这 10 000 行运行 FULLTEXT 搜索。但是从我所读到的,这是不可能的?
罗兰多
我现在明白你在你的索引上强加了FULLTEXT,而我在我的上强加了system_id, status。当我像你说的那样翻转查询时,运行查询需要 3.4 秒,而强制 system_id, status 索引需要 0.54 秒。我是否正确理解 FULLTEXT 基本上是一种带有所有单词作为标记的树,这意味着当我搜索 office 时需要更长的时间,因为有很多带有 office 的文档在里面?与我搜索 ABC123(我知道它只在 1 个文档中)相比,它的速度要快得多。
我也尝试了您的其他查询 Rolando,这需要 1.3 秒,所以最快的查询仍然是我使用 system_id, status 索引的查询。至少对于像office 这样的词。我想知道该查询实际上是如何工作的。首先它使用system_id, status 索引来获取我感兴趣的10 000 行。之后它在每一行上运行布尔搜索(即不使用索引)?这意味着拥有 400 000 个文档的客户 (system_id) 的搜索速度会比搜索 10 000 个文档的速度慢(即它会随文档数量线性扩展)?这里和LIKE 有什么区别?是LIKE 会查找“字符”而FULLTEXT 仍会查找单词并使用布尔搜索功能吗?
因为如果我现在搜索 "office sverige" 而不仅仅是 office,则强制使用 system_id, status 索引的查询需要 17 秒,而仅使用 LIKE '%office sverige%' 运行简单查询需要 0.11 秒。
里克·詹姆斯
我看到您回答了我的一个问题,即FULLTEXT 变慢了,因为我敢打赌,我的数据库中有很多文档中都包含office 这个词。当我搜索我知道非常独特的东西时,查询会变得更快。我不知道我的客户会确切搜索什么,我不想告诉他们更常见的词需要更长的时间来搜索。就是这样吗?
我之所以没有选择LIKE,是因为正如你所说。前面和后面的通配符会变得非常慢,因为它必须搜索所有可能性。我认为在这种情况下它会更快,因为它使用了system_id, status 索引并且office 是一个非常常见的词。
根据您从我的问题中了解的要求,您能否为我指明运行什么查询/解决方案的正确方向?我也许可以实现类似 Sphynx och ElasticSearch 或其他的东西,但我真的不想这样做。现在我倾向于强制使用system_id, status 索引,因为我认为这在所有不同情况下都会表现最好。但是,如果我应该运行LIKE 或MATCH 进行查询?非常感谢您的帮助!
更新 2
里克·詹姆斯 非常感谢您的回复,我将在下面回答您的问题:
status 列可能不适合分区。在 99% 的行中,它的值将是 900。当一行不是状态 900 时,表示它现在正在处理中,并且该处理只完成一次,然后变为 900。
我总是将= 用于system_id 和status。我认为也许我可以基于system_id 进行分区,因为就像我说这是一个多租户应用程序,所以我所有的租户文档都在documents 表中,然后我有一个像system_id 这样的键(应该有被tenant_id) 分开了。
今天documents 表中有大约 400 万行,但它的增长速度很快。假设今天每天大约有 2-3000 个新文档进来,所以我想构建一个在有 2000 万个文档时可行的解决方案。
我的大多数租户最多会拥有 10-20 000 个文档,但也可能会有 500 000 个文档。这些人可以接受慢一点的搜索,但不应该太多。
我一直在想一个解决方案,虽然很激烈,但不是让我的所有租户都在同一个数据库中,而是拥有一个多租户应用程序,每个租户都有自己的数据库。然后我不必按system_id 过滤,而可以只使用FULLTEXT 索引。然而,这确实涉及大量的重建工作,我不想走那条路。
【问题讨论】:
-
如果您知道查询将搜索的文档数量,那么您可以决定使用哪种搜索类型。我们无法告诉您哪种方法更适合您的特定情况,您需要对其进行测试。
-
我也一直在考虑这个问题@Shadow,但我不想走那条路,因为这意味着有些人将拥有二进制搜索功能,有些人将拥有
LIKE,为不同的租户提供不同的解决方案。最好的办法是使用system_id索引不必扫描整个表,然后使用FULLTEXT搜索那个较小的结果集。 -
然后使用子查询返回你想要的sysids,并在外部查询中使用全文搜索。
-
单个词搜索对全文索引无效,使用
LIKEthtat 对单个词会更快 -
@Shadow 会是什么样子?我试过这个:
SELECT id FROM ( SELECT * FROM documents WHERE system_id = 1 AND status = 100 ) A WHERE MATCH(content,notes) AGAINST ('office' IN BOOLEAN MODE);但这比我展示的要慢。另外explain表明它只是使用FULLTEXT索引。 @nbk 它并不总是一个词。这里只是为了展示。
标签: mysql