在 where 子句和 order by 子句之间的 MySQL 索引

Posted

技术标签:

【中文标题】在 where 子句和 order by 子句之间的 MySQL 索引【英文标题】:MySQL index in between where clause and order by clause 【发布时间】:2013-06-12 22:43:27 【问题描述】:

我的表结构如下:

CREATE TABLE test (
    id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,

    field_1 VARCHAR(60) NOT NULL,
    field_2 INT(10) UNSIGNED NULL,
    field_3 INT(10) UNSIGNED NULL,
    field_4 INT(10) UNSIGNED NULL,
    field_5 CHAR(2) NULL,
    field_6 INT(10) UNSIGNED NOT NULL,

    rank TINYINT(2) NOT NULL DEFAULT '0',   
    status TINYINT(3) NOT NULL DEFAULT '0',

    PRIMARY KEY (id),
    INDEX (status)

) DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE = MyISAM;

在上表中,rankstatus 字段将分别具有 0-9 和 0-4 之间的整数值。

目前该表包含大约 950K 数据,我正在尝试尽可能优化我的查询。

基本上,我需要在字段rank 上选择带有降序的where 子句的字段。

例如,下面是几个sql查询:

SELECT field_1, field_2, field_3 FROM test WHERE field_1 = 'data1' && status IN ('0', '1', '2') ORDER BY rank DESC LIMIT 0, 20;
SELECT field_1, field_2, field_3 FROM test WHERE field_2 = '5' && status IN ('1', '2') ORDER BY rank DESC LIMIT 0, 20;
SELECT field_1, field_2, field_3 FROM test WHERE field_5 = 'US' && status IN ('0', '2') ORDER BY rank DESC LIMIT 0, 20;

上面的查询ORDER BY rank DESC 很重要。所以我很困惑是否应该在单列或多列上添加索引。

谁能建议我最好的解决方案。

【问题讨论】:

很遗憾,您的问题没有是/否的答案。添加索引绝对可以提高性能,但也可能会阻碍性能。您最好的选择是试一试并查看EXPLAIN 计划。 【参考方案1】:

您的关键问题是您的 status 列有超过 950k 行最多有 4 个不同的值。在 BTREE 索引上,这将是一个非常痛苦的过程。

用于上述 3 个查询的一些更有效的索引可能如下

INDEX forQuery1 ( field_1 , status , rank ) USING BTREE,
INDEX forQuery2 ( field_2 , status , rank ) USING BTREE,
INDEX forQuery3 ( field_5 , status , rank ) USING BTREE,

您会发现第二个查询尤其应该受益,但是您仍然会遇到数据差异对于数据集大小而言非常低的问题,并且 mysql 很可能会退回到表扫描您的 EXPLAIN 可能会显示 LIMIT 以减轻其影响。提到的索引应该适合确定要返回的行。

有关 MySQL 如何使用索引的更多信息,请查看 13.1.13. CREATE INDEX Syntax,尤其是关于 B-Tree 索引特征的部分和以下摘录

如果表有一个多列索引,任何最左边的前缀 优化器可以使用索引来查找行。例如,如果您 在 (col1, col2, col3) 上有一个三列索引,您已编制索引 (col1)、(col1, col2) 和 (col1, col2, col3) 的搜索功能。

如果列不形成最左边的前缀,MySQL 不能使用索引 的指数。假设您有此处显示的 SELECT 语句:

有时 MySQL 不使用索引,即使有可用的索引。一 发生这种情况的情况是优化器估计 使用索引需要 MySQL 访问一个非常大的 表中行的百分比。 (在这种情况下,表扫描是 可能更快,因为它需要更少的搜索。)但是,如果 这样的查询使用 LIMIT 只检索一些行,MySQL 使用 无论如何,一个索引,因为它可以更快地找到几行 返回结果。

作为附加说明,您不需要引用数字数据类型,因此field_2 = 5 && status IN ( 1 , 2 ) 是有效的(事实上,由于引用整数数据类型而不是将它们指定为数字,我在过去遇到了一些奇怪的问题)

【讨论】:

因为mysql索引是升序的 无法确定,因为我们没有您的完整数据集和精确的服务器配置。索引可能仍用于查询,MySQL 足够明智地处理它,尽管正如您在内部所说的那样,它存储在 ASC 中。阅读 BTREE 的工作原理 :)

以上是关于在 where 子句和 order by 子句之间的 MySQL 索引的主要内容,如果未能解决你的问题,请参考以下文章

简述SELECT语句中的FROM、WHERE以及ORDER BY子句的作用。SQL Server

为 where 子句和 order_by 创建 MYSQL 索引

SQL语句中,为啥where子句不能使用列别名,而order by却可以?

第二章:oracle_sql语句之限制(where子句)和排列数据(order by子句)

使用 where 和 order by 子句从 1 更新序列号的列

Spring数据JPA findFirst用where子句(过滤)和order by,抛出内部异常