MySQL:关于INDEX使用的LIKE 123和= 123之间的区别

时间:2015-07-07 16:02:50

标签: mysql performance sql-order-by

我遇到了一种非常奇怪的行为,结果只是在我的条件下使用正确的运算符。

假设以下表结构包含数百万个条目:

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;

一个非常简单的选择,有两个条件,按obj__title排序,限制为500,执行安静缓慢(500ms):

SELECT SQL_NO_CACHE * FROM obj WHERE (obj__status = 2) AND (obj__obj_type__id = 59) ORDER BY obj__title ASC LIMIT 0, 500;

没有&#34; ORDER BY obj__title&#34;它像魅力一样(<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的作用有点不同&gt; 255,或任何其他CHAR或TEXT字段..没有任何关于它的整数行为。

有人可以了解这种情况吗?任何数据库设计或查询提示,以加快查询速度也非常受欢迎!

3 个答案:

答案 0 :(得分:1)

对于此查询:

obj(obj__status, obj__obj_type__id, obj__title)

最佳指数为where

否则,我希望在两个like字段之一上有一个索引。

但是,当您使用order by时,您将数字与字符串进行比较。这通常会阻止使用索引。唯一可能的索引是console.log(instagram_infos);,这恰好适用于您的情况。

但是,正确的索引应该有更好的表现。

答案 1 :(得分:1)

ORDER BY必须在LIMIT之前满足。如果存在大量行,并且MySQL执行Extra列中显示的排序操作(“Using filesort”),那可能会很昂贵。

MySQL还可以通过使用前导列为ORDER BY obj__title的索引,在不执行排序操作的情况下满足obj__title。这就是你在改变谓词时所看到的情况。 EXPLAIN显示正在使用obj__title上的索引,没有排序操作。但MySQL必须检查每一行,看它是否满足谓词。

LIKE谓词导致在字符串上下文中计算列,而不是数字。也就是说,MySQL必须执行从整数到varchar的隐式转换。这可以防止MySQL使用索引来满足谓词。 MySQL基本上被迫为表中的每一行进行转换,以便评估谓词。

为了获得第一个查询的最佳效果:

  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上的索引的查询都可以使用新索引。

答案 2 :(得分:1)

您的第一个选择需要此复合索引。 (我冒昧地删除了“obj_”,它只会使SQL混乱。)

INDEX(type_id, status, title)

MySQL很少在查询中使用多个索引;这个3列索引适用于WHERE status=(const) AND type_id=(const) ORDER BY title。我看到它使用“索引相交”来尝试补偿缺少合适的复合索引,但只是部分补偿。

也许优化器看了LIKE并说“Punt!我放弃使用 numeric 比较,所以我们不要在type_id或status上使用任何索引。相反,让我们看看我们是否可以使用INDEX(title)“来避免文件排序。它似乎更好。

还有另一件事使 文件包特别昂贵。 “使用临时”和“Filesort”更喜欢通过MEMORY表在RAM中执行所有操作。但several things可以防止这种情况发生。一个是获取TEXT字段,您可以(SELECT *包含description TEXT)。我怀疑优化器是否注意到了这一点。但时间似乎有了。

有关索引的更多提示,请参阅my index cookbook。同时,仅对字符串使用LIKE,而不是数值。