在 MySQL 中添加多列索引比使用单列索引有什么显着的好处吗?
Is there any significant benefit in adding multi-column indices in MySQL over having single column indices?
作为高负载系统中数据库优化的新手,我有以下问题 - 假设我们有以下查询(查询带有示例数据):
SELECT *
FROM ticket
WHERE ticket_status='draft'
AND user_id='789437879'
ORDER BY ticket_id DESC LIMIT 0, 15
我们已经有以下索引:
CREATE INDEX ticket_status on ticket(ticket_status);
CREATE INDEX user_id on ticket(user_id);
CREATE INDEX ticket_id on ticket(ticket_id);
如果我们执行以下操作,优化此查询是否会有显着的性能优势:
CREATE INDEX make_that_query_more_efficient on ticket(user_id,ticket_status);
或者它几乎没有任何区别,因为所有列都已编入索引?
这取决于查询。但绝对是在你的例子中。
在许多查询中,它会产生巨大的差异。
这里有一个关于这样的讨论:http://mysql.rjweb.org/doc.php/index1。它讨论了针对简单查询的各种索引策略。结论是复合索引显然是查询的最佳选择。
同时,在这个论坛中有几十个,也许数百个示例,我解决性能问题(高 CPU、慢查询等)的方法主要是将单列索引替换为多列索引。当 WHERE
、ORDER BY
、 和 LIMIT
都可以由 INDEX
.[=37 处理时,性能提升有时是惊人的=]
多对多映射 table ('junction') 是一种非常常见的模式模式,新手无法正确索引。更多信息:http://mysql.rjweb.org/doc.php/index_cookbook_mysql#many_to_many_mapping_table
对于您的查询,这将是一个不同的世界:
INDEX(user_id, ticket_status, -- these two can be in either order
ticket_id) -- this needs to be last
执行将快速钻取索引的 BTree 到具有 ticket_status='draft' AND user_id='789437879'
的行。它将从这些项目的末尾开始并向后扫描 (DESC
),拾取 15 个(或更少)项目。然后它将查找其他列 (*
) 并传送它们。
几乎任何其他索引都需要扫描超过 15 个项目。
至于你的指数。
如果ticket_id
已经是PRIMARY KEY
,那么不要添加INDEX(ticket_id)
;会没用的。
如果你的任何索引是我推荐的索引的前缀,DROP它;这将是多余的,而且会碍事。 (使用 EXPLAIN SELECT
来查看。)
如果优化器选择了您的 (ticket_status)
,它会查看所有具有所需状态的条目,根据 user_id
进行过滤,对结果进行排序,然后剥离 15 行.
同样适用于(user_id)
如果优化器使用 INDEX(ticket_id)
,它将从 id 的末尾开始并向后工作。如果没有 15 个相关行,它将直到扫描整个 table.
才会停止
请注意,我的复合索引甚至避免了排序。
其余的索引可能有用也可能没用;这取决于其他查询是否可以使用它们。
一个 suitable 索引可能对 SELECT
比对 INSERTs
的负担更有利;所以不用担心这个权衡。
以 user_id
开头可能对其他查询有用;从 status
开始似乎不太可能。由于“基数”,单列 INDEX(ticket_status)
不太可能被任何查询使用。
我的 3 列索引可能比您类似的 2 列索引好得多。我的负责 ORDER BY
和 LIMIT
;你的需要收集很多行并对它们进行排序。
如果 *
中有大的 TEXT
列,性能差异可能会更大。
作为高负载系统中数据库优化的新手,我有以下问题 - 假设我们有以下查询(查询带有示例数据):
SELECT *
FROM ticket
WHERE ticket_status='draft'
AND user_id='789437879'
ORDER BY ticket_id DESC LIMIT 0, 15
我们已经有以下索引:
CREATE INDEX ticket_status on ticket(ticket_status);
CREATE INDEX user_id on ticket(user_id);
CREATE INDEX ticket_id on ticket(ticket_id);
如果我们执行以下操作,优化此查询是否会有显着的性能优势:
CREATE INDEX make_that_query_more_efficient on ticket(user_id,ticket_status);
或者它几乎没有任何区别,因为所有列都已编入索引?
这取决于查询。但绝对是在你的例子中。
在许多查询中,它会产生巨大的差异。
这里有一个关于这样的讨论:http://mysql.rjweb.org/doc.php/index1。它讨论了针对简单查询的各种索引策略。结论是复合索引显然是查询的最佳选择。
同时,在这个论坛中有几十个,也许数百个示例,我解决性能问题(高 CPU、慢查询等)的方法主要是将单列索引替换为多列索引。当 WHERE
、ORDER BY
、 和 LIMIT
都可以由 INDEX
.[=37 处理时,性能提升有时是惊人的=]
多对多映射 table ('junction') 是一种非常常见的模式模式,新手无法正确索引。更多信息:http://mysql.rjweb.org/doc.php/index_cookbook_mysql#many_to_many_mapping_table
对于您的查询,这将是一个不同的世界:
INDEX(user_id, ticket_status, -- these two can be in either order
ticket_id) -- this needs to be last
执行将快速钻取索引的 BTree 到具有 ticket_status='draft' AND user_id='789437879'
的行。它将从这些项目的末尾开始并向后扫描 (DESC
),拾取 15 个(或更少)项目。然后它将查找其他列 (*
) 并传送它们。
几乎任何其他索引都需要扫描超过 15 个项目。
至于你的指数。
如果
ticket_id
已经是PRIMARY KEY
,那么不要添加INDEX(ticket_id)
;会没用的。如果你的任何索引是我推荐的索引的前缀,DROP它;这将是多余的,而且会碍事。 (使用
EXPLAIN SELECT
来查看。)如果优化器选择了您的
(ticket_status)
,它会查看所有具有所需状态的条目,根据user_id
进行过滤,对结果进行排序,然后剥离 15 行.同样适用于
(user_id)
如果优化器使用
才会停止INDEX(ticket_id)
,它将从 id 的末尾开始并向后工作。如果没有 15 个相关行,它将直到扫描整个 table.请注意,我的复合索引甚至避免了排序。
其余的索引可能有用也可能没用;这取决于其他查询是否可以使用它们。
一个 suitable 索引可能对
SELECT
比对INSERTs
的负担更有利;所以不用担心这个权衡。以
user_id
开头可能对其他查询有用;从status
开始似乎不太可能。由于“基数”,单列INDEX(ticket_status)
不太可能被任何查询使用。我的 3 列索引可能比您类似的 2 列索引好得多。我的负责
ORDER BY
和LIMIT
;你的需要收集很多行并对它们进行排序。如果
*
中有大的TEXT
列,性能差异可能会更大。