Jac*_*ski 4 mysql performance index
我正在查看我们的 MySQL 5.7.16 数据库,我注意到有些索引使用主键作为索引中的第一列,然后是其他列。直观上,这对我来说似乎没有意义,因为索引总是通过使用第一列将其缩小到一行。但是,我不确定是否有任何特殊情况需要使用这种索引。在任何情况下这都会提高性能吗?
索引有不止一种用途。主要用途(在大多数情况下)是快速识别满足查询条件的行。执行此操作时,会考虑索引最左边的列。
假设您有一个包含 100 列的表users
,其中包含日期、字符串和数字。表中的五列是:id
、username
、country
、acct_expir_date
和access_level
(一个 int)。id
是主键,它和username
都是唯一的。
假设country
和上有一个(非聚集)索引username
。查询:
SELECT `username`, `acct_expir_date` FROM `users` WHERE country = 'UNITED STATES';
Run Code Online (Sandbox Code Playgroud)
可以使用该索引来识别来自该国家/地区的所有用户,在实际表中找到他们的记录,并返回所请求的数据。
然而,查询:
SELECT `username`, `country` FROM `users` WHERE `username` LIKE 'g%';
Run Code Online (Sandbox Code Playgroud)
将无法使用索引 - 索引中最左边的列 ,country
不是WHERE
子句的一部分。
也就是说,索引确实还有第二个用途。如果查询中的所有列都存在于索引中,则查询可以忽略实际的表,并像使用表本身一样使用索引。这称为覆盖索引。
假设我们的表上现在有另一个非聚集索引,按顺序位于acct_expir_date
、username
、id
、 和列上。access_level
并且,我们有这样的查询:
SELECT `username`, `access_level`, `acct_expir_date`
FROM `users`
WHERE `acct_expir_date` >= '2017-07-01'
AND `acct_expir_date` < '2017-08-01'
;
Run Code Online (Sandbox Code Playgroud)
即使SELECT
列列表是*
(所有列),此查询也可能会使用我们的索引;但是,由于我们的索引包含查询中的所有列,因此users
表本身不会被触及 - 查询将简单地使用索引中的信息来提供请求的数据。
因此,在您的情况下,索引中直到主键的列将用于索引的第一次使用 - 定位记录。剩余的柱子可用于第二次使用;提供覆盖索引。
注意:正如 Balazs Papp 在评论中所述,索引中主键右侧的列可用于帮助识别查询中对一系列主键值感兴趣的特定行;但是,必须检查索引中的每一行,因为不能根据主键右侧的任何内容对行进行排序(因为它是唯一的)。由于索引已经在内存中,因此通过索引进一步缩小行的实际选择范围确实可能比加载完整记录然后缩小范围更快;但是,如果要定期在搜索中使用额外的列,则它们可能在任何唯一列的左侧更有用,在那里它们可以更快地缩小范围。
更新:可以完整加载到内存中的表通常不需要覆盖索引。如果您的表不是那么大,那么在维护和存储索引时,索引带来的任何性能提升都可能会丢失。考虑表的整体大小、表行的大小与索引行的大小,以及可能使用索引的查询的执行频率与添加或更新这些列中的数据的频率。如果这些索引看起来弊大于利,您可能需要考虑放弃它们。然而,这样做时要非常小心;如果它们正在提高某些关键查询的性能,则删除它们可能会导致严重问题。我大约每月删除不超过一个;在一个月的正常业务过程中,无论该指数可能有什么用途,都应该会遇到。而且,只有在您部门的关键人员了解情况的情况下才能这样做;不仅仅是你的老板,还有处理绩效问题报告的人,因此可能出现的任何问题都可以得到解决。另外,请记住重新创建索引需要多长时间;实际上,在某些情况下,创建索引,将其用于一份月度报告,然后删除它仍然会带来性能优势,而无需维护和(长期)存储成本。
更新 2:为了(改进)完整性,正如 joanolo 在评论中提到的那样,索引还有第三个一般用途 - 它们确实按这些列对索引列中的值进行排序。如果该排序与您的ORDER BY
子句匹配,则引擎可以通过以必要的顺序检索记录来避免对记录进行排序。这与OP的问题不是特别相关,因为唯一列意味着索引中右侧的任何值都不会被排序(因为一旦索引到达唯一列,行的位置就固定了),但它是真的。