Mysql 无法使用此表实现覆盖索引(2个等式和一个选择)?
我在Mysql 无法使用此表实现覆盖索引(2个等式和一个选择)?,mysql,sql,optimization,indexing,Mysql,Sql,Optimization,Indexing,我在FAMILY、CUSTOMER\u id和AMOUNT上添加了一个封面索引IDX\u FAMILY\u CUSTOMER\u AMOUNT,因为大多数时候我使用以下查询: CREATE TABLE `discount_base` ( `id` varchar(12) COLLATE utf8_unicode_ci NOT NULL, `amount` decimal(13,4) NOT NULL, `description` varchar(255) COLLATE utf8_u
FAMILY
、CUSTOMER\u id
和AMOUNT
上添加了一个封面索引IDX\u FAMILY\u CUSTOMER\u AMOUNT
,因为大多数时候我使用以下查询:
CREATE TABLE `discount_base` (
`id` varchar(12) COLLATE utf8_unicode_ci NOT NULL,
`amount` decimal(13,4) NOT NULL,
`description` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`family` varchar(4) COLLATE utf8_unicode_ci NOT NULL,
`customer_id` varchar(8) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
KEY `IDX_CUSTOMER` (`customer_id`),
KEY `IDX_FAMILY_CUSTOMER_AMOUNT` (`family`,`customer_id`,`amount`),
CONSTRAINT `FK_CUSTOMER` FOREIGN KEY (`customer_id`)
REFERENCES `customer` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
然而,使用EXPLAIN
和记录反弹(~250000)它会说:
为什么我越来越使用where;使用索引而不是仅仅使用索引
编辑:使用少量数据(使用where;使用index):
另一个id
是family
+customer\u id
(const):
是家庭和客户id字符串吗?我猜您可能会将customer_id作为一个整数传递,这可能会导致发生类型转换,因此索引不会用于该特定列
确保将客户机ID作为字符串传递,或考虑将表更改为存储CutOMLSID作为INT/< P>
如果您使用的是字母数字标识,那么这不适用。我非常确定
使用索引是重要的部分,它意味着“使用覆盖索引”
需要进一步检查的两件事:
EXPLAIN SELECT amount
FROM discount_base
WHERE `id` = '060320000275';
可能会提供进一步的线索
EXPLAIN FORMT=JSON SELECT ...
将显示以各种方式读取/写入/等的行数。如果某个数字表示大约250000(在您的情况下),则表示表扫描。如果所有的数字都很小(大约是查询返回的行数),那么您可以确信它确实有效地执行了该查询
这里的数字并不区分对索引的读取和对数据的读取。但它们忽略了缓存。由于缓存的原因,计时(两次相同的运行)可能会显著不同;处理程序%的值不会更改。上的解释有这样一句话:
使用索引
使用从表中检索列信息
只显示索引树中的信息,而无需执行其他操作
尝试读取实际行。查询时可以使用此策略
仅使用作为单个索引一部分的列
如果额外的列
还表示使用where,这意味着索引用于执行
关键字值的查找。如果不使用where,优化器可能会
读取索引以避免读取数据行,但不将其用于
查找。例如,如果索引是查询的覆盖索引,
优化器可以扫描它而不使用它进行查找
根据您提供的信息,我的最佳猜测是,优化器首先使用您的IDX\u客户
索引,然后执行键查找,根据键(客户id)从实际数据页检索非键数据(金额和系列)。
这很可能是由于索引中列的基数(例如唯一性)造成的。你应该检查
在where子句中使用的列,并将基数最高的列放在索引的第一位。从专栏猜
名称和当前结果,customer\u id
的基数最高
因此,改变这一点:
FLUSH STATUS;
SELECT ...;
SHOW SESSION STATUS LIKE 'Handler%';
为此:
KEY `IDX_FAMILY_CUSTOMER_AMOUNT` (`family`,`customer_id`,`amount`)
进行更改后,应运行ANALYZE TABLE
更新表统计信息。这将更新表统计信息,它可以
影响优化器对索引的选择。这听起来不错。根据报告:
如果额外的列还显示Using where,则表示索引正在
用于执行键值的查找。如果不使用where,则
优化器可能正在读取索引以避免读取数据行,但不会
使用它进行查找。例如,如果索引是覆盖索引
对于查询,优化器可以扫描它,而不使用它进行查找
这意味着,单独使用索引将读取整个索引以检索结果,而不是使用索引结构来查找特定值。您可能可以通过选择系列、客户id、折扣基数中的金额获得此信息。在何处使用;使用索引意味着优化器利用索引查找和检索与查询参数(族、客户id)匹配的行。问题的答案取决于引擎实际使用索引的目的
在给定的查询中,您要求引擎:
查找值(其中/连接)
基于此查找结果检索信息(选择)
对于第一部分,一旦过滤结果(查找),在Extra
中就会有一个条目指示使用WHERE
,因此这就是您在解释计划中看到它的原因
对于第二部分,引擎不需要超出给定的索引,因为它是一个覆盖索引。解释计划通过使用索引显示来通知它。此使用索引提示与使用WHERE
相结合,意味着您的索引也用于查询的查找部分,如mysql文档中所述:
使用索引
从表中检索列信息时仅使用
索引树中的信息,而不必进行额外的查找
读取实际行。当查询使用
仅限作为单个索引一部分的列
如果额外的列还显示Using where,则表示索引正在
用于执行键值的查找。如果不使用where,则
优化器可能正在读取索引以避免读取数据行,但不会
使用它进行查找。例如,如果索引是覆盖索引
对于查询,优化器可以扫描它,而不使用它进行查找
检查这把小提琴:
我删除了where子句,现在查询仅使用索引显示。这是因为不需要在表中进行查找。这确实可能是个问题
请注意,使用utf8\u unicode\u ci
排序规则,单个索引键可能会匹配数百万个字符串。例如,所有这些字母都与b匹配
EXPLAIN FORMT=JSON SELECT ...
FLUSH STATUS;
SELECT ...;
SHOW SESSION STATUS LIKE 'Handler%';
KEY `IDX_FAMILY_CUSTOMER_AMOUNT` (`family`,`customer_id`,`amount`)
KEY `IDX_FAMILY_CUSTOMER_AMOUNT` (`customer_id`,`family`,`amount`)
SELECT amount
FROM discount_base
WHERE family = :family AND customer_id = :customer_id;
WHERE family in (lower(:family, upper(:family), . . .) and . . .
KEY IDX_CUSTOMERFAMILY_AMOUNT (CustomerFamily, amount)
KEY `IDX_FAMILY_CUSTOMER_AMOUNT` (`family`,`customer_id`)
USE INDEX (`IDX_FAMILY_CUSTOMER_AMOUNT`)
SELECT amount FROM discount_base
USE INDEX (`IDX_FAMILY_CUSTOMER_AMOUNT`)
WHERE family = '1' AND customer_id = '1'