蓝海航行家
发布于

在 mysql 中未命中索引

当选择所有列时,索引不起作用,但如果只选择几列,它就会起作用。

  mysql> explain SELECT  *  FROM fatura  USE INDEX (datapago_serv_pago)   WHERE id_servidor = 10 AND pago = '1' AND data_pago <= '2021-05-05';
+----+-------------+--------+------------+------+--------------------+------+---------+------+----------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys      | key  | key_len | ref  | rows     | filtered | Extra       |
+----+-------------+--------+------------+------+--------------------+------+---------+------+----------+----------+-------------+
|  1 | SIMPLE      | fatura | NULL       | ALL  | datapago_serv_pago | NULL | NULL    | NULL | 10199216 |     0.00 | Using where |
+----+-------------+--------+------------+------+--------------------+------+---------+------+----------+----------+-------------+
  mysql> explain SELECT  uid_  FROM fatura  USE INDEX (datapago_serv_pago)   WHERE id_servidor = 10 AND pago = '1' AND data_pago <= '2021-05-05';
+----+-------------+--------+------------+-------+--------------------+--------------------+---------+------+---------+----------+--------------------------+
| id | select_type | table  | partitions | type  | possible_keys      | key                | key_len | ref  | rows    | filtered | Extra                    |
+----+-------------+--------+------------+-------+--------------------+--------------------+---------+------+---------+----------+--------------------------+
|  1 | SIMPLE      | fatura | NULL       | range | datapago_serv_pago | datapago_serv_pago | 5       | NULL | 5099608 |     0.01 | Using where; Using index |
+----+-------------+--------+------------+-------+--------------------+--------------------+---------+------+---------+----------+--------------------------+
  CREATE TABLE `fatura` (
  `uid_` bigint unsigned NOT NULL,
  `uid_cliente` bigint unsigned NOT NULL,
  `uid_cliente_servico` bigint unsigned NOT NULL,
  `id_servidor` tinyint unsigned NOT NULL,
  `id` int unsigned NOT NULL DEFAULT '0',
  `data_cadastro` date NOT NULL,
  `valor` decimal(12,2) NOT NULL DEFAULT '0.00',
  `vencimento` date NOT NULL DEFAULT (0),
  `pago` tinyint unsigned NOT NULL DEFAULT '0',
  `data_pago` date NOT NULL DEFAULT (0),
  `valor_pago` decimal(12,2) unsigned NOT NULL DEFAULT '0.00',
  `historico` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '',
  `id_cliente` int unsigned NOT NULL DEFAULT (0),
  `id_servico` int unsigned NOT NULL DEFAULT (0),
  `nome` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '',
  `desativada` tinyint unsigned NOT NULL DEFAULT '0',
  `operador_inclusao` varchar(50) NOT NULL,
  `operador_liquidacao` varchar(50) NOT NULL,
  `forma_pago` varchar(50) NOT NULL,
  `status_banco` tinyint NOT NULL DEFAULT (0),
  PRIMARY KEY (`uid_`),
  KEY `vencimento` (`vencimento`) USING BTREE,
  KEY `id_cliente` (`id_cliente`),
  KEY `id_servico` (`id_servico`),
  KEY `pago` (`pago`),
  KEY `id_servidor` (`id_servidor`),
  KEY `id` (`id`) USING BTREE,
  KEY `uid_cliente` (`uid_cliente`),
  KEY `data_pago` (`data_pago`),
  KEY `data_cadastro` (`data_cadastro`),
  KEY `historico` (`historico`),
  KEY `uid_cliente_servico` (`uid_cliente_servico`),
  KEY `desativada` (`desativada`),
  KEY `operador_inclusao` (`operador_inclusao`),
  KEY `operador_liquidacao` (`operador_liquidacao`),
  KEY `venc_serv_pago` (`vencimento`,`id_servidor`,`pago`),
  KEY `forma_pago` (`forma_pago`),
  KEY `status_banco` (`status_banco`),
  KEY `datapago_serv_pago` (`data_pago`,`id_servidor`,`pago`),
  KEY `vencimento_serv_pago` (`data_pago`,`id_servidor`,`pago`)

)
如果我选择日期范围,它可以正常工作:

  explain SELECT  *  FROM fatura  USE INDEX (datapago_serv_pago)   WHERE id_servidor = 10 AND pago = '1' AND data_pago >= '2021-04-01'  AND data_pago <= '2021-05-05';
+----+-------------+--------+------------+-------+--------------------+--------------------+---------+------+--------+----------+-----------------------+
| id | select_type | table  | partitions | type  | possible_keys      | key                | key_len | ref  | rows   | filtered | Extra                 |
+----+-------------+--------+------------+-------+--------------------+--------------------+---------+------+--------+----------+-----------------------+
|  1 | SIMPLE      | fatura | NULL       | range | datapago_serv_pago | datapago_serv_pago | 5       | NULL | 158342 |     0.01 | Using index condition |
+----+-------------+--------+------------+-------+--------------------+--------------------+---------+------+--------+----------+-----------------------+

在整个文档中搜索了很多,但找不到原因或解决方案。

浏览 (12)
点赞 (1)
收藏
1条评论
Klustron小助手
如果基于成本的优化器估计使用索引比跳过索引并仅读取所有行需要更多的工作,则 MySQL 不会使用索引。 请记住,在 InnoDB 中进行二级索引查找时,实际上是两次查找。一个用于在索引中查找条目,另一个用于获取表中不属于索引的其他列的主键查找。 因此,根据搜索匹配的表量,使用索引的成本可能会更高。根据我的经验,如果查询条件匹配超过 20% 的表,优化器通常会选择不使用该索引。我不认为这是一个官方或记录在案的阈值,它只是基于观察。 因此,我猜想您在索引的第一列上的条件: data_pago &lt;= &#39;2021-05-05&#39; 匹配表的 20% 或更多。 当你的条件是针对更窄的日期范围时: data_pago &gt;= &#39;2021-04-01&#39; AND data_pago &lt;= &#39;2021-05-05&#39; 这使优化器相信它将匹配表中的较小部分,因此值得使用 index。 此范围条件中引用的列 是索引的第一列,这意味着该索引的后续列不会用于搜索。基本上,当且仅当最左侧的列都用于相等条件时,索引中的多个列才能帮助搜索。一旦你在任何类型的不等式或范围条件中使用了一列,那就是索引的最后一列,将用于搜索或排序。data_pago 例如,在您的案例中,索引为 on ,但条件是(重新排列这些术语以匹配索引的列顺序)。这些列中的第一列是范围条件,因此不能使用其他两列。(data_pago,id_servidor,pago) data_pago &lt;= &#39;2021-05-05&#39; AND id_servidor = 10 AND pago = &#39;1&#39; 如果你在第三个位置定义了索引,那么可以使用所有三列,这可能会更好地缩小搜索范围,因此它对表的匹配率大大小于 20%。data_pago 当您仅选择单个列而不是 .然后,优化器会推断它是仅索引查询,并且它不承担读取表行的额外成本。这意味着它需要获取的列都来自索引本身(主键列始终是 InnoDB 中该索引的一部分)。所以它改变了主意,并使用了索引。SELECT * 请注意第二个 EXPLAIN.这意味着它检查的行将通过应用非索引条件丢弃其中的 99.99%。这是糟糕的优化,因为这意味着它必须检查大量行才能丢弃它们。理想的优化是 ,这将表明它通过索引查找检查的每一行实际上是属于结果集中的行。filtered: 0.01filtered: 100.0 显然,在执行 table-scan () 时,该列没有被计算,因此它报告 0.0。文档中没有提到这一点。
点赞
评论