MySQL: LIKE 123 和 = 123 关于 INDEX 用法的区别
MySQL: Difference between LIKE 123 and = 123 regarding INDEX usage
我遇到了一个非常奇怪的行为,结果证明这是在我的 where 条件下使用正确的运算符的问题。
假设以下 table 结构具有几百万个条目:
CREATE TABLE `obj` (
`obj__id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`obj__obj_type__id` int(10) unsigned DEFAULT NULL,
`obj__title` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`obj__const` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`obj__description` text COLLATE utf8_unicode_ci,
`obj__created` datetime DEFAULT NULL,
`obj__created_by` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`obj__updated` datetime DEFAULT NULL,
`obj__updated_by` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`obj__property` int(10) unsigned DEFAULT '0',
`obj__status` int(10) unsigned DEFAULT '1',
`obj__sysid` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`obj__scantime` datetime DEFAULT NULL,
`obj__imported` datetime DEFAULT NULL,
`obj__hostname` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`obj__undeletable` int(1) unsigned NOT NULL DEFAULT '0',
`obj__rt_cf__id` int(11) unsigned DEFAULT NULL,
`obj__cmdb_status__id` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`obj__id`),
KEY `obj_FKIndex1` (`obj__obj_type__id`),
KEY `obj_ibfk_2` (`obj__cmdb_status__id`),
KEY `obj__sysid` (`obj__sysid`),
KEY `obj__title` (`obj__title`),
KEY `obj__const` (`obj__const`),
KEY `obj__hostname` (`obj__hostname`),
KEY `obj__status` (`obj__status`),
KEY `obj__updated_by` (`obj__updated_by`)
) ENGINE=InnoDB AUTO_INCREMENT=7640131 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
一个非常简单的 select,有两个条件,按 obj__title 排序,限制为 500,执行安静缓慢(500 毫秒):
SELECT SQL_NO_CACHE * FROM obj WHERE (obj__status = 2) AND (obj__obj_type__id = 59) ORDER BY obj__title ASC LIMIT 0, 500;
没有 "ORDER BY obj__title" 它运行起来就像一个魅力(<1ms)。
EXPLAIN SELECT 告诉我 MySQL 正在执行文件排序而不是使用 obj__title 索引。所以,好吧,很明显这个查询很慢:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE obj index_merge obj_FKIndex1,obj__status obj_FKIndex1,obj__status 5,5 NULL 1336 Using intersect(obj_FKIndex1,obj__status); Using where; Using filesort
当我强制索引 obj__title 与 FORCE 或 USE INDEX 一起使用时,mysql 没有使用其他索引,导致性能再次很差。不过没关系,很明显,性能不佳与这两个条件的组合和顺序有关。
既然我花了几个小时研究优化这个查询,我想出了一个非常简单的交换:我将我的条件运算符从 = 交换为 LIKE。所以我的查询是这样的:
EXPLAIN SELECT SQL_NO_CACHE * FROM obj WHERE (obj__status LIKE 2) AND (obj__obj_type__id LIKE 59) ORDER BY obj__title ASC LIMIT 0, 500;
这就是发生的事情..
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE obj index obj_FKIndex1,obj__status obj__title 768 NULL 500 Using where
查询性能为 150 毫秒。我真的被吓到了。
我对速度不是很满意,但至少它的表现还不错。
但我真正想知道的是为什么LIKE使用索引而=不使用索引?我在 MySQL 文档中没有找到任何相关提示。关于 LIKE 不区分大小写和 LIKE 对 VARCHARS > 255 或任何其他 CHAR 或 TEXT 字段的行为有点不同的一些注意事项。没有关于它的整数行为的单个词。
有人可以阐明这种情况吗?任何可以加快查询速度的数据库设计或查询技巧也非常受欢迎!
对于此查询:
SELECT SQL_NO_CACHE *
FROM obj
WHERE (obj__status = 2) AND (obj__obj_type__id = 59)
ORDER BY obj__title ASC
LIMIT 0, 500;
最好的索引是obj(obj__status, obj__obj_type__id, obj__title)
。
否则,我希望在两个 where
字段之一上有一个索引。
但是,当您使用 like
时,您是在将数字与字符串进行比较。这通常会阻止使用索引。唯一可能的索引是 order by
,它恰好适用于您的情况。
但是,适当的索引应该有更好的性能。
ORDER BY
必须在LIMIT
之前满足。如果行过多,并且 MySQL 执行额外列中显示的排序操作 ("Using filesort"),那可能会很昂贵。
通过使用前导列为 obj__title
的索引,MySQL 也可以在不执行排序操作的情况下满足 ORDER BY obj__title
。这就是您在更改谓词时看到的情况。 EXPLAIN 显示正在使用 obj__title
上的索引,没有排序操作。但是 MySQL 必须检查每一行,看它是否满足谓词。
LIKE 谓词导致在字符串上下文而不是数字中计算列。也就是说,MySQL 必须执行从整数到 varchar 的隐式转换。这会阻止 MySQL 使用索引来满足谓词。 MySQL 基本上被迫对 table 中的每一行进行转换,以便评估谓词。
为了第一个查询的最佳性能:
SELECT SQL_NO_CACHE *
FROM obj
WHERE obj__status = 2
AND obj__obj_type__id = 59
ORDER BY obj__title ASC
LIMIT 0, 500
您需要一个包含前导列的索引:
.... ON obj (obj__status, obj__obj_type__id, obj__title)
那么,MySQL可以利用单索引满足两个等式谓词和的顺序。
请注意,这会使单列 obj__status 上的索引变得多余。任何使用 obj__status
索引的查询都可以使用新索引。
您的第一个 select 需要这个复合索引。 (我冒昧地删除了只会使 SQL 混乱的 "obj_"。)
INDEX(type_id, status, title)
MySQL 很少在一个查询中使用多个索引;此 3 列索引适合 WHERE status=(const) AND type_id=(const) ORDER BY title
。我看到它使用 "index intersect" 来尝试弥补 suitable 复合索引的不足,但只是部分。
也许优化器看着 LIKE
并说 "Punt! I give up on using numeric comparisons, so let's not use either index on type_id or status. Instead, let's see if we can avoid the filesort by using INDEX(title)
"。而且它恰好更好。
还有一件事使 文件排序的成本特别高。 "Using temporary" 和 "Filesort" 更喜欢 通过内存 table 在 RAM 中执行所有操作。但是 several things 可以防止这种情况发生。一种是获取 TEXT 字段,您可以这样做(SELECT *
,其中包括 description TEXT
)。我怀疑优化器是否注意到了这一点。不过时间好像有。
有关索引的更多提示,请参阅 my index cookbook。同时,仅对字符串使用 LIKE
,而不是数值。
我遇到了一个非常奇怪的行为,结果证明这是在我的 where 条件下使用正确的运算符的问题。
假设以下 table 结构具有几百万个条目:
CREATE TABLE `obj` (
`obj__id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`obj__obj_type__id` int(10) unsigned DEFAULT NULL,
`obj__title` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`obj__const` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`obj__description` text COLLATE utf8_unicode_ci,
`obj__created` datetime DEFAULT NULL,
`obj__created_by` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`obj__updated` datetime DEFAULT NULL,
`obj__updated_by` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`obj__property` int(10) unsigned DEFAULT '0',
`obj__status` int(10) unsigned DEFAULT '1',
`obj__sysid` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`obj__scantime` datetime DEFAULT NULL,
`obj__imported` datetime DEFAULT NULL,
`obj__hostname` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`obj__undeletable` int(1) unsigned NOT NULL DEFAULT '0',
`obj__rt_cf__id` int(11) unsigned DEFAULT NULL,
`obj__cmdb_status__id` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`obj__id`),
KEY `obj_FKIndex1` (`obj__obj_type__id`),
KEY `obj_ibfk_2` (`obj__cmdb_status__id`),
KEY `obj__sysid` (`obj__sysid`),
KEY `obj__title` (`obj__title`),
KEY `obj__const` (`obj__const`),
KEY `obj__hostname` (`obj__hostname`),
KEY `obj__status` (`obj__status`),
KEY `obj__updated_by` (`obj__updated_by`)
) ENGINE=InnoDB AUTO_INCREMENT=7640131 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
一个非常简单的 select,有两个条件,按 obj__title 排序,限制为 500,执行安静缓慢(500 毫秒):
SELECT SQL_NO_CACHE * FROM obj WHERE (obj__status = 2) AND (obj__obj_type__id = 59) ORDER BY obj__title ASC LIMIT 0, 500;
没有 "ORDER BY obj__title" 它运行起来就像一个魅力(<1ms)。
EXPLAIN SELECT 告诉我 MySQL 正在执行文件排序而不是使用 obj__title 索引。所以,好吧,很明显这个查询很慢:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE obj index_merge obj_FKIndex1,obj__status obj_FKIndex1,obj__status 5,5 NULL 1336 Using intersect(obj_FKIndex1,obj__status); Using where; Using filesort
当我强制索引 obj__title 与 FORCE 或 USE INDEX 一起使用时,mysql 没有使用其他索引,导致性能再次很差。不过没关系,很明显,性能不佳与这两个条件的组合和顺序有关。
既然我花了几个小时研究优化这个查询,我想出了一个非常简单的交换:我将我的条件运算符从 = 交换为 LIKE。所以我的查询是这样的:
EXPLAIN SELECT SQL_NO_CACHE * FROM obj WHERE (obj__status LIKE 2) AND (obj__obj_type__id LIKE 59) ORDER BY obj__title ASC LIMIT 0, 500;
这就是发生的事情..
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE obj index obj_FKIndex1,obj__status obj__title 768 NULL 500 Using where
查询性能为 150 毫秒。我真的被吓到了。
我对速度不是很满意,但至少它的表现还不错。
但我真正想知道的是为什么LIKE使用索引而=不使用索引?我在 MySQL 文档中没有找到任何相关提示。关于 LIKE 不区分大小写和 LIKE 对 VARCHARS > 255 或任何其他 CHAR 或 TEXT 字段的行为有点不同的一些注意事项。没有关于它的整数行为的单个词。
有人可以阐明这种情况吗?任何可以加快查询速度的数据库设计或查询技巧也非常受欢迎!
对于此查询:
SELECT SQL_NO_CACHE *
FROM obj
WHERE (obj__status = 2) AND (obj__obj_type__id = 59)
ORDER BY obj__title ASC
LIMIT 0, 500;
最好的索引是obj(obj__status, obj__obj_type__id, obj__title)
。
否则,我希望在两个 where
字段之一上有一个索引。
但是,当您使用 like
时,您是在将数字与字符串进行比较。这通常会阻止使用索引。唯一可能的索引是 order by
,它恰好适用于您的情况。
但是,适当的索引应该有更好的性能。
ORDER BY
必须在LIMIT
之前满足。如果行过多,并且 MySQL 执行额外列中显示的排序操作 ("Using filesort"),那可能会很昂贵。
obj__title
的索引,MySQL 也可以在不执行排序操作的情况下满足 ORDER BY obj__title
。这就是您在更改谓词时看到的情况。 EXPLAIN 显示正在使用 obj__title
上的索引,没有排序操作。但是 MySQL 必须检查每一行,看它是否满足谓词。
LIKE 谓词导致在字符串上下文而不是数字中计算列。也就是说,MySQL 必须执行从整数到 varchar 的隐式转换。这会阻止 MySQL 使用索引来满足谓词。 MySQL 基本上被迫对 table 中的每一行进行转换,以便评估谓词。
为了第一个查询的最佳性能:
SELECT SQL_NO_CACHE *
FROM obj
WHERE obj__status = 2
AND obj__obj_type__id = 59
ORDER BY obj__title ASC
LIMIT 0, 500
您需要一个包含前导列的索引:
.... ON obj (obj__status, obj__obj_type__id, obj__title)
那么,MySQL可以利用单索引满足两个等式谓词和的顺序。
请注意,这会使单列 obj__status 上的索引变得多余。任何使用 obj__status
索引的查询都可以使用新索引。
您的第一个 select 需要这个复合索引。 (我冒昧地删除了只会使 SQL 混乱的 "obj_"。)
INDEX(type_id, status, title)
MySQL 很少在一个查询中使用多个索引;此 3 列索引适合 WHERE status=(const) AND type_id=(const) ORDER BY title
。我看到它使用 "index intersect" 来尝试弥补 suitable 复合索引的不足,但只是部分。
也许优化器看着 LIKE
并说 "Punt! I give up on using numeric comparisons, so let's not use either index on type_id or status. Instead, let's see if we can avoid the filesort by using INDEX(title)
"。而且它恰好更好。
还有一件事使 文件排序的成本特别高。 "Using temporary" 和 "Filesort" 更喜欢 通过内存 table 在 RAM 中执行所有操作。但是 several things 可以防止这种情况发生。一种是获取 TEXT 字段,您可以这样做(SELECT *
,其中包括 description TEXT
)。我怀疑优化器是否注意到了这一点。不过时间好像有。
有关索引的更多提示,请参阅 my index cookbook。同时,仅对字符串使用 LIKE
,而不是数值。