MySql 优化器不使用 varchar 上的索引

Posted

技术标签:

【中文标题】MySql 优化器不使用 varchar 上的索引【英文标题】:MySql optimizer doesn't use the index on varchar 【发布时间】:2021-11-11 09:36:35 【问题描述】:

我有一个表定义为

CREATE TABLE `article` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `field1` varchar(1024) NOT NULL,
  `priority` int(11) NOT NULL,
  `prodcode` varchar(64) NOT NULL,
  `status` int(8) NOT NULL,
  `error` varchar(1024) DEFAULT NULL,
  `ctime` datetime DEFAULT CURRENT_TIMESTAMP,
  `mtime` datetime DEFAULT NULL,
  `event` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `article_prodcode_idx` (`prodcode`),
  KEY `article_status_idx` (`status`),
  KEY `article_priority_idx` (`priority`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

此表包含大约 4000 万条记录。 当我运行类似

的查询时
SELECT * FROM article WHERE prodcode='a-4536-x-bef45-green';

优化器“决定”此类查询不必使用任何索引(“EXPLAIN SELECT...”会导致

+----+-------------+----------+------+---------------+------+---------+------+----------+-------------+
| id | select_type | table    | type | possible_keys | key  | key_len | ref  | rows     | Extra       |
+----+-------------+----------+------+---------------+------+---------+------+----------+-------------+
|  1 | SIMPLE      | article  | ALL  | NULL          | NULL | NULL    | NULL | 39415251 | Using where |
+----+-------------+----------+------+---------------+------+---------+------+----------+-------------+

如果我添加例如另一个字段,如优先级或状态,优化器只使用这样的索引,而不是 article_prodcode_idx 索引。问题是查询扫描了整个 4000 万条记录,结果在 100 秒后出现。为什么那里没有使用索引?

我也检查了这个答案:mysql partial indexes on varchar fields and group by optimization 但我没有找到任何问题的答案。我应该怎么做才能让查询...快速返回结果?

谢谢

【问题讨论】:

您的查询选择了多少行? @akina 没有,一两个。 是否涉及字符集转换? @Salman 不,没有字符集问题。即使通用字符集是 utf8,内容也是纯 ascii。 Field1 可以包含 utf8 字符串,但不能包含索引字符串。 您问题中的表定义为PRIMARY KEY (oid),但您的表中没有这样的列。我仍然建议在生产服务器上仔细检查表定义。排除作为问题原因的缺失索引。 【参考方案1】:

经过深入检查,我解决了问题。在某些方面,我在描述问题时犯了一个错误。事实上,在我的脚本中,我有类似的东西

SET @pc='a-4536-x-bef45-green'; 
...
SELECT * FROM article WHERE prodcode=@pc;

虽然在其他一些方面我有

SELECT * FROM article WHERE prodcode='a-4536-x-bef45-green';

在第一种情况下,不使用索引。在第二个中,一切都按预期工作。 这是第一个查询的解释:

EXPLAIN FORMAT=JSON SELECT * FROM article WHERE prodcode=@pc;

  "query_block": 
  "select_id": 1,
  "table": 
    "table_name": "article",
    "access_type": "ALL",
    "rows": 39498773,
    "filtered": 100,
    "attached_condition": "(convert(`shop`.`article`.`prodcode` using utf8mb4) = (@`pc`))"
    
  

这是第二个

EXPLAIN FORMAT=JSON SELECT * FROM article WHERE prodcode='a-4536-x-bef45-green';


"query_block": 
  "select_id": 1,
  "table": 
    "table_name": "article",
    "access_type": "ref",
    "possible_keys": [
      "article_prodcode_idx"
    ],  
    "key": "article_prodcode_idx",
    "used_key_parts": [
      "prodcode"
    ],
    "key_length": "194",
    "ref": [
      "const"
    ],
    "rows": 2,
    "filtered": 100,
    "index_condition": "(`shop`.`article`.`prodcode` = 'a-4536-x-bef45-green')"
    
  

我尝试了很多次,例如使用

SELECT * FROM article WHERE prodcode in (@pc, 'another code');

但结果始终相同:如果涉及变量,则不使用索引。

知道了,我更改了我的脚本以便不再使用 SET,并且一切都开始运行良好。

我还是想知道为什么……但至少问题解决了。

【讨论】:

答案在这里:***.com/questions/15032641/…

以上是关于MySql 优化器不使用 varchar 上的索引的主要内容,如果未能解决你的问题,请参考以下文章

MySql Query - 使用 varchars、索引进行优化,需要一个多小时才能运行

Oracle 优化器不接受索引提示

Varchar 字段上的 MySQL 唯一索引 - 优点和缺点?

mysql慢sql优化

Day517.索引优化与查询优化 -mysql

MySQL数据库优化_索引