【问题标题】:Mysql query optimizations using indexes使用索引的 Mysql 查询优化
【发布时间】:2011-07-14 16:26:42
【问题描述】:

我的数据库架构由以下两个表组成:

CREATE TABLE `categories` (
  `id` bigint(20) NOT NULL auto_increment,
  `title` varchar(128) NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `articles` (
  `id` bigint(20) NOT NULL auto_increment,
  `title` varchar(512) NOT NULL,
  `body` longtext,
  `state` varchar(7) NOT NULL,
  `type` varchar(6) NOT NULL,
  `category` bigint(20) default NULL,
  `publishedAt` datetime default NULL,
  PRIMARY KEY  (`id`),
  KEY `FK_category_to_article_category` (`category`),
  CONSTRAINT `FK_category_to_article_category` FOREIGN KEY (`category`) REFERENCES `categories` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

对于文章表,state 列的值类似于“PUBLISHED”或“UNPUBLISHED”,type 列的值类似于“NEWS”、“GOSSIP”和“OPINION”。

我的应用程序执行了很多这样的查询:

select * from articles where state="PUBLISHED" and type in ("NEWS","GOSSIP") 
and category in (4) and publishedAt<=now() order by publishedAt desc;

我有大约 10K 篇文章,我正在尝试确定上述查询是否使用类别的默认外键表现更好,或者我应该使用多列索引。

没有索引(使用“解释扩展”):

+----+-------------+-------+------+---------------------------------+---------------------------------+---------+-------+------+-----------------------------+
| id | select_type | table | type | possible_keys                   | key                             | key_len | ref   | rows | Extra                       |
+----+-------------+-------+------+---------------------------------+---------------------------------+---------+-------+------+-----------------------------+
|  1 | SIMPLE      | this_ | ref  | FK_category_to_article_category | FK_category_to_article_category | 9       | const |  630 | Using where; Using filesort |
+----+-------------+-------+------+---------------------------------+---------------------------------+---------+-------+------+-----------------------------+

如果我创建多列索引并再次解释(强制特定索引):

create index I_s_t_c_p on articles (state, type, category, publishedAt);


+----+-------------+-------+-------+---------------+-----------+---------+------+------+------------------------------------------+
| id | select_type | table | type  | possible_keys | key       | key_len | ref  | rows | Extra                                    |
+----+-------------+-------+-------+---------------+-----------+---------+------+------+------------------------------------------+
|  1 | SIMPLE      | this_ | range | I_s_t_c_p     | I_s_t_c_p | 61      | NULL | 1216 | Using where; Using index; Using filesort |
+----+-------------+-------+-------+---------------+-----------+---------+------+------+------------------------------------------+

查询实际返回的行数是 630。在我看来,多列索引应该比 FK 执行得更好,因为使用了所有索引列,但事实上使用索引时检查了 ~1200 行让我困惑。我知道这些数字只是估计值,但是这两个键之间的差异很大;使用组合索引,我们检查了双倍的行数。

所以我的问题如下:

  1. 为什么要使用多列索引检查这么多行?
  2. 由于使用 FK 我们有一个连接类型“ref”并且使用组合索引我们有一个连接类型“range”,这是否意味着使用 FK 的查询比另一个更好/更快?李>
  3. 我是否应该使用对检查的行数的估计作为判断索引是否良好/最优的标准?
  4. 在这个用例中,多列索引是否比 FK 更好?我应该根据什么做出决定?

一些附加信息:

  • 优化器没有在查询上强制建立索引,而是选择了 FK。当我对文章执行analyze table 时,选择了多列索引。
  • 我正在使用 MySql 5.0.15
  • 索引信息

+----------+------------+---------------------------------+--------------+-------------+-------------+------------+
| Table    | Non_unique | Key_name                        | Seq_in_index | Column_name | Cardinality | Index_type |
+----------+------------+---------------------------------+--------------+-------------+-------------+------------+
| articles |          0 | PRIMARY                         |            1 | id          |       12561 | BTREE      |
| articles |          1 | FK_category_to_article_category |            1 | category    |          37 | BTREE      |
| articles |          1 | I_s_t_c_p                       |            1 | state       |           8 | BTREE      |
| articles |          1 | I_s_t_c_p                       |            2 | type        |          32 | BTREE      |
| articles |          1 | I_s_t_c_p                       |            3 | category    |         163 | BTREE      |
| articles |          1 | I_s_t_c_p                       |            4 | publishedAt |       12561 | BTREE      |
+----------+------------+---------------------------------+--------------+-------------+-------------+------------+

提前致谢。

【问题讨论】:

  • 想法:如果 statetype 只能采用一组有界值,您可以将它们设为 ENUM 或其他整数类型,这比 VARCHAR 比较起来更快.
  • 多键索引中的顺序很重要。尝试 (publishedAt,category,type,state) 为您的索引。
  • @bot403:正如我对 Mchl 的回复(见下文),publishedAt 似乎没有必要,但类别列在前的索引顺序比状态列在前的结果更差。这可能与文章的数量有关吗?也许在大约 50K 篇文章中,以类别为先的索引可以显示更好的结果?
  • @Kerrek SB:我不知道 mysql ENUM 类型,所以感谢您的洞察力。但是我使用的是休眠,似乎 mysql ENUM 没有得到很好的支持(stackoverflow.com/questions/2160700/…)。另外从另一个讨论 (stackoverflow.com/questions/766299/…) 看来,即使它有助于我的选择查询的性能,我也会减慢任何插入和更新的速度。

标签: mysql performance indexing


【解决方案1】:

如您所见,publishedAt 上的索引与 PK 具有相同的基数。这真的没有帮助。我会尝试用(category,type,state) 的顺序创建一个复合索引。这样,索引的第一部分是最有选择性的。

【讨论】:

  • 正是我会做的。索引基于任何第一个元素将根据常用的条件条件返回最小条目。
  • 您对publishedAt 的看法是正确的。从索引中删除它,立即将检查的行数减少到一半 - 与 FK 相同。再加上在大约 20K 篇文章中对其进行测试,该索引的性能优于 FK——这是合乎逻辑的假设。但是,您建议的索引顺序 -(category,type,state)- 似乎与(state, type, category, publishedAt) 索引的性能相同或更差。我理解“索引的第一部分应该是最有选择性的”,但我很困惑,因为我看到了更糟糕的结果。难道是因为state是一个单一的等式约束?
  • 不幸的是,索引选择性只是索引如何执行的一个线索。如果您的数据在各个类别之间分布不均,它的性能可能不会那么好。
猜你喜欢
  • 2012-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-01
  • 2018-11-30
相关资源
最近更新 更多