【问题标题】:Why is MySQL not using index for simple "SELECT * FROM Table WHERE field='value'" query?为什么 MySQL 不使用索引进行简单的 \"SELECT * FROM Table WHERE field=\'value\'\" 查询?
【发布时间】:2022-11-01 20:29:21
【问题描述】:

我正在尝试对 MySQL 5.7 数据库进行一个非常简单的查询,但查询速度很慢,并且说明显示它没有使用索引,尽管它将它列为可能的键。下面是查询、解释输出和表模式。有任何想法吗?谢谢

查询:SELECT text FROM LogMessages where lotNumber = 5556677

解释输出:

mysql> explain SELECT text FROM LogMessages where lotNumber = 5556677;
+----+-------------+------------------------------+------------+------+------------------------------------------------------------------------------+------+---------+------+----------+----------+-------------+
| id | select_type | table                        | partitions | type | possible_keys                                                                | key  | key_len | ref  | rows     | filtered | Extra       |
+----+-------------+------------------------------+------------+------+------------------------------------------------------------------------------+------+---------+------+----------+----------+-------------+
|  1 | SIMPLE      | LogMessages                  | NULL       | ALL  | idx_LogMessages_lotNumber                                                    | NULL | NULL    | NULL | 35086603 |    10.00 | Using where |
+----+-------------+------------------------------+------------+------+------------------------------------------------------------------------------+------+---------+------+----------+----------+-------------+
1 row in set, 5 warnings (0.07 sec)

表架构:

CREATE TABLE `LogMessages` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `lotNumber` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `text` text COLLATE utf8mb4_unicode_ci,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idLogMessages_UNIQUE` (`id`),
  KEY `idx_LogMessages_lotNumber` (`lotNumber`)
) ENGINE=InnoDB AUTO_INCREMENT=37545325 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

【问题讨论】:

  • 表中有多少行?有多少涉及5556677?

标签: mysql query-optimization type-coercion


【解决方案1】:

你已经得到了答案,但我想我会提供更多的背景信息。

https://dev.mysql.com/doc/refman/5.7/en/type-conversion.html 解释了为什么不使用索引:

对于字符串列与数字的比较,MySQL 不能使用列上的索引来快速查找值。如果 str_col 是索引字符串列,则在以下语句中执行查找时不能使用索引:

SELECT * FROM tbl_name WHERE str_col=1;

原因是有许多不同的字符串可以转换为值 1,例如 '1'、'1' 或 '1a'。

您问题中的 EXPLAIN 报告显示type: ALL,这意味着它是表扫描。它没有使用索引。

如果我们要使用字符串文字,它是字符串到字符串的比较,所以它使用索引。

mysql> explain SELECT text FROM LogMessages where lotNumber = '5556677';
+----+-------------+-------------+------------+------+---------------------------+---------------------------+---------+-------+------+----------+-------+
| id | select_type | table       | partitions | type | possible_keys             | key                       | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------------+------------+------+---------------------------+---------------------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | LogMessages | NULL       | ref  | idx_LogMessages_lotNumber | idx_LogMessages_lotNumber | 183     | const |    1 |   100.00 | NULL  |
+----+-------------+-------------+------------+------+---------------------------+---------------------------+---------+-------+------+----------+-------+

如果我们在计算结果为字符串值的表达式中使用数字文字,它也会使用索引。有几种方法可以做到这一点:

mysql> explain SELECT text FROM LogMessages where lotNumber = 5556677 collate utf8mb4_unicode_ci;
+----+-------------+-------------+------------+------+---------------------------+---------------------------+---------+-------+------+----------+-------+
| id | select_type | table       | partitions | type | possible_keys             | key                       | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------------+------------+------+---------------------------+---------------------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | LogMessages | NULL       | ref  | idx_LogMessages_lotNumber | idx_LogMessages_lotNumber | 183     | const |    1 |   100.00 | NULL  |
+----+-------------+-------------+------------+------+---------------------------+---------------------------+---------+-------+------+----------+-------+

mysql> explain SELECT text FROM LogMessages where lotNumber = cast(5556677 as char);
+----+-------------+-------------+------------+------+---------------------------+---------------------------+---------+-------+------+----------+-------+
| id | select_type | table       | partitions | type | possible_keys             | key                       | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------------+------------+------+---------------------------+---------------------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | LogMessages | NULL       | ref  | idx_LogMessages_lotNumber | idx_LogMessages_lotNumber | 183     | const |    1 |   100.00 | NULL  |
+----+-------------+-------------+------------+------+---------------------------+---------------------------+---------+-------+------+----------+-------+

mysql> explain SELECT text FROM LogMessages where lotNumber = concat(5556677);
+----+-------------+-------------+------------+------+---------------------------+---------------------------+---------+-------+------+----------+-------+
| id | select_type | table       | partitions | type | possible_keys             | key                       | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------------+------------+------+---------------------------+---------------------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | LogMessages | NULL       | ref  | idx_LogMessages_lotNumber | idx_LogMessages_lotNumber | 183     | const |    1 |   100.00 | NULL  |
+----+-------------+-------------+------------+------+---------------------------+---------------------------+---------+-------+------+----------+-------+

在这三个示例中,type: ref 表示它正在使用索引,进行非唯一查找。

【讨论】:

    【解决方案2】:

    啊,刚刚想通了。 lotNumber 字段是一个 varchar,但我在查询中将其作为整数输入。如果我将 5556677 值放在引号中,则查询使用索引并且几乎是即时的。

    【讨论】:

    • 恭喜你得出结论。引用/不引用决定对于查询完成速度的可预测性至关重要。
    • 请查看个人资料以获取联系信息。
    猜你喜欢
    • 1970-01-01
    • 2011-04-06
    • 1970-01-01
    • 2011-09-09
    • 2011-09-02
    • 2011-10-07
    • 1970-01-01
    • 1970-01-01
    • 2013-08-15
    相关资源
    最近更新 更多