FULLTEXT 索引需要更长的时间来执行

Posted

技术标签:

【中文标题】FULLTEXT 索引需要更长的时间来执行【英文标题】:FULLTEXT index takes longer to execute 【发布时间】:2016-11-22 07:52:01 【问题描述】:

以下查询需要 1.1s 执行,EXPLAIN 显示使用了 FULLTEXT 索引:

SELECT SQL_NO_CACHE COUNT(*)
FROM e_entity
WHERE meta_oid=336799 AND MATCH(sIndex07) AGAINST ("#UPR-1393#" IN NATURAL LANGUAGE MODE)

EXPLAIN:
id: 1
select_type: SIMPLE
table: e_entity
type: fulltext
possible_keys: App_Parent,sindex07
key: sIndex07
key_len: 0
ref: (NULL)
rows: 1
extra: Using Where

sIndex07 列上有一个 FULLTEXT 索引。但是,当这个 FULLTEXT 索引被删除并替换为通常的 KEY 索引时,查询:

SELECT SQL_NO_CACHE COUNT(*)
FROM e_entity
WHERE meta_oid=336799 AND sIndex07 LIKE "%#UPR-1393#%"

EXPLAIN:
id: 1
select_type: SIMPLE
table: e_entity
type: ref
possible_keys: App_Parent
key: App_Parent
key_len: 4
ref: const
rows: 331283
extra: Using Where

CREATE TABLE `e_entity` (
`OID` int(11) NOT NULL AUTO_INCREMENT,
`E_E_OID` int(11) DEFAULT NULL,
`UNIQUE_IDX` int(11) NOT NULL,
`APP_OID` int(11) NOT NULL,
`META_OID` int(11) NOT NULL,
`STORE_DATE` datetime NOT NULL,
`REL_DISPLAY` varchar(1024) NOT NULL,
`sIndex01` varchar(1024) NOT NULL,
`SINDEX02` varchar(1024) NOT NULL,
`SINDEX03` varchar(1024) NOT NULL,
`SINDEX04` varchar(1024) NOT NULL,
`SINDEX05` varchar(1024) NOT NULL,
`SINDEX06` varchar(1024) NOT NULL,
`sIndex07` varchar(1024) NOT NULL,
`SINDEX08` varchar(1024) NOT NULL,
`SINDEX09` varchar(1024) NOT NULL,
`sIndex10` varchar(1022) NOT NULL,
`SINDEX11` varchar(1024) NOT NULL,
`SINDEX12` varchar(1024) NOT NULL,
`SINDEX13` varchar(1024) NOT NULL,
`SINDEX14` varchar(1024) NOT NULL,
`sIndex15` varchar(1022) NOT NULL,
`SINDEX16` varchar(1024) NOT NULL,
`SINDEX17` varchar(1024) NOT NULL,
`SINDEX18` varchar(1024) NOT NULL,
`SINDEX19` varchar(1024) NOT NULL,
`SINDEX20` varchar(1024) NOT NULL,
`NINDEX01` double NOT NULL,
`NINDEX02` double NOT NULL,
`NINDEX03` double NOT NULL,
`NINDEX04` double NOT NULL,
`NINDEX05` double NOT NULL,
`NINDEX06` double NOT NULL,
`NINDEX07` double NOT NULL,
`NINDEX08` double NOT NULL,
`NINDEX09` double NOT NULL,
`NINDEX10` double NOT NULL,
`DINDEX01` datetime NOT NULL,
`DINDEX02` datetime NOT NULL,
`DINDEX03` datetime NOT NULL,
`DINDEX04` datetime NOT NULL,
`DINDEX05` datetime NOT NULL,
`DINDEX06` datetime NOT NULL,
`DINDEX07` datetime NOT NULL,
`DINDEX08` datetime NOT NULL,
`DINDEX09` datetime NOT NULL,
`DINDEX10` datetime NOT NULL,
`FREETEXT` mediumtext NOT NULL,
`UID` int(11) DEFAULT NULL,
PRIMARY KEY (`OID`),
KEY `E_E_OID` (`E_E_OID`),
KEY `sIndex01` (`SINDEX01`),
KEY `sIndex02` (`SINDEX02`),
KEY `sIndex03` (`SINDEX03`),
KEY `sIndex04` (`SINDEX04`),
KEY `sIndex05` (`SINDEX05`),
KEY `sIndex06` (`SINDEX06`),
FULLTEXT `sIndex07` (`SINDEX07`),
KEY `sIndex08` (`SINDEX08`),
KEY `sIndex09` (`SINDEX09`),
KEY `sIndex10` (`SINDEX10`),
KEY `sIndex11` (`SINDEX11`),
KEY `sIndex12` (`SINDEX12`),
KEY `sIndex13` (`SINDEX13`),
KEY `sIndex14` (`SINDEX14`),
KEY `sIndex15` (`SINDEX15`),
KEY `sIndex16` (`SINDEX16`),
KEY `sIndex17` (`SINDEX17`),
KEY `sIndex18` (`SINDEX18`),
KEY `sIndex19` (`SINDEX19`),
KEY `sIndex20` (`SINDEX20`),
KEY `dIndex01` (`DINDEX01`),
KEY `dIndex02` (`DINDEX02`),
KEY `dIndex03` (`DINDEX03`),
KEY `dIndex04` (`DINDEX04`),
KEY `dIndex05` (`DINDEX05`),
KEY `dIndex06` (`DINDEX06`),
KEY `dIndex07` (`DINDEX07`),
KEY `dIndex08` (`DINDEX08`),
KEY `dIndex09` (`DINDEX09`),
KEY `dIndex10` (`DINDEX10`),
KEY `nIndex01` (`NINDEX01`),
KEY `nIndex02` (`NINDEX02`),
KEY `nIndex03` (`NINDEX03`),
KEY `nIndex04` (`NINDEX04`),
KEY `nIndex05` (`NINDEX05`),
KEY `nIndex06` (`NINDEX06`),
KEY `nIndex07` (`NINDEX07`),
KEY `nIndex08` (`NINDEX08`),
KEY `nIndex09` (`NINDEX09`),
KEY `nIndex10` (`NINDEX10`),
KEY `rel_display` (`REL_DISPLAY`),
KEY `App_Parent` (`META_OID`),
) ENGINE=InnoDB AUTO_INCREMENT=1245843 DEFAULT CHARSET=utf8    ROW_FORMAT=COMPRESSED

只需 0.6 秒即可完成。我在其他问题中看到 MATCH 子句需要嵌套,但我不确定如何将其嵌套在 COUNT 语句中。 此外,当删除 meta_oid 子句时,使用 FULLTEXT 索引运行的查询比第二个查询快 50%,所以虽然 FULLTEXT 似乎是一个好处,但我在将它与其余部分结合使用时遇到了困难meta_oid 已编入索引,sIndex07 也是 varchar(1024),数据库大小为 4.5Gb。

编辑: FULLTEXT 搜索速度较慢的原因是搜索词中有一个连字符,因此在我的特定情况下返回的数据集比 LIKE 运算符大得多。没有连字符的搜索确实使用了FULLTEXT,并且性能比LIKE 好一百倍

我将在不到 24 小时内将赏金奖励给可以使用连字符进行搜索而无需重新编译 mysql 二进制文件的人,从而使FULLTEXT 更快,这是问题的最初目的。

【问题讨论】:

请提供SHOW CREATE TABLE;我们不知道 App_Parent 索引了哪些列。 对于这么大的表,您是否运行了两次查询?这将避免缓存效应,这通常会使时间偏差 10 倍。 问题已编辑 是“阵列”的东西散布在 4*10 列上吗?它们不能移动到另一个表中的 10 行 * 4 列吗? 令我惊讶的是,您能够在具有如此多索引的表中插入任何内容。 【参考方案1】:

MySQL 使用查询计划器来确定如何最好地解决查询。通常,只使用一个索引来解析“WHERE”组件,并且这是从可能适用的可能索引列表中选择的。可能的索引列表由 EXPLAIN 在possible_keys 下显示,所选索引由key 标识。为了做出选择,MySQL 会考虑许多因素,例如索引的唯一性,以尝试确定哪个索引可以最好地缩小可能结果的列表。

一旦它缩小了索引中匹配的行列表。它将Use Where 读取这些行并根据 WHERE 子句中的剩余条件检查它们。

在这个操作中有很多边缘情况,有时 MySQL 可能会做出一个糟糕的选择。 MySQL 5.1 中的查询计划器发生了显着变化,并且在它再次变得良好之前需要几个版本。

如果不检查您的数据,就很难说明 MySQL 做出错误选择的原因。虽然这样做:

SELECT SQL_NO_CACHE COUNT(*)
FROM e_entity
WHERE MATCH(sIndex07) AGAINST ("#UPR-1393#" IN NATURAL LANGUAGE MODE);

会告诉你 MySQL 使用全文索引从数据库中读取了多少行。在您的原始查询中,它必须根据“meta_oid=336799”解析所有这些行以确定最终计数。

SELECT SQL_NO_CACHE COUNT(*)
FROM e_entity
WHERE meta_oid=336799

会告诉您使用META_OID 上的App_Parent 索引MySQL 正在读取多少实际行。在您的第二个查询中,它必须针对 LIKE "%#UPR-1393#%" 解析这些行

如果后一个查询产生的数字比第一个低得多,那么它可以解释为什么使用App_Parent 而不是全文索引会更快。

【讨论】:

这并不能解释为什么使用FULLTEXT 操作符使用AND 比使用LIKE 慢。 FULLTEXTLIKE 返回相同的行数 @Rafael Diaz,在您的问题中,您说没有 meta_oid 子句的 FULLTEXT 查询要快 50%。速度差异的原因是 MySQL 解析查询的方式,而不是索引本身。为了公平比较,只做 FULLTEXT v LIKE 没有其他 WHERE 条件,知道结果会很有趣。 结果确实是:当meta_oid 上没有AND 运算符时,FULLTEXT 查询的执行速度提高了 50% 这就是我所期望的。尝试强制第一个查询使用与第二个相同的索引。它应该更快,并证明有时 MySQL 会做出错误的选择:SELECT SQL_NO_CACHE COUNT(*) FROM e_entity FORCE INDEX (App_Parent) WHERE meta_oid=336799 AND MATCH(sIndex07) AGAINST ("#UPR-1393#" IN NATURAL LANGUAGE MODE) 我刚刚意识到查询甚至不相等,它们返回的行数不同,这就是时间差异的原因。我不知道如何让MATCH AGAINST 表现得像LIKE【参考方案2】:

MATCH(sIndex07) 需要一个FULLTEXT 索引才能有效地工作。否则它和LIKE '%string%' 一样糟糕(或更糟糕?)。

更多

没有FULLTEXT,这应该会使LIKE 运行得更快:INDEX(meta_oid=336799, sIndex07)

【讨论】:

INDEX时的计时吗?或者什么时候是FULLTEXT 当时是FULLTEXT 复合索引在这种情况下也好不到哪里去

以上是关于FULLTEXT 索引需要更长的时间来执行的主要内容,如果未能解决你的问题,请参考以下文章

使用 ARM NEON 执行比 C 代码需要更长的时间

SQL-query 在代码中比直接查询 db 花费更长的时间

为啥 c 中的幂函数需要比预期更长的时间

异步比顺序执行花费更长的时间

为什么g ++需要更长的时间来编译 用-std = c ++ 11?

如何解决需要比 SQL 中允许的更长的列名?